有人可以解释BCrypt如何验证散列吗?

27 c# bcrypt bcrypt.net

我正在使用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);
Run Code Online (Sandbox Code Playgroud)

如果没有在任何地方保存盐,BCrypt如何用哈希验证密码.我唯一的想法是,它以某种方式在哈希的末尾附加盐.

这是正确的假设吗?

Ian*_*oyd 89

BCrypt哈希字符串如下所示:

$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
$==$==$======================-------------------------------
Run Code Online (Sandbox Code Playgroud)

哪里

  • 2a:算法标识符(BCrypt,UTF8编码密码,空终止)
  • 10:成本因素(2 10= 1,024轮)
  • Ro0CUfOqk6cXEKf3dyaM7O:OpenBSD-Base64编码盐(22个字符,16个字节)
  • hSCvnwM9s4wIX9JeLapehKK5YdLxKcm:OpenBSD-Base64编码哈希(31个字符,24个字节)

编辑:我刚刚注意到这些单词完全符合.我不得不分享:

$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------
Run Code Online (Sandbox Code Playgroud)

BCrypt 确实创建一个24字节的二进制散列,使用16个字节的盐.您可以随意存储二进制哈希和盐; 什么都没说,你必须将base-64编码成一个字符串.

BCrypt是由正在研究OpenBSD的人创建的.OpenBSD已经为其密码文件定义了一种格式:

$ [HashAlgorithmIdentifier]$[AlgorithmSpecificData]

这意味着"bcrypt规范"不可避免地与OpenBSD密码文件格式相关联.每当有人创建"bcrypt哈希"时,他们总是将其转换为格式的ISO-8859-1字符串:

$ 2a$ [Cost]$[Base64Salt][Base64Hash]

一些要点:

  • 2a 是算法标识符
    • 1:MD5
    • 2:早期的bcrypt,对编码密码所在的混淆(过时)
    • 2a:当前的bcrypt,它将密码指定为UTF-8编码
  • 成本是计算哈希时使用的成本因素."当前"值为10,表示内部密钥设置经过1,024轮
    • 10:2 10 = 1,024次迭代
    • 11:2 11 = 2,048次迭代
    • 12:2 12 = 4,096次迭代
  • OpenBSD密码文件使用的base64算法与其他人使用的Base64编码不同; 他们有自己的:

    Regular Base64 Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
        BSD Base64 Alphabet: ./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
    
    Run Code Online (Sandbox Code Playgroud)

    因此,bcrypt的任何实现都不能使用任何内置或标准的base64库


有了这些知识,您现在可以correctbatteryhorsestapler根据保存的哈希验证密码:

$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
Run Code Online (Sandbox Code Playgroud)

  • 骗子:TwentyTwoCharacter与ThirtyOneCharacter**s**(复数).幸运的是,Salt以**s**开头,所以你可以争辩说这两个词是分开的.它仍然很棒...... (2认同)

Eri*_*ert 24

如果没有将盐保存在任何地方,BCrypt如何用哈希验证密码?

显然它没有做任何这样的事情.盐必须在某处保存.

让我们在维基百科上查找密码加密方案.来自http://en.wikipedia.org/wiki/Crypt_(Unix):

函数的输出不仅仅是散列:它是一个文本字符串,它也对salt进行编码并标识所使用的散列算法.

或者,您对此主题的上一个问题的答案包括源代码的链接.您可以随时选择自己阅读,而不是要求互联网为您阅读源代码.这可能会让你的答案更快.源代码的相关部分是:

    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();
Run Code Online (Sandbox Code Playgroud)

显然返回的字符串是版本信息,然后是使用的轮数,接着是编码为base64的salt,接着是编码为base64的哈希.