【问题标题】:Hash Function to Generate 16 Alphanumerical Characters from Input String in C#在 C# 中从输入字符串生成 16 个字母数字字符的哈希函数
【发布时间】:2019-10-17 09:25:19
【问题描述】:

我需要一个函数来接收输入字符串,无论其长度如何,并输出 0-9A-Z 的 16 个字符的固定长度。如果输入相同的字符串,该函数应该具有相同的输出。

有什么建议吗?谢谢

【问题讨论】:

  • 请注意,如此短的散列不能防止故意的冲突。成本应该在 2^42 次散列调用左右,这是非常可行的。只要您的哈希值少于十亿左右,意外冲突应该很少见。

标签: c# hash cryptography


【解决方案1】:

你可以使用类似的东西:

public static string HashString(string text)
{
    const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    byte[] bytes = Encoding.UTF8.GetBytes(text);

    SHA256Managed hashstring = new SHA256Managed();
    byte[] hash = hashstring.ComputeHash(bytes);

    char[] hash2 = new char[16];

    // Note that here we are wasting bits of hash! 
    // But it isn't really important, because hash.Length == 32
    for (int i = 0; i < hash2.Length; i++)
    {
        hash2[i] = chars[hash[i] % chars.Length];
    }

    return new string(hash2);
}

SHA256Managed 将生成一个 32 字节的散列。然后使用%(模数)运算符,我们为每个字节选择一个字符。请注意,我们以这种方式浪费了很多位,但这并不重要,因为我们有比我们需要的更多的位(我们需要log2(36) * 16 == 82.7,我们有 256 位哈希)

【讨论】:

  • 优秀..!它很好用。我之前也认为使用模数可以帮助填充字符串以仅接受所需的字符。谢谢@xanatos
  • 请注意,这种方法会在输出中引入偏差。前几个数字将比其他字符更常见。幸运的是,这不会导致大多数用例的实际弱点。
  • @CodesInChaos 我在问为什么...但后来我意识到... 256 / 36 = 7 余数为 4...那些 4 :-)
【解决方案2】:

如果你想创建一个散列,你应该研究散列算法。最广为人知的是 MD5,但它是一种 128 位 散列算法。这意味着如果您将原始字节转换为十六进制字符串,它将是 32 个字符长(正如大多数人所知),这意味着您需要一个 64 位 的散列函数。我快速搜索了一下,发现了 SipHash (http://en.wikipedia.org/wiki/SipHash),然后我找到了一个 C# 实现 (https://github.com/BrandonHaynes/siphash-csharp)

如果你使用 SipHash 算法,你应该得到一个 16 字符长度的字符串。

【讨论】:

  • 1) 十六进制的问题在于它没有使用 OP 允许的全部 36 个字符。这将散列减少到 64 位,而不是其他可能的 82 位。 2) SipHash 在没有密钥的情况下甚至不会尝试抗碰撞。
【解决方案3】:

试试这个,它使用 MD5Hash 算法。

public string GenerateHash(string str)
{
    using (var md5Hasher = MD5.Create())
    {
        var data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(str));
        return BitConverter.ToString(data).Replace("-", "").Substring(0, 16);
    }
}

【讨论】:

    【解决方案4】:

    我真的很喜欢@Xanatos 的答案,并决定实施它。 那时我意识到我正在创建一个我看到的常见问题的实例。

    当我输入安装 Windows 的密钥时,我有时会弄错数字。尤其是当我正在阅读的条形码被划伤或褪色时。简直就是条码上相似的字符、视力不好和磨损的组合。

    以下内容抄袭自@Xanatos 的答案作为扩展方法,但删除了相似字符(例如“1”、“I”、“O”、“0”等)。

    public static string ConstantLengthHash(this string Input)
    {
        const string chars = "234679ACDEFGHJKLMNPQRTUVWXYZ";
        byte[] bytes = Encoding.UTF8.GetBytes(Input);
    
        SHA256Managed hashstring = new SHA256Managed();
        byte[] hash = hashstring.ComputeHash(bytes);
    
        char[] hash2 = new char[16];
    
        // Note that here we are wasting bits of hash! 
        // But it isn't really important, because hash.Length == 32
        for (int i = 0; i < hash2.Length; i++)
        {
            hash2[i] = chars[hash[i] % chars.Length];
        }
    
        return new string(hash2);
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用 LINQ:

      var c = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      var rn = new Random();
      var res = new string(Enumerable.Repeat(c, 16)
                    .Select(x => x[rn.Next(x.Length)])
                    .ToArray());
      

      另请参阅:RNGCryptoServiceProvider Class

      使用加密随机数生成器 (RNG) 由加密服务提供商 (CSP) 提供的实现。 这个类不能被继承。

      或者你可以试试这个:

      Guid g = Guid.NewGuid();
      MD5 md5 = MD5.Create();
      Guid hashed = new Guid(md5.ComputeHash(g.ToByteArray()));
      

      【讨论】:

      • OP 想要哈希而不是随机字符串。
      • @RahulTripathi 它似乎创建了一个随机哈希字符串。我需要输出是一个固定长度的字符串,基于非固定长度的输入字符串创建。所以输出字符串不应该只是随机的。如果输入相同,它应该始终具有相同的输出。这就像对密码进行哈希处理,但输出应限制为 16 个字符,并且只接受 0-9 和 A-Z。
      • @BennyChen:- 你可以像这样固定长度:Guid.NewGuid().ToString().Substring(0, 16);
      猜你喜欢
      • 2021-11-22
      • 2017-05-31
      • 2017-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-13
      • 2015-03-09
      相关资源
      最近更新 更多