问题描述
我正在开发一个应用程序,我必须在该应用程序中比较数据库中的 2 个散列密码,在 PHP 中使用 $Password = password_hash($RawPassword,PASSWORD_BCRYPT);
生成一个密码
而发送到数据库以与 PHP 散列密码进行比较的另一个密码是在 Java 中使用 String hashedPassword = BCrypt.hashpw(password);
生成的
从 PHP 7.0 开始,salting 是自动生成的,我怎么知道 PHP 中应用了什么盐,以便我可以将它应用到我的 Java 代码中?或者有没有办法仍然指定不再出现在 PHP Hashing 文档中的盐?
解决方法
绝大多数 bcrypt impls 背后的标准想法是,数据库中的内容看起来像 $2y$10$AB
,其中 A 是 22 个字符,B 是 31 个字符,总共 60 个。A 是:{ {1}} 和 B 是:left(base64(salt + 00 + 00),22)
。 (left(base64(bcryptraw(salt + pass)),31)
指的是哈希算法/编辑:2y 和 2a 或多或少可以互换;大多数 bcrypt impls 将它们视为相同的,并且不太可能在乎哪一个。2y
指的是应用的 bcrypt 轮数。10 是常见的,通常是您想要的)。
哪里:
-
10
= 应用 base64 转换,使用 .和 / 作为第 63 个和第 64 个字符。 -
base64(X)
是串联的,即+
(一个 16 字节的字节数组)添加了 2 个零字节。 -
salt
表示:取前left(chars,size)
个字符并丢弃其余字符。 -
size
是以字节为单位的盐,salt
是密码,通过 UTF_8 转换为字节。 (如果不通过 UTF-8 进行转换,它通常是 $2a$,您应该升级,密码中包含非 ascii 字符的人在旧的 $2a$ 模式下会得到非常糟糕的哈希值!
这个字符串包含一切,bcrypt impl需要检查给定密码是否正确。因此,所有非白痴的 bcrypt 库实现只有两种方法,没有其他方法:
pass
编辑:注意:一个真正设计良好的加密库调用这些方法 // This is for when the user creates an account or edits their password.
// send the password to this method,then take the string it returns,// and store this in your database.
hash = crypto.hashPass(password);
// This is for when the user tries to log in. For 'hash',send the thing
// that the hashPass method made for you.
boolean passwordIsCorrect = crypto.checkPass(password,hash);
和 processNewPassword
以避免导致您问这个问题的那种混淆,但不幸的是,那里似乎没有人有足够的时间思考他们的名字所暗示的 5 秒钟。不幸的。安全很难。
如果你的 BCrypt API 不能像这样工作,摆脱它,并找到一个像这样工作的标准实现。
听起来您使用了错误的方法。要检查密码,请不要使用 hashPass。在你的实现中使用 checkPass 或任何用于 checkPass 的东西(它可能被称为 checkExistingPassword
或 checkPw
或 verifyPw
等。它需要 2 个字符串)。
因此,您永远不应该生成盐,也不应该从这样的字符串中提取盐。让 bcrypt 库来做。标准 bcrypt 库生成的那些“哈希”(validate
字符串)是可以互换的;你的 PHP 库可以生成 em,你的 java 库可以检查 em,反之亦然。
如果您必须提取盐(但不要):
- 在
$2y$
部分之后取这 22 个字符。 - 在此后附加“aa”。
- base64decode 结果。
- 这会让你得到 18 个字节。丢弃最后 2 个字节,其中包含垃圾。
- 剩下的 16 个字节是盐。
你绝对不应该写这个 - 你的 bcrypt 库会这样做。