正在生成V5 UUID.什么是名称和命名空间?

我已阅读_ABC_第0页,但我不明白namenamespace的作用。

对于版本3和版本5的UUID,额外的命令行 必须给出命名空间和名称的参数。命名空间为 字符串表示形式的UUID或 内部预定义的命名空间UUID的标识符(目前已知的有";ns:DNS";、";ns:URL'、",ns:oid'和",ns:x500")。这个 名称是任意长度的字符串。

命名空间:

命名空间要么是字符串表示形式的UUID,要么是

这是否意味着我需要将它(UUID V4)存储在与生成的UUID V5相关的某个位置?在这两种情况下,为什么不自动完成?

名称是任意长度的字符串。

name是一个完全随机的字符串。那么它的目的是什么呢?它可以从UUID V5解码吗?

130443 次浏览

名称和命名空间可用于创建(很可能)唯一UUID的层次结构。

粗略地说,类型3或类型5 UUID是通过将名称空间标识符与名称散列在一起而生成的。类型3 UUID使用MD5,类型5 UUID使用SHA1。只有128位可用,其中5位用于指定类型,因此所有的哈希位都不会进入UUID.(此外,MD5被认为是加密破解的,而SHA1已奄奄一息,因此不要使用它来验证需要“非常安全”的数据)。也就是说,它为您提供了一种创建可重复/可验证的“哈希”函数的方法,该函数将可能的分层名称映射到概率上唯一的128位值,可能类似于分层哈希或MAC.

假设您有一个(键,值)存储,但它只支持一个名称空间。您可以使用类型3或类型5 UUID生成大量不同的逻辑命名空间。首先,为每个名称空间创建一个根UUID.这可以是类型1(主机+时间戳)或类型4(随机)UUID,只要您将其存放在某个地方即可。或者,您可以为根创建随机UUID(或者使用null UUID:00000000-0000-0000-0000-000000000000作为根),然后使用“uuid -v5 $ROOTUUID $NAMESPACENAME ”为每个名称空间创建可复制的UUID.现在,您可以使用“uuid -v5 $NAMESPACEUUID $KEY ”为名称空间中的键创建唯一的UUID.这些UUID可以放在单个键值存储中,很有可能避免冲突。可以递归地重复该过程,使得如果例如与UUID键相关联的“值”依次表示某种逻辑“命名空间”,如桶、容器或目录,则可以依次使用其UUID来生成更分层的UUID.

生成的类型3或类型5 UUID保存名称空间ID和名称空间中的名称(键)的(部分)散列。它不保存名称空间UUID,就像消息MAC不保存它所编码的消息的内容一样。从UUID算法的角度来看,该名称是一个“任意”(八位字节)字符串。但是,它的含义取决于您的应用程序。它可以是逻辑目录中的文件名、对象存储中的对象ID等等。

虽然这适用于数量适中的名称空间和键,但如果您的目标是非常大量的键,而这些键很可能是唯一的,那么它最终会失去动力。生日问题(又名生日悖论)的维基百科条目包括一个表,该表给出了不同数量的键和表大小的至少一次冲突的概率。对于128位,以这种方式散列260亿个密钥具有p=10^-18的冲突概率(可忽略),但是26万亿个密钥将至少一个冲突的概率增加到p=10^-12(万亿分之一),并且散列26*10^15个密钥将至少一个冲突的概率增加到p=10^-6(百万分之一)。对编码UUID类型的5位进行调整,它将以更快的速度用完,因此一万亿个密钥发生一次冲突的几率大约为万亿分之一。

有关概率表,请参阅http://en.wikipedia.org/wiki/Birthday_problem#Probability_table

有关UUID编码的详细信息,请参阅http://www.ietf.org/rfc/rfc4122.txt

Type3和Type5 UUID只是一种将哈希填充到UUID中的技术:

  • 类型1:将MAC地址+日期时间填充为128位
  • 类型3:将MD5哈希填充为128位
  • 类型4:将随机数据填充为128位
  • 类型5:将SHA1的散列填充为128位
  • 类型6顺序UUID的非官方想法

编辑:非官方类型6现在有一个官方RFC

SHA1散列输出160位(20字节);哈希的结果被转换为UUID.

使用来自SHA1的20字节摘要:

SHA1 Digest:   74738ff5 5367 e958 1aee 98fffdcd1876 94028007
UUID (v5):     74738ff5-5367-5958-9aee-98fffdcd1876
⭡    ⬑first two bits set to 1 and 0, respectively
╰─low nibble is set to 5, to indicate type 5

我该做什么?

你可能想知道我应该做什么。基本上,您可以对以下内容的连接进行散列:

SHA1(命名空间UUID+任何字符串);

使用所谓的命名空间作为字符串的前缀,以防止名称冲突。

UUID RFC为您预定义了四个命名空间:

  • NameSpace_DNS:{6BA7B810-9DAD-11D1-80B4-00C04FD430C8}
  • NameSpace_URL:{6BA7B811-9DAD-11D1-80B4-00C04FD430C8}
  • NameSpace_OID:{6BA7B812-9DAD-11D1-80B4-00C04FD430C8}
  • NameSpace_X500:{6BA7B814-9DAD-11D1-80B4-00C04FD430C8}

所以,你可以拼凑起来:

StackOverflowDnsUUID = sha1(Namespace_DNS + "stackoverflow.com");
StackOverflowUrlUUID = sha1(Namespace_URL + "stackoverflow.com");

然后,RFC定义如何:

  • 从SHA1中获取160位
  • 并将其转换为128位UUID

基本要点是仅取前128位,在类型记录中填充5,然后将clock_seq_hi_and_reserved部分的前两位分别设置为1和0。

更多例子

现在,您已经有了一个生成所谓的名字的函数,您可以拥有该函数(在伪代码中):

UUID NameToUUID(UUID NamespaceUUID, String Name)
{
//Note: All code on stackoverflow is public domain - no attribution required.


Byte[] hash = sha1(NamespaceUUID.ToBytes() + Name.ToBytes());
Uuid result;


//Copy first 16-bytes of the hash into our Uuid result
Copy(hash, result, 16);


//set high-nibble to 5 to indicate type 5
result[6] &= 0x0F;
result[6] |= 0x50;


//set upper two bits to "10"
result[8] &= 0x3F;
result[8] |= 0x80;


return result;
}

(注意:系统的字节序可能会影响上述字节的索引)

现在,您可以拨打电话:

uuid = NameToUUID(Namespace_DNS, 'www.stackoverflow.com');
uuid = NameToUUID(Namespace_DNS, 'www.google.com');
uuid = NameToUUID(Namespace_URL, 'http://www.stackoverflow.com');
uuid = NameToUUID(Namespace_URL, 'http://www.google.com/search&q=rfc+4112');
uuid = NameToUUID(Namespace_URL, 'http://stackoverflow.com/questions/5515880/test-vectors-for-uuid-version-5-converting-hash-into-guid-algorithm');

现在回到你的问题。

对于版本3和版本5的UUID,必须给出额外的命令行参数命名空间和名称。命名空间是字符串表示形式中的UUID,或者是内部预定义的命名空间UUID的标识符(目前已知的是“ ns:DNS ”、“ ns:URL ”、“ ns:oid ”和“ ns:x500 ”)。名称是任意长度的字符串。

命名空间是您喜欢的任何UUID.它可以是预定义的,也可以是您自己创建的,例如1

UUID Namespace_RectalForeignExtractedObject = '8e884ace-bee4-11e4-8dfc-aa07a5b093db'

名称是任意长度的字符串。

名称就是您想要附加到名称空间的文本,然后进行哈希处理并填充到UUID中:

uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'screwdriver');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'toothbrush');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'broomstick');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'orange');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'axe handle');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'impulse body spray');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'iPod Touch');

