Bcrypt 不是密钥派生算法;它是一种密码哈希算法。
- PBKDF2 可以接受密码并输出
n
所需的位
- scrypt 可以输入密码并输出
n
所需的位
这些是密钥派生函数。他们获取密码并生成n
位,然后您可以将其用作加密密钥。
BCrypt 无法做到这一点。BCrypt 不是密钥派生函数。它是一个密码哈希函数。它总是输出相同数量的位。
奖励: bcrypt 总是准确地输出 24 字节(192 位),因为 bcrypt 的输出是加密的结果OrpheanBeholderScryDoubt
。
注意:这不是散列 OrpheanBeholderScryDoubt
的结果- bcrypt 算法实际上是使用河豚密码进行加密 OrpheanBeholderScryDoubt
(并重复加密 64 次)。
bcrypt 的优势来自“昂贵的密钥设置”。
奖励:bcrypt 的优势在于它价格昂贵。而“贵”就是记忆。算法需要的内存越多,它对抗暴力攻击的能力就越强。
- SHA-2:可以在 128 字节的 RAM 中运行
- bcrypt:不断接触 4 KB 的 RAM
- scrypt:不断触及 16 MB 的 RAM(在 Android 和 LiteCoin 的默认配置中)
- Argon2:通常建议您将其配置为触摸 1 GB 的 RAM
防御暴力攻击意味着防御并行化。需要 128 字节的算法可以在 1 GB 视频卡上进行 700 万次并行操作。
Scrypt 需要 16 MB 的 RAM,只能并行运行 62 个。
Argon2 使用 1 GB 的 RAM,只能在显卡上运行 1 个。无论如何,它在 CPU 上运行得更快。
将 bcrypt 组合成密钥派生函数 (KDF)
您可以将 bcrypt 组合成一个密钥派生函数。您可以使用标准功能PBKDF2
为您完成。
通常 PBKDF2 被称为:
String password = "hunter2";
String salt = "sea salt 69 nice";
Byte[] key = PBKDF2(password, salt, 32, 10000); //32-bytes is 256 bits
但是,您可以使用 bcrypt 字符串结果作为盐:
String password = "hunter2";
String salt = bcrypt.HashPassword(password, 12);
Byte[] key = PBKDF2(password, salt, 32, 1); //32-bytes is 256-bits
现在您已经“使用 bcrypt”生成了一个 256 位密钥。这是一个巧妙的技巧。
事实上,这个 hack 非常简洁,它实际上就是scrypt所做的:
String password = "hunter2";
String salt = ScryptExpensiveKeyHash(password, userSalt, ...);
Byte[] key = PBKDF2(password, salt, 32, 1); //32-bytes is 256-bits
结论
Bcrypt 不是密钥派生函数。这是 PBKDF2、scrypt(使用 PBKDF2)和 argon2 等函数的目标。
仅允许 NIST 批准的算法时使用 bcrytp
将这个 pbkdf2 构造与 bcrypt 一起使用还有另一个很好的理由。
有时,不知道他们在说什么的“安全专家”会坚持要求您使用 PBDKF2 进行密钥派生。(是的,它确实发生了)。你会一遍又一遍地告诉他们 PBDKF2 是非常弱的密钥派生系统(它基于 SHA2 的运行速度太快了,10,000 或 100,000 次迭代远远不足以保护你免受暴力攻击 -这就是 bcrypt、scrypt 和 argon2 的发明目的)。
但是这个人不会放过它,会要求使用PBKDF2。有了这种结构,您仍然可以使用 bcrypt 来保证安全性,而 PBKDF2 则可以用于需要它的无知者。
您只是碰巧使用了强“盐”。