什么是 KDF?理解“口令 → 密钥”的桥梁
KDF(Key Derivation Function,密钥派生函数)是一类将任意长度的"种子秘密"(如用户口令、DH 共享值、主密钥)转换为一个或多个固定长度、高熵密钥的密码学算法。它的基本形式可以抽象为:DK = KDF(Password, Salt, Cost, Length),其中 DK 代表派生密钥(Derived Key)。
为什么需要 KDF?原因在于现实世界的秘密与密码学算法要求之间存在结构性差距:
- 长度差距:AES-256 需要精确的 32 字节输入,而用户口令通常只有 8-16 个 ASCII 字符。
- 熵值差距:即使是"强口令",其熵值通常也远低于均匀随机的 32 字节(约 256 bit 熵)。
- 结构差距:用户口令有字典结构、拼写规律、键盘邻近字符等可预测特征,直接当密钥时极易被暴力破解。
KDF 的核心价值就在于"抹平"上述差距:它把低熵、变长、人类可读的口令,拉伸成一个高熵、定长、不可预测的二进制密钥。在此过程中,KDF 需要兼顾两个看似矛盾的目标:一是单向不可逆(拿到 DK 推不出原口令),二是确定性(相同输入始终产生相同输出)。我们的工具支持四种主流 KDF,让您在一个页面里就能直观对比它们的差异。
为什么需要 KDF?彩虹表、暴力破解与密码存储安全
早期系统常常直接存储用户口令的明文,或者仅做一次 MD5/SHA-1 哈希。这些做法在今天看来极其危险,因为它们无法抵御两种经典攻击:
① 彩虹表攻击(Rainbow Table):攻击者预先计算大量可能口令的哈希值,形成一张"哈希 → 原文"的大表。一旦拿到目标系统的哈希表,只需查表即可还原密码。防范的关键是使用随机盐值——每个用户使用独立的随机盐,使得预计算表对每个用户都不同,彩虹表在存储规模上迅速失去可行性。
② 离线暴力破解(Offline Brute-force):一旦攻击者拿到密码哈希文件,他们可以利用 GPU / ASIC 以每秒数十亿次的速度尝试各种口令组合。现代 GPU 对 SHA-256 的速度可以超过 10^9 hash/s。要防御这种攻击,KDF 必须让单次哈希计算的成本足够高——例如执行 100,000 次迭代,使每秒能验证的密码数量从"十亿"变成"一万",攻击时间从"秒级"拉长到"天级"甚至"年级"。
由此可见,一个 KDF 的"强度"主要取决于两个要素:盐值的随机性与计算开销。下文会详细展开每类算法的具体表现,并给出 OWASP、NIST 和 PHC 的推荐参数。
PBKDF2 与 HKDF:最通用的密钥派生方案
PBKDF2(Password-Based Key Derivation Function 2,RFC 2898 / PKCS #5 v2.0)是目前使用最广泛的 KDF。它的工作方式非常简洁:将 HMAC 函数反复迭代 N 次,把中间结果进行异或或拼接,最终产生指定长度的密钥。核心参数包括:
- 迭代次数(c / iterations):NIST SP 800-132 建议不低于 1,000,OWASP 2023 推荐不少于 100,000(SHA-256)。
- 哈希算法(PRF):常见搭配 HMAC-SHA-256、HMAC-SHA-512。在 SHA-1 已不安全的今天,应避免使用。
- 盐值(Salt):建议长度不低于 128 bit(16 字节),每次生成时使用密码学安全的随机数。
- 派生长度(dkLen):根据目标密钥的需求设定,典型值为 16、32、64 字节。
HKDF(HMAC-based Key Derivation Function,RFC 5869)则解决了另一个问题:当你已经有一个"高熵共享秘密"(如 DH、ECDH 输出、随机数生成的种子),需要从中派生出多个独立密钥时。HKDF 分为两步:
- Extract(提取):使用盐值对输入的密钥材料做一次 HMAC,"压缩"成一个固定长度的伪随机密钥(PRK)。
- Expand(扩展):用 PRK 作为 HMAC 的密钥,反复推导 N = ceil(L/HashLen) 次,得到最终密钥流 OKM。
一个常见的误用是"用 HKDF 代替 PBKDF2 来派生弱口令"——由于 HKDF 的计算非常快,它不适合把低熵口令做强化。正确用法是:口令 → PBKDF2 / Argon2 → 主密钥 → HKDF → 多业务子密钥的组合链。我们的工具中,您可以把 PBKDF2 输出的二进制密钥直接粘贴到 HKDF 模式中进一步派生多份独立子密钥。
Scrypt 与 Argon2:内存困难算法的演进
即便迭代次数调到百万级,PBKDF2 仍然是纯 CPU 成本——GPU / ASIC 在并发 SHA-256 计算上可以轻松达到 CPU 的 1,000 倍以上。为了打破这个不对称,密码学家引入了"内存困难(Memory-Hard)"思路:让每次派生需要消耗大量随机读写的 RAM,使得 GPU/ASIC 的并行优势被大幅削弱。
Scrypt(Colin Percival, 2009 / RFC 7914)是第一个真正实用的内存困难 KDF。它引入了三个参数:
- N:内存成本因子(必须为 2 的幂,通常 16,384 ~ 1,048,576)。
- r:块大小(通常 8),决定一次读取操作混合的字节数。
- p:并行因子(通常 1),增加 p 会在不显著增加内存的前提下提高 CPU 工作量。
Scrypt 的近似内存占用为 128 × N × r 字节。当 N=16384、r=8 时约为 16 MiB;当 N=1,048,576 时约为 1 GiB,这对攻击者来说极为昂贵。
Argon2(PHC 2015 冠军)则把内存困难做到了当代最前沿。它有三个变体:Argon2d(对侧信道攻击较敏感但抗 GPU 最好,用于加密货币挖矿)、Argon2i(抗侧信道最安全,推荐用于密码存储)、Argon2id(混合模式,目前 OWASP 的第一推荐)。主要参数:
- t:时间成本(迭代次数,推荐 1-3)。
- m:内存成本(KiB,推荐 64 MiB 或更高)。
- p:并行度(通常 1,高并发应用可用 4)。
- output length:输出长度,通常 32 字节。
OWASP 2024 推荐:使用 Argon2id,参数为 t=3, m=65536 (64 MiB), p=4。如果系统资源受限,可使用 t=2, m=37000, p=1;在无法使用 Argon2 的场景下,退而求其次使用 Scrypt(N=32768, r=8, p=1)或 PBKDF2(SHA-256,iterations ≥ 600,000)。在我们的 KDF 工具中可以一键切换这些算法对比派生速度。
盐值管理与参数选择的最佳实践
在讨论"用什么算法"之后,真正决定系统安全的常常是"参数与盐值怎么存"。以下是实用清单:
① 盐值的三条黄金规则
- 唯一:每个用户 / 每次派生使用独立盐值,绝不在多条记录间共用同一盐。
- 不可预测:使用密码学安全的随机数生成器(如浏览器的 crypto.getRandomValues、Node.js 的 crypto.randomBytes)生成至少 16 字节。
- 独立存储:盐值不需要保密,但必须与派生密钥(或哈希)一起持久化;不要把盐和密码"拼"成一个字符串再哈希,这样会丧失唯一性。
② 参数的选择方法
- 在目标环境中做一次基准测试:调整参数使单次派生花费 100 ms ~ 1,000 ms(对注册/登录等交互场景)或 1 s ~ 5 s(对备份加密等高风险场景)。
- Argon2 的参数调优通常先固定内存 m(如 64 MiB)、并行度 p(如 4),再提高 t 直到耗时满足目标。
- 一旦密码哈希存储在数据库中,建议使用密钥分隔(Pepper / Secret Pepper):把一个服务器端密钥(不入库)与口令一起混合进 KDF。即使数据库被拖走,缺少 Pepper 的攻击者仍然无法暴力破解。
③ 存储格式的可升级性(Agility)
使用结构化字符串(类似 PHC / modular crypt format)把参数与密钥一起存储,例如 $argon2id$v=19$m=65536,t=3,p=4$salt_b64$dk_b64。这样将来可以直接提高参数值,让老用户在下一次登录时"自动"迁移到更高参数,而不会破坏旧记录的兼容性。
真实系统中的 KDF:协议、密码存储与多密钥派生
以下是 KDF 在现代系统中的典型应用模式,能帮助你更系统地选择与组合算法:
① 用户密码存储(Server-Side Password Hash)
绝大多数 Web 应用使用"BCrypt / Scrypt / Argon2"存储密码。推荐顺序:Argon2id → Scrypt → BCrypt → PBKDF2 + HMAC-SHA-256。注意:PBKDF2 的安全性高度依赖 HMAC 迭代次数与是否叠加"服务器端 Pepper"。在新系统中应优先选 Argon2id。
② 全磁盘加密(FDE)
BitLocker、VeraCrypt、LUKS 等全磁盘加密产品都是用 KDF 从用户口令派生磁盘加密主密钥。典型链路是:口令 → KDF(如 Argon2 / Scrypt / PBKDF2)→ 卷主密钥 → XTS-AES 加密扇区。这里的参数通常会被设置得非常"慢"(单次派生数秒),因为用户开机频率低、攻击者只能离线穷举。
③ 密钥协商协议中的 HKDF
TLS 1.3、Signal、WireGuard 等协议在完成 ECDHE / X25519 握手后,会使用 HKDF 把协商出的共享秘密(Shared Secret)"扩展"成多份独立子密钥(客户端流量密钥、服务器端流量密钥、IV、Finished MAC Key 等)。这种模式的价值在于"隔离"——即使后续某个子密钥泄露,也无法回溯到共享秘密或其他子密钥。
④ 主密钥 → 多业务密钥的派生
在大型系统中,安全团队通常会保管一个离线的"主密钥(Master Key)",然后使用 HKDF 配合不同的业务标签(info 参数)为支付、用户认证、消息加密等业务分别派生出独立子密钥。这样做的好处是:主密钥离线,各业务密钥彼此隔离,任何一个子密钥泄露不会影响其他业务。您可以在我们的工具中模拟这一流程:先使用 PBKDF2 得到主密钥,再用 HKDF + 不同 info 标签派生出多份业务子密钥。
数据安全与隐私:为什么选择本地处理的在线工具
密钥派生涉及的输入(口令、共享秘密、盐值)通常都是极高敏感信息,一旦发送到远程服务器就等于把"开门的钥匙"交给了第三方。即使服务器承诺不存储,任何传输、日志、监控环节都存在泄露风险。
基于这个原则,本工具的所有计算都在浏览器本地完成:
- PBKDF2 与 HKDF:直接调用浏览器的标准 Web Crypto API,由各浏览器厂商经过严格审计的实现完成;
- Scrypt 与 Argon2id:使用开源的密码学库(如 noble-hashes)在 JS 层完成,不经过任何网络;
- 任何输入不会被持久化到 localStorage,不会被复制到剪贴板(除非用户主动点击"复制"按钮),刷新浏览器后即全部清空。
在离线或生产环境使用时,我们建议:打开浏览器后断开网络,或直接在本地部署 HTML 文件后使用。这样可以确保:即便浏览器本身有远程代码或扩展,它们也无法把你的密码材料上传到任何地方。
最后提醒:在选择任何 KDF 工具时,都应该确认它不使用自定义密码学原语、不使用弱哈希、所有关键参数可由用户配置。我们的工具源码、依赖与文档均遵循这一原则,您可以参考KDF 工具页和相关指南进行交叉验证。