名称只不过是在某个命名空间中唯一的标识符。问题是名称空间通常很小,并且一个名称中的名称经常与其他名称中的名称冲突。例如,我的汽车的牌照号码(名称)在我的州DMV的名称空间中是唯一的,但在世界上可能不是唯一的。其他州的DMV可能在自己的名称空间中使用了相同的名称。见鬼,其他人可能也有匹配的电话号码(姓名),因为那是另一个名称空间,等等。

可以将UUID视为位于单个名称空间中,该名称空间非常庞大,以至于它可以为每件事提供唯一的名称。这就是“通用”手段。但是如何将其他名称空间中的现有名称映射到UUID呢?

一个显而易见的解决方案是为每个项目生成一个UUID(v1或v4),以替换其不相交名称空间中的旧名称。缺点是它们要大得多,您必须将所有新名称传达给拥有数据集副本的每个人,更新所有API等。无论如何,您实际上无法完全摆脱旧名称,这意味着现在每个项目都有名称,那么您使事情变得更好还是更糟?

这就是V3/V5的用武之地。UUID,就像V4一样随机,但实际上是确定的;拥有名称空间的正确UUID的任何人都可以独立地为该名称空间中的任何给定名称生成相同的UUID.您根本不需要发布它们,甚至不需要预先生成它们,因为任何人都可以根据需要动态创建它们!

DNS名称和URL是非常常用的名称空间,因此为它们发布了标准的UUID.ASN.1 OID和X.500名称并不常见,但标准机构喜欢它们,因此他们也为它们发布了标准名称空间UUID.

对于所有其他名称空间,您必须生成自己的名称空间UUID(v1或v4),并将其传递给需要它的任何人。如果您有多个名称空间,那么必须为每个名称空间发布UUID显然是不理想的。

这就是层次结构的用武之地:创建一个“基”。UUID(无论什么类型),然后将其用作命名其他名称空间的名称空间。这样,您只需发布基本UUID(或使用一个明显的UUID),每个人都可以计算其余部分。

例如,我们想要为StackOverflow创建一些UUID.它在DNS命名空间中有一个明显的名称,因此其基础是显而易见的:

uuid ns_dns = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
uuid ns_base = uuidv5(ns_dns, 'stackoverflow.com');

StackOverflow本身为用户、问题、答案、评论等提供了单独的命名空间,但这些也是相当明显的:

uuid ns_user     =  uuidv5( ns_base, 'user'     );
uuid ns_question =  uuidv5( ns_base, 'question' );
uuid ns_answer   =  uuidv5( ns_base, 'answer'   );
uuid ns_comment  =  uuidv5( ns_base, 'comment'  );

这个特定的问题是#10867405,所以它的UUID应该是:

uuid here = uuidv5(ns_question, '10867405');

请注意,在此过程中有 random,因此任何遵循相同逻辑的人都将得到相同的答案,但UUID名称空间是如此庞大,以至于它永远不会与从任何其他名称空间/名称对生成的UUID发生冲突(实际上,给定122位加密哈希的安全性)。