密码生成单向不可逆-bcrypt算法

BCrypt 是一种基于 Blowfish 加密算法的密码哈希函数,由 Niels Provos 和 David Mazières在 1999 年提出,设计目标是安全地存储密码

核心特点

  1. 自适应成本因子(Cost Factor / Work Factor)

    • 可以调整计算难度,随着硬件性能提升,逐步增加迭代次数
    • 默认成本因子通常为10(5+),每增加 1,迭代次数翻倍(2¹⁰ → 2¹¹ → 2¹² …)
  2. 自动加盐(Salt)

    • 每次哈希都自动生成一个随机的 16 字节 salt
    • 防止彩虹表攻击(rainbow table attack)
  3. 单向不可逆

    • 只能验证,不能还原原始密码

算法流程

密码 + 盐 → Blowfish 密钥调度 → 密钥扩展(反复加密空字符串) → 哈希输出

具体步骤:

  1. 生成 Salt:随机生成 16 字节 salt,与 cost factor 一起编码
  2. 初始化 Blowfish 状态:使用 Blowfish 的密钥调度算法(Key Schedule),将密码和 salt 混合到 P-array 和 S-box 中
  3. 密钥扩展(Key Expansion):反复用 Blowfish 加密空字符串,不断重新初始化状态。迭代次数为 2^cost
  4. 最终编码:将最终的 P-array 和 S-box 状态输出,与 salt 一起用 Modified Oracle’s Base64 编码

输出格式

$2b$12$<22字符salt><31字符hash>

例如:

$2b$12$WApznUPhDubN0oe1fqfQ8OZiQREdiCRt9gOqwllZ1XPJiGNLHjOmq
部分 说明
$2b$ 算法版本号(2a/2b/2y 有细微差异)
12 cost factor(成本因子)
前22字符 Base64 编码的 salt(16 字节)
后31字符 Base64 编码的哈希值(31 字节,来自 72 字节输出截断)

验证密码

验证时提取盐值,用相同成本因子重新计算哈希,然后与存储的哈希值进行恒定时间比较(timing-safe comparison):

1
2
3
4
5
6
7
8
9
10
11
12
import bcrypt

# 生成哈希
password = b"my_password"
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)

# 验证密码
if bcrypt.checkpw(b"my_password", hashed):
print("密码正确")
else:
print("密码错误")

安全性

优点

  • 抗彩虹表攻击(自动加盐)
  • 可通过调整 cost factor 适应硬件发展
  • 密码长度上限 72 字节,超出部分被截断
  • 实现简单,广泛使用

缺点 / 注意事项

  • 内存消耗低,容易被 GPU/ASIC 大规模并行破解
  • 72 字节密码截断限制(bcrypt 基于 Blowfish,其子密钥最长 56 字节)
  • 在需要抗 GPU 攻击的场景,建议使用 Argon2、scrypt 或 PBKDF2(配合高迭代次数)
  • Argon2 已赢得 Password Hashing Competition (PHC),被推荐为新一代标准

与其他算法对比

算法 抗 GPU 内存消耗 推荐场景
BCrypt 传统场景,兼容性要求高
scrypt 中高 需要抗硬件加速
PBKDF2 HSM / 合规要求场景
Argon2 当前推荐首选

最佳实践

  1. cost factor ≥ 12(可根据服务器性能调整,确保哈希耗时 250ms–1s)
  2. 不要自己生成 salt,让库自动处理
  3. 使用成熟库(如 Go 的 golang.org/x/crypto/bcrypt、Python 的 bcrypt、Java 的 jBCrypt
  4. 传输层加密:bcrypt 只保护存储,传输必须用 TLS
  5. 新项目优先考虑 Argon2id