HKDF 是什么
HKDF 是 RFC 5869 定义的两阶段通用密钥派生函数。第一阶段 Extract 把输入密钥材料压缩成固定长度的伪随机密钥 PRK,作用是把分布不均匀的熵「提纯」;第二阶段 Expand 用 PRK 加上上下文标签 info 扩展出任意长度、彼此独立的子密钥。TLS 1.3 的密钥调度完全建立在它之上,握手密钥与记录层密钥都由它派生。
基本信息
| 阶段一 | Extract(salt, IKM) → PRK |
|---|---|
| 阶段二 | Expand(PRK, info, L) → OKM |
| 适用输入 | 已具备一定熵的共享秘密、DH 协商输出 |
| 不适用输入 | 低熵口令,应先经过慢散列 |
| salt | 可省略,省略时按全零处理 |
| 隔离手段 | 不同用途必须使用不同的 info 标签 |
典型使用场景
- 从 DH 或 ECDH 协商出的共享秘密派生会话密钥,为加密与认证分配不同子密钥
- 实现 TLS 1.3 风格的密钥调度:用握手上下文派生每条记录的独立密钥
- 文件加密生成数据密钥:主密钥经 HKDF 扩展出每份文件各自的密钥
- 协议设计里把一次协商结果拆成多个方向、多个用途的密钥
- 做密钥版本管理:info 中携带版本号,升级前后派生出互不干扰的密钥
Python 示例(cryptography)
Python 示例(cryptography)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
# 同一共享秘密派生两把用途不同的密钥,靠 info 标签隔离
enc = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=b'enc-key').derive(shared_secret)
mac = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=b'mac-key').derive(shared_secret)
# info 中带上协议版本与实体标识,避免不同场景派生出一把密钥参数读法
| 参数 | 作用 | 取值建议 |
|---|---|---|
| salt | Extract 阶段的初始盐 | 有协议上下文时传明确值,无则省略 |
| info | 绑定派生用途的标签 | 包含用途、版本与实体标识 |
| length | 输出长度 | 按算法需要,不超过 255 倍哈希长度 |
TLS 1.3 的密钥调度使用 HKDF-Expand-Label,把握手上下文(Transcript-Hash)作为输入,实现每条记录密钥的独立派生。
HKDF 假设输入已有足够熵:它不增加计算代价,也不会慢化攻击者的尝试。低熵口令必须先经过 PBKDF2、scrypt 或 Argon2id,再交给 HKDF 扩展。
info 标签最容易被忽视:同一 PRK 派生的密钥如果 info 相同,得到的其实是同一把密钥,多用途共用会让某一处的泄露波及全部场景。