我正在使用C#和BCrypt.Net来哈希我的密码。
例如:
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如何使用哈希验证密码。我唯一的想法是,它以某种方式在哈希的末尾附加盐。
这是正确的假设吗?
答案 0 :(得分:84)
BCrypt哈希 字符串 如下所示:
$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
$==$==$======================-------------------------------
其中
2a
:算法标识符(BCrypt,UTF8编码密码,空终止)10
:成本因素(2 10
= 1,024轮)Ro0CUfOqk6cXEKf3dyaM7O
:OpenBSD-Base64编码的盐(22个字符,16个字节)hSCvnwM9s4wIX9JeLapehKK5YdLxKcm
:OpenBSD-Base64编码哈希(31个字符,24个字节)编辑:我刚刚注意到这些词语完全符合。我不得不分享:
$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash $==$==$======================-------------------------------
BCrypt 使用16字节盐创建一个24字节的二进制哈希。您可以随意存储二进制哈希和盐;什么都没有说你有到base-64将它编码成一个字符串。
但 BCrypt 是由正在使用OpenBSD的人创建的。 OpenBSD 已经为其密码文件定义了一种格式:
<强> $ 强> [HashAlgorithmIdentifier]
的 $ 强> [AlgorithmSpecificData]
这意味着“bcrypt规范”与OpenBSD密码文件格式密不可分。每当有人创建“bcrypt hash”时,他们总是将其转换为ISO-8859-1格式的字符串:
<强> $ 强> 2a
的 $ 强> [Cost]
的 $ 强> [Base64Salt][Base64Hash]
一些要点:
2a
是算法标识符
OpenBSD密码文件使用的base64算法与其他人使用的Base64编码不同;他们有自己的:
Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
所以bcrypt的任何实现都不能使用任何内置或标准的base64库
有了这些知识,您现在可以针对保存的哈希验证密码correctbatteryhorsestapler
:
$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
答案 1 :(得分:26)
显然它没有做任何这样的事情。盐必须保存在某个地方。如果没有将盐保存在任何地方,BCrypt如何用哈希验证密码?
让我们在维基百科上查找密码加密方案。来自http://en.wikipedia.org/wiki/Crypt_(Unix):
函数的输出不仅仅是散列:它是一个文本字符串,它也对salt进行编码并标识所使用的散列算法。
或者,关于此主题的previous question答案包含指向source code的链接。 不是要求互联网为您阅读源代码,您可以随时选择自己阅读。这可能会让您的答案更快。源代码的相关部分是:
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的哈希值。