Can someone explain how BCrypt verifies a hash?
我正在使用C#和BCrypt.Net哈希密码。
例如:
1 2 3 4 5 6 7 | string salt=BCrypt.Net.BCrypt.GenerateSalt(6); var hashedPassword = BCrypt.Net.BCrypt.HashPassword("password", salt); //This evaluates to True. How? I'm not telling it the salt anywhere, nor //is it a member of a BCrypt instance because there IS NO BCRYPT INSTANCE. Console.WriteLine(BCrypt.Net.BCrypt.Verify("password", hashedPassword)); Console.WriteLine(hashedPassword); |
如果BCrypt没有将盐保存在任何地方,那么BCrypt如何使用哈希验证密码。 我唯一的想法是,它以某种方式在哈希的末尾添加了盐。
这是正确的假设吗?
BCrypt哈希字符串如下所示:
好。
1 2 | $2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm $==$==$======================------------------------------- |
哪里
好。
好。
Edit: i just noticed these words fit exactly. i had to share:
Ok.
1
2 $2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------
BCrypt确实使用16字节的salt创建了24字节的二进制哈希。您可以随意存储二进制哈希和盐。没什么说您必须将它以base-64编码为字符串。
好。
但是BCrypt是由从事OpenBSD的人创建的。 OpenBSD已经为其密码文件定义了一种格式:
好。
$
好。
这意味着" bcrypt规范"与OpenBSD密码文件格式紧密相关。每当有人创建" bcrypt哈希"时,他们总是将其转换为以下格式的ISO-8859-1字符串:
好。
$
好。
一些要点:
好。
好。
好。
OpenBSD密码文件使用的base64算法与其他所有人使用的Base64编码不同;他们有自己的:
好。
1 2 | Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 |
因此,bcrypt的任何实现都不能使用任何内置或标准的base64库
好。
好。
有了这些知识,您现在可以针对保存的哈希值验证密码
好。
1 | $2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km |
BCrypt变体
bcrypt版本之间存在很多混淆。
好。
2美元
好。
BCrypt是由OpenBSD人士设计的。它被设计为哈希密码以存储在OpenBSD密码文件中。哈希密码带有前缀以标识所使用的算法。 BCrypt获得了前缀
好。
这与其他算法前缀相反:
好。
好。
$ 2a $
好。
原始的BCrypt规范未定义如何处理非ASCII字符或如何处理空终止符。规范进行了修改,以指定在对字符串进行哈希处理时:
好。
好。
$ 2x $,$ 2y $(2011年6月)
好。
在BCrypt的PHP实现crypt_blowfish ??中发现了一个错误。设置了第8位的字符处理不当。
好。
他们建议系统管理员更新其现有的密码数据库,用
好。
版本$ 2x $和$ 2y $不比$ 2a $"更好"或"更强"。它们是BCrypt的一种特殊的错误实现的残余。
好。
$ 2b $(2014年2月)
好。
在BCrypt的OpenBSD实现中发现了一个错误。他们将字符串的长度存储在
好。
好。
2a,2x,2y和2b之间没有区别。如果正确编写了实现,则它们都将输出相同的结果。
好。
好。
唯一需要关心2x和2y的人就是您在2011年就一直在使用crypt_blowfish的人。并且唯一需要关心2b的人是那些可能在运行OpenBSD的人。
好。
所有其他正确的实现都是相同且正确的。
好。
好。
How is BCrypt verifying the password with the hash if it's not saving the salt anywhere?
显然,它没有做任何这样的事情。 盐必须保存在某个地方。
让我们在Wikipedia上查找密码加密方案。 从http://en.wikipedia.org/wiki/Crypt_(Unix):
The output of the function is not merely the hash: it is a text string which also encodes the salt and identifies the hash algorithm used.
或者,对您先前关于此主题的问题的答案包括指向源代码的链接。 源代码的相关部分是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | StringBuilder rs = new StringBuilder(); rs.Append("$2"); if (minor >= 'a') { rs.Append(minor); } rs.Append('$'); if (rounds < 10) { rs.Append('0'); } rs.Append(rounds); rs.Append('$'); rs.Append(EncodeBase64(saltBytes, saltBytes.Length)); rs.Append(EncodeBase64(hashed,(bf_crypt_ciphertext.Length * 4) - 1)); return rs.ToString(); |
显然,返回的字符串是版本信息,其次是使用的轮数,其次是编码为base64的salt,然后是编码为base64的哈希。