如何从SHA-1字节数组生成GUID?

6

我有以下代码来生成SHA-1哈希值:

SHA1 sha1 = SHA1CryptoServiceProvider.Create();
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString);
Byte[] hash = sha1.ComputeHash(myStringBytes);

有没有一种方法可以将hash转换成Guid(我猜是类型5,为了与SHA-1保持一致)?


这有点令人困惑,因为哈希和 GUID 是不同的东西。GUID 应该在每次请求新的 GUID 时给你一个唯一/不同的字符串。哈希根据相同的输入提供一致的结果。 - Justin
这将是一个确定性且可重复的 GUID,基于哈希,旨在根据输入生成唯一值。更多信息:http://waterjuice.org/2013/06/type-3-and-5-guids/ - Guillermo Gutiérrez
4
@Justin:您误解了OP所要求的内容。GUID只是全局唯一标识符的缩写,这就是GUID的含义。有很多方法可以生成GUID。例如,您可以基于当前时间和位置生成GUID;那是全球唯一的。(大多数情况下)。这是一种类型一的GUID。您也可以使用随机数生成器生成统计上唯一的GUID;那是一种类型四的GUID。一个独特字符串的加密强哈希也是一种独特性的来源,并且可以用来构造GUID。 - Eric Lippert
@EricLippert 但是使用哈希值和真正的 GUID,你不觉得存在冲突的可能性吗? - Justin
1
@Justin:首先,类型5 GUID是“正确”的GUID。如果你的意思是问“类型4随机GUID和类型5 SHA哈希GUID之间是否存在碰撞的可能性?”,那么答案显然是“不可能”,因为它们必须在版本号上有所不同! - Eric Lippert
2个回答

7
你可以使用基于rfc4122的C#代码,这是C# code
为了防止链接失效,这里提供一些代码:link rot
public static Guid Create(Guid namespaceId, string name)
{
    if (name == null)
        throw new ArgumentNullException("name");

    // convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3)
    // ASSUME: UTF-8 encoding is always appropriate
    byte[] nameBytes = Encoding.UTF8.GetBytes(name);

    // convert the namespace UUID to network order (step 3)
    byte[] namespaceBytes = namespaceId.ToByteArray();
    SwapByteOrder(namespaceBytes);

    // comput the hash of the name space ID concatenated with the name (step 4)
    byte[] hash;
    using (HashAlgorithm algorithm =  SHA1.Create())
    {
        algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0);
        algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length);
        hash = algorithm.Hash;
    }

    // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12)
    byte[] newGuid = new byte[16];
    Array.Copy(hash, 0, newGuid, 0, 16);

    // set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8)
    newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4));

    // set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10)
    newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80);

    // convert the resulting UUID to local byte order (step 13)
    SwapByteOrder(newGuid);
    return new Guid(newGuid);
}

/// <summary>
/// The namespace for fully-qualified domain names (from RFC 4122, Appendix C).
/// </summary>
public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8");

/// <summary>
/// The namespace for URLs (from RFC 4122, Appendix C).
/// </summary>
public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8");

/// <summary>
/// The namespace for ISO OIDs (from RFC 4122, Appendix C).
/// </summary>
public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8");

// Converts a GUID (expressed as a byte array) to/from network order (MSB-first).
internal static void SwapByteOrder(byte[] guid)
{
    SwapBytes(guid, 0, 3);
    SwapBytes(guid, 1, 2);
    SwapBytes(guid, 4, 5);
    SwapBytes(guid, 6, 7);
}

private static void SwapBytes(byte[] guid, int left, int right)
{
    byte temp = guid[left];
    guid[left] = guid[right];
    guid[right] = temp;
}

3
正如Justin指出的,Guid每次都应该是唯一的,而哈希将为相同的值每次给出一致的结果。现在我想补充一点,Guid和哈希(如果不是所有算法)都会发生碰撞,尽管我的直觉是哈希比Guid更容易发生碰撞......尽管这可能取决于哈希的大小(即128位,256位,512位等)。你会遇到另一个问题,那就是SHA1哈希的byte[]长度为20字节,而Guid的长度为16字节,因此,从SHA1哈希创建Guid将不准确。例如:
string myString = "Hello World";
SHA1 sha1 = SHA1CryptoServiceProvider.Create();
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString);
Byte[] hash = sha1.ComputeHash(myStringBytes);
Console.WriteLine(new Guid(hash.Take(16).ToArray()));

上面的示例将从您的哈希创建一个Guid,尽管它使用LINQ从哈希数组中获取16个字节(因此不准确...最后4个字节被简单地省略)

MD5是一个16字节的哈希,因此似乎比SHA1更适合转换为Guid。

例子:

string myString = "Hello World";
MD5 md5 = MD5.Create();
Byte[] myStringBytes = ASCIIEncoding.Default.GetBytes(myString);
Byte[] hash = md5.ComputeHash(myStringBytes);
Console.WriteLine(new Guid(hash));

这将从MD5哈希中产生一个准确的Guid,尽管我要说明的是,这只是MD5哈希的Guid表示形式...byte[]数据不应该有任何实际变化。

1
首先,你误解了为什么使用加密强度哈希函数生成唯一字符串的哈希值来生成GUID是合理的。请参见我上面的评论。其次,此代码是错误的;它没有设置GUID的版本号,这是GUID格式的文件化要求。 - Eric Lippert
1
@EricLippert,请考虑这里的文本:http://en.wikipedia.org/wiki/Universally_unique_identifier#Version_5_.28SHA-1_hash.29 - 鉴于这里指出GUIDv5是从160位截断为128位的SHA1,我猜我的第一个代码示例比第二个示例更接近OP的要求? - 这里还需要做一些额外的工作来设置版本号,对吧? - Matthew Layton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接