用C#编写哈希函数从输入字符串生成16个字母数字字符

3
我需要一个函数,无论输入字符串的长度如何,都可以输出16个字符的0-9A-Z。如果输入相同的字符串,该函数应具有相同的输出结果。
有什么建议吗?谢谢。

请注意,这样一个短哈希无法防止故意碰撞。成本应该在2^42个哈希调用左右,这是相当可行的。只要您拥有少于十亿个哈希值,意外碰撞应该很少见。 - CodesInChaos
5个回答

4
您可以使用以下内容:

您可以使用类似于以下的内容:

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 - Buzz
请注意,这种方法会在输出中引入偏差。前几个数字将比其他字符更常见。幸运的是,对于大多数用例,这不应导致实际上的弱点。 - CodesInChaos
@CodesInChaos 我之前询问为什么... 然后我意识到... 256 / 36 = 7 余数为4...那就是那4个 :-) - xanatos

0

试试这个,它使用MD5哈希算法。

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);
    }
}

0

如果你想创建一个哈希值,你应该研究一下哈希算法。其中一个最为人所知的是MD5,但那是一个128位的哈希算法。这意味着如果你将原始字节转换成十六进制字符串,它将会有32个字符(大多数人都知道),因此你需要一个64位的哈希函数。我进行了快速搜索,找到了SipHashhttp://en.wikipedia.org/wiki/SipHash),然后我找到了一个C#实现(https://github.com/BrandonHaynes/siphash-csharp)。

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


  1. 十六进制的问题在于它没有使用 OP 允许的全部 36 个字符。这将哈希减少到 64 位,而不是本来可能的 82 位。
  2. 当没有密钥时,SipHash 甚至不尝试防止碰撞。
- CodesInChaos

0

我非常喜欢@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);
}

-3

您可以使用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

使用加密服务提供程序(CSP)提供的实现,实现了一个加密随机数生成器(RNG)。此类不能被继承。

或者您可以尝试这个:

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

2
OP想要哈希而不是随机字符串。 - CodesInChaos
@RahulTripathi 看起来它创建了一个随机哈希字符串。我需要的输出是一个固定长度的字符串,基于一个非固定长度的输入字符串创建。因此,输出字符串不应该只是随机的。如果输入相同,它应该始终具有相同的输出。就像对密码进行哈希一样,但输出应该限制为16个字符,并且仅接受0-9和A-Z。 - Buzz
@BennyChen:你可以这样固定长度:Guid.NewGuid().ToString().Substring(0, 16); - Rahul Tripathi

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