生成v5 UUID。什么是名称和名称空间?

时间:2012-06-03 02:13:42

标签: uuid

我已阅读man页面,但我并未指出namenamespace的用途。

  

对于版本3和版本5,UUID是附加命令行   必须给出参数名称空间和名称。命名空间也是   字符串表示中的UUID或             内部预定义命名空间UUID的标识符(当前已知为“ns:DNS”,“ns:URL”,“ns:OID”和“ns:X500”)。该   name是一个任意长度的字符串。

命名空间:

  

命名空间是字符串表示形式的UUID或

这是否意味着我需要将它(UUID v4)存储在与生成的UUID v5相关的某个地方?在任何一种情况下,为什么不自动完成?

  

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

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

3 个答案:

答案 0 :(得分:168)

类型3和类型5 UUID只是将哈希填充到UUID中的一种技术。

  • 类型1:将MAC地址,日期时间填充为128位
  • 类型3 :将MD5哈希填充为128位
  • 类型4:将随机数据填充为128位
  • 类型5 :将SHA1哈希填充为128位

SHA1哈希输出160位(20字节)。哈希的结果被转换为UUID。来自SHA1的20字节:

SHA1 Digest:   74738ff5 5367 e958 9aee 98fffdcd1876 94028007
UUID (v5):     74738ff5-5367-5958-9aee-98fffdcd1876
                             ^_low nibble is set to 5 to indicate type 5
                                  ^_first two bits set to 1 and 0, respectively

(请注意,' 9'的前两位分别为1和0,所以这没有效果。)

我要哈希什么?

你可能想知道我应该散列什么。基本上你哈希连接:

sha1([NamespaceUUID]+[AnyString]);

您在字符串前加上一个所谓的命名空间,以防止名称冲突。

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)
{
    byte[] hash = sha1(NamespaceUUID.ToBytes() + Name.ToBytes());
    UUID result;
    Copy(hash, result, 16);
    result[6] &= 0x0F; 
    result[6] |= 0x50;
    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。它可以是预定义的一个,也可以自己构成,例如:

UUID Namespace_RectalForeignExtractedObject = '4d79546f-6e67-7565-496e-486572417373'
  

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

名称只是您要附加到命名空间的文本,然后进行散列,并填充到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');
  

注意:任何已发布到公共领域的代码。无需归属。

答案 1 :(得分:77)

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

粗略地说,通过将命名空间标识符与名称一起散列来生成类型3或类型5 UUID。类型3 UUID使用MD5,类型5 UUID使用SHA1。只有128位可用,5位用于指定类型,因此所有散列位都不会进入UUID。 (此外,MD5被认为是加密断开的,并且SHA1处于最后阶段,所以不要使用它来验证需要非常安全的数据")。也就是说,它为您提供了一种创建可重复/可验证的#hash;#34;函数将可能的分层名称映射到概率上唯一的128位值,可能像分层哈希或MAC一样。

假设您有一个(key,value)存储,但它只支持一个命名空间。您可以使用类型3或类型5 UUID生成大量不同的逻辑名称空间。首先,为每个命名空间创建一个根UUID。这可以是类型1(主机+时间戳)或类型4(随机)UUID,只要您将其存放在某处。或者,您可以为根创建一个随机UUID(或以root身份使用null UUID:00000000-0000-0000-0000-000000000000,然后使用"为每个命名空间创建可重现的UUID。 uuid -v5 $ROOTUUID $NAMESPACENAME&#34 ;.现在,您可以使用" uuid -v5 $NAMESPACEUUID $KEY"为命名空间内的键创建唯一的UUID。这些UUID可以被抛入单个键值存储中,很可能避免冲突。这个过程可以递归地重复,以便如果例如"值"与UUID密钥相关联又代表某种逻辑"命名空间"像桶,容器或目录一样,它的UUID可以依次用于生成更多的分层UUID。

生成的类型3或类型5 UUID包含名称空间标识和名称空间名称(密钥)的(部分)哈希。它不再保留命名空间UUID,而不是消息MAC保存它所编码的消息的内容。这个名字是"任意的" (octet)字符串从uuid算法的角度来看。但其含义取决于您的应用。它可以是逻辑目录中的文件名,对象存储中的object-id等等。

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

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

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

答案 2 :(得分:9)

名称不过是在某些名称空间中唯一的标识符。问题在于名称空间通常很小,一个名称空间经常与其他名称空间冲突。例如,在我的州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),每个人都可以计算出其余的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');

请注意,此过程中没有 随机操作,因此,遵循相同逻辑的任何人都将得到相同的答案,但是UUID名称空间是如此之大,以至于(有效地,由于122位加密哈希)永远不会与从任何其他名称空间/名称对生成的UUID冲突。