两个函数各干什么
HMAC 用一个密钥把哈希函数变成带认证的完整性校验:只有持有密钥的一方能算出正确的校验值,因此它同时验证完整性与来源(RFC 2104)。HKDF 解决的是「从一份共享秘密派生出多把密钥」:先用提取阶段得到固定长度的伪随机密钥,再按不同上下文标识派生出任意多把用途明确的密钥(RFC 5869)。
为什么不能自己拼
直接把密钥拼在消息后面再哈希,不是 HMAC,会落入长度扩展一类问题;把哈希结果截断当密钥用,也缺少 HKDF 那样的结构性保证。TLS 1.3 的整个密钥调度都建立在 HKDF 之上,自己发明派生流程基本只是重复踩过的坑。
基本信息
| HMAC 结构 | 两轮哈希,密钥分别与内外填充异或,可抵御长度扩展 |
|---|---|
| HKDF 阶段 | 提取提炼熵,扩展按上下文派生多把密钥 |
| 比较要求 | 必须用常量时间比较,普通字符串相等会泄露时序 |
| 规范 | RFC 2104、RFC 5869,TLS 1.3 见 RFC 8446 §7.1 |
典型使用场景
- 开放接口签名:把参数与时间戳一起签名,防止篡改与重放
- 消息队列或回调地址的完整性校验
- 会话 Cookie 防篡改(签名而不是加密)
- 协议与加密库内部的密钥派生
HMAC 示例
HMAC 示例
openssl dgst -sha256 -hmac 'secret-key' message.txt # 命令行生成 HMAC
openssl rand -hex 32 # 生成对称密钥
# 代码里比较校验值必须用常量时间函数,例如 Python 的 hmac.compare_digest易混概念对照
| 概念 | 作用 | 典型误用 |
|---|---|---|
| HMAC | 带密钥的完整性校验 | 把密钥直接拼在消息后做哈希 |
| 普通哈希 | 只验完整性,不含密钥 | 当作签名使用,任何人都能重算 |
| HKDF | 从一份秘密派生多把密钥 | 直接截断哈希结果充当密钥 |
| AEAD | 同时完成加密与认证 | 只用加密不校验,无法发现篡改 |
派生时的上下文标识要写清用途:同一份主秘密派生出的「加密密钥」与「登录令牌」必须使用不同标识,避免跨用途复用。
比较校验值不能用普通字符串相等:比较过程会因为首个不同字节的位置而耗时不同,理论上可被逐步猜出正确值。