22

在阅读了 Jeff Atwood 关于存储密码的帖子后,我偶然发现了 BCrypt.net,这让我想到了 Thomas Ptacek 建议使用 BCrypt来存储密码。这最终让我想到了 BCrypt 的这个 C# 实现

在上面最后一个链接的评论中,有人问“为什么 GenerateSalt(30) 永远需要,但 GenerateSalt(31) 似乎根本不需要时间?”

我运行了 BCrypt.HashPassword(password, BCrypt.GenerateSalt(31)) 并在 0 毫秒内得到了结果。

我已经运行 BCrypt.HashPassword("password", BCrypt.GenerateSalt(30)) 超过 5 分钟,但仍然没有结果。

我意识到多年来我们可能不需要随机生成的 30 个字符的盐来创建我们的密码哈希(或BCrypt 的不可逆加密)。编辑我应该稍微阅读一下代码,logRounds 与盐长度没有任何关系。谢谢阿罗诺特。

那么,为什么 GenerateSalt(31) 几乎会立即返回一个值(而它需要的时间大约是 GenerateSalt(30) 的两倍?

更新

这是修复:

private byte[] CryptRaw(byte[] password, byte[] salt, int logRounds) {
    // ... snip ...
    uint rounds = 1U << logRounds;
    // ... snip
}
4

2 回答 2

25

我怀疑这个错误在这里:

private byte[] CryptRaw(byte[] password, byte[] salt, int logRounds) {
    // ... snip ...
    int rounds = 1 << logRounds;
    // ... snip
}

当您为 指定 31 时logRounds,它会将其计算为 2^32,它不适合 anint并且会溢出,因此哈希实际上是在……呃,零次通过。作者应该uint改用。容易修复!


还想对此发表评论:

我意识到我们可能不需要随机生成的 30 个字符的盐来创建我们的密码哈希......

请注意,该logRounds参数不是指盐中的字符/字节数,它始终为 16。它指的是散列将用于计算的遍数的对数底;换句话说,这是一种根据摩尔定律使 bcrypt 规模化的方法,如果计算机能够以足够快的速度破解现有的哈希,则计算该函数的成本会增加几个数量级。

于 2010-02-08T15:11:55.437 回答
10

如果散列GenerateSalt(31)几乎立即返回,那是一个错误。您应该报告上游(我有,对于 jBCrypt)。:-)

默认情况下,对数轮数为 10。这意味着(如果我没记错的话)使用 1024 轮。每次增加对数轮数时,轮数都会加倍。

在 30 个对数轮中,您正在执行 1073741824 轮。这理所当然地需要很长时间。在 31 轮日志中,应该完成 2147483648 轮,但我怀疑您使用的特定实现会溢出。:-(

于 2010-02-08T15:02:35.410 回答