BCrypt哈希字符串的样子如下:
$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
\__/\/ \____________________/\_____________________________/
| | Salt Hash
| Cost
Version
哪里
2a
: 算法标识符(BCrypt,UTF8编码的密码,以null结尾)
10
: 成本因子(210
= 1,024轮次)
Ro0CUfOqk6cXEKf3dyaM7O
: OpenBSD-Base64编码的盐(16字节 ⇒ 22个字符)
hSCvnwM9s4wIX9JeLapehKK5YdLxKcm
: OpenBSD-Base64编码的哈希值(24字节 ⇒ 31个字符)
Edit: i just noticed these words fit exactly. i had to share:
$2a$10$TwentytwocharactersaltThirtyonecharacterspasswordhash
$==$==$======================-------------------------------
BCrypt创建一个24字节的二进制哈希值,使用16字节的盐。你可以自由地存储二进制哈希值和盐,没有规定必须将其转换为字符串进行base-64编码。
但是BCrypt是由OpenBSD的开发人员创建的。OpenBSD已经为他们的密码文件定义了一种格式:
$[哈希算法标识符]$[算法特定数据]
这意味着"bcrypt规范"与OpenBSD密码文件格式密不可分。每当有人创建一个"bcrypt哈希"时,他们总是将其转换为以下ISO-8859-1格式的字符串:
$2a$[成本]$[Base64盐][Base64哈希]
几个重要点:
2a
是算法标识符
- 1:MD5
- 2:早期的bcrypt,对密码的编码方式存在混淆(已过时)
- 2a:当前的bcrypt,指定密码为UTF-8编码
Cost 是计算哈希时使用的成本因素。"当前"值为10,表示内部密钥设置经过1,024轮迭代
- 10:210 = 1,024次迭代
- 11:211 = 2,048次迭代
- 12:212 = 4,096次迭代
OpenBSD密码文件使用的base64算法与其他人使用的Base64编码不同;他们有自己的编码方式:
常规Base64字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
BSD Base64字母表:./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
因此,任何bcrypt的实现都不能使用任何内置或标准的base64库
有了这个知识,你现在可以验证一个密码
correctbatteryhorsestapler
与保存的哈希值进行比对:
$2a$12$mACnM5lzNigHMaf7O1py1O3vlf6.BA8k8x3IoJ.Tq3IB/2e7g61Km
BCrypt变种
关于bcrypt版本存在很多混淆。
$2$
BCrypt是由OpenBSD团队设计的。它被设计用于对密码进行哈希处理,以便存储在OpenBSD密码文件中。哈希处理后的密码会带有一个前缀来标识所使用的算法。BCrypt的前缀是$2$
。
这与其他算法的前缀形成了对比:
$1$
: MD5
$5$
: SHA-256
$6$
: SHA-512
$2a$
最初的BCrypt规范没有定义如何处理非ASCII字符,或者如何处理空终止符。规范进行了修订,明确指定在对字符串进行哈希处理时:
$2x$, $2y$ (2011年6月)
在
crypt_blowfish中发现了一个错误,这是BCrypt的PHP实现。它没有正确处理第8位设置的字符。
他们建议系统管理员更新其现有密码数据库,将
$2a$
替换为
$2x$
,以表示这些哈希值不好(需要使用旧的破损算法)。他们还提出了让crypt_blowfish发出由修复算法生成的哈希值
$2y$
的想法。除了OpenBSD的官方版本之外,没有其他人采用
2x
/
2y
的想法。
此版本标记仅适用于crypt_blowfish。
版本 $2x$ 和 $2y$ 并不比 $2a$ "更好" 或 "更强大"。它们是 BCrypt 的一个特定错误实现的遗留物。
$2b$ (2014年二月)
在 OpenBSD 的 BCrypt 实现中发现了一个错误。他们使用一种不支持字符串的语言来实现,所以他们通过长度前缀、字符指针和使用 []
对指针进行索引来伪装字符串操作。不幸的是,他们将字符串的长度存储在一个 unsigned char
中。如果密码超过255个字符,它会溢出并在255处截断。BCrypt 是为 OpenBSD 创建的。当他们的库中出现 bug 时,他们决定升级版本号。这意味着其他人如果想保持与"他们"规范的兼容性,也需要进行版本升级。
在2a、2x、2y和2b之间没有任何区别。如果你正确地编写了实现,它们都会输出相同的结果。
- 如果你从一开始就做对了(将字符串存储为utf8并散列空终止符),那么:2、2a、2x、2y和2b之间没有任何区别。如果你正确地编写了实现,它们都会输出相同的结果。
- 版本$2b$并不比$2a$“更好”或“更强”。它是BCrypt的一个特定有缺陷的实现的遗留物。但由于BCrypt属于OpenBSD,他们可以将版本标记更改为任何他们想要的。
- 版本$2x$和$2y$不比其他任何东西更好,甚至也不可取。它们是有缺陷实现的遗留物,应该被彻底遗忘。
唯一需要关心2x和2y的人是那些可能在2011年使用过crypt_blowfish的人。而唯一需要关心2b的人是那些可能在运行OpenBSD的人。
所有其他正确的实现都是相同且正确的。