密钥派生函数(KDF):HKDF、PBKDF2、scrypt 与 Argon2 深度对比

密码学概念 · 2026-06-03

概述

密钥派生函数(Key Derivation Function, KDF)是密码学中最基础也最容易被忽视的组件之一。它的核心任务是:从一个可能不够均匀、不够长的输入密钥材料(Keying Material)中,派生出密码学安全的密钥

为什么需要 KDF?因为密码学算法(AES、SM4、HMAC 等)要求密钥具有特定的长度和均匀分布的随机性,而现实中的密钥来源往往不满足这些要求:

  • 用户密码:长度不一、熵值低
  • Diffie-Hellman 共享秘密:长度固定但可能不够均匀
  • 主密钥:需要派生出多个用途不同的子密钥
KDF 解决了这些问题,同时提供了密钥分离(不同用途使用不同密钥)和密钥扩展(从短密钥生成长密钥)的能力。

KDF 的分类

根据应用场景和安全目标,KDF 可以分为两大类:

1. 基于伪随机函数的 KDF(PRF-based KDF)

适用于高熵输入(如 DH 共享秘密、主密钥),目标是密钥分离和扩展。

代表:HKDF(RFC 5869)

2. 基于密码的 KDF(Password-based KDF, PBKDF)

适用于低熵输入(如用户密码),目标是增加计算成本以抵抗暴力破解。

代表:PBKDF2(RFC 8018)、scrypt(RFC 7914)、Argon2(RFC 9106)

HKDF:基于 HMAC 的密钥派生

设计背景

HKDF(HMAC-based Key Derivation Function)由 Krawczyk 在 2010 年提出,定义于 RFC 5869。它是最简洁、最通用的 KDF 设计,被 TLS 1.3、Signal 协议、WireGuard 等广泛采用。

两阶段结构

HKDF 由两个阶段组成:

阶段 1:HKDF-Extract(提取)

CODE
PRK = HMAC-Hash(salt, IKM)
  • IKM(Input Keying Material):输入密钥材料
  • salt:可选的盐值(不提供则使用全零串)
  • PRK(Pseudo-Random Key):提取出的伪随机密钥
Extract 阶段的作用是将可能不均匀的 IKM 压缩为固定长度的伪随机值。即使 IKM 的熵分布不均,HMAC 的输出也是均匀分布的。

阶段 2:HKDF-Expand(扩展)

CODE
T(0) = empty string
T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
...
OKM = T(1) | T(2) | ... 截断到所需长度
  • info:可选的上下文信息(用于密钥分离)
  • OKM(Output Keying Material):输出的派生密钥
Expand 阶段通过迭代 HMAC 生成任意长度的输出。不同的 info 值可以从同一个 PRK 派生出多个独立的密钥。

安全特性

  • 安全性归约:HKDF 的安全性归约到底层 PRF(HMAC)的安全性
  • 密钥分离:不同的 info 值保证派生密钥的独立性
  • 前向安全:即使某个派生密钥泄露,也无法反推 PRK 或其他派生密钥

国密场景中的 HKDF

在国密体系中,HKDF 可以使用 SM3 作为底层哈希函数:

CODE
PRK = HMAC-SM3(salt, IKM)
T(i) = HMAC-SM3(PRK, T(i-1) | info | i)

GM/T 0003.4-2012(SM2 密钥交换协议)中定义的密钥派生函数与 HKDF-Expand 结构类似,使用 SM3 哈希迭代生成密钥数据。

PBKDF2:基于密码的密钥派生

设计背景

PBKDF2(Password-Based Key Derivation Function 2)定义于 PKCS#5 v2.0(RFC 8018),是最广泛使用的密码哈希函数。

算法结构

CODE
DK = PBKDF2(PRF, Password, Salt, c, dkLen)

其中:

  • PRF:伪随机函数(通常为 HMAC-SHA256 或 HMAC-SM3)
  • Password:用户密码
  • Salt:盐值(推荐 ≥128 位)
  • c:迭代次数(推荐 ≥600,000 次,OWASP 2023 建议)
  • dkLen:派生密钥长度
核心计算:

CODE
U₁ = PRF(Password, Salt || INT(32, i))
U₂ = PRF(Password, U₁)
...
U_c = PRF(Password, U_{c-1})
T_i = U₁ ⊕ U₂ ⊕ ... ⊕ U_c
DK = T₁ || T₂ || ... 截断到 dkLen

安全分析

PBKDF2 通过迭代增加计算成本,但存在一个根本性弱点:内存硬度不足

攻击者可以使用 ASIC 或 GPU 并行计算,因为每次迭代只需要存储前一次的结果(O(1) 内存)。这使得 PBKDF2 在面对大规模并行攻击时防护能力有限。

参数选择建议

安全级别迭代次数适用场景
最低100,000遗留系统
推荐600,000一般应用(OWASP 2023)
高安全1,000,000+敏感数据保护

scrypt:内存困难型 KDF

设计背景

scrypt 由 Colin Percival 于 2009 年提出,定义于 RFC 7914。它专门设计来抵抗 ASIC/GPU 攻击,通过内存困难性(Memory-hardness)大幅提高攻击者的硬件成本。

算法结构

CODE
scrypt(N, r, p, Password, Salt, dkLen)

核心参数:

  • N:CPU/内存成本参数(必须是 2 的幂,推荐 2^17 = 131072)
  • r:块大小参数(推荐 8)
  • p:并行化参数(推荐 1)
阶段 1:生成大内存块(ROMix)

  • 使用 PBKDF2 生成初始块 B
  • 对 B 中的每个块 V_i,执行 N 次迭代:
CODE
V_i = ScryptROMix(B_i, N)
  • ScryptROMix 维护一个大小为 N 的数组 V,每次迭代都随机读写数组元素
阶段 2:最终派生

CODE
DK = PBKDF2(Password, B, 1, dkLen)

内存困难性原理

scrypt 的核心思想是:计算过程需要大量内存,且内存访问模式是伪随机的

  • 内存需求:128 × N × r 字节(N=2^17, r=8 时约 128MB)
  • 攻击者如果想减少内存使用,就必须重新计算丢失的块,这会增加计算量
  • ASIC 的优势在于并行计算,但内存是物理资源,无法通过增加核心数来分摊

与国密结合

scrypt 可以与 SM3 结合使用:

PYTHON
import hashlib

# 使用 HMAC-SM3 替代 HMAC-SHA256
def sm3_prf(password, salt):
    return hmac.new(password, salt, hashlib.sm3).digest()

Argon2:密码哈希竞赛冠军

设计背景

Argon2 是 2015 年密码哈希竞赛(PHC)的获胜者,定义于 RFC 9106。它综合了 scrypt 的内存困难性和新的数据依赖内存访问模式,是目前最推荐的密码哈希算法

三种变体

变体特点适用场景
Argon2d数据依赖内存访问,抗 GPU/ASIC加密货币、无侧信道风险场景
Argon2i数据独立内存访问,抗侧信道密码哈希(推荐)
Argon2id混合模式(前几轮独立,后几轮依赖)通用推荐

算法结构

CODE
Argon2(type, version, m, t, p, Password, Salt, ...)

核心参数:

  • type:Argon2d/Argon2i/Argon2id
  • m:内存大小(KB),推荐 65536(64MB)
  • t:迭代次数,推荐 3
  • p:并行度,推荐 4
核心步骤:

  • 初始化:用密码和盐值填充初始块
  • 填充内存:生成 m/4 个 1KB 块,每个块依赖前一个块
  • 迭代混合:进行 t 轮混合,每轮对块进行 Blake2b 哈希
  • 最终化:对最后一个块进行哈希得到输出

安全优势

  • 可证明安全:Argon2 的安全性可以归约到 Blake2b 的安全性
  • 灵活配置:可以独立调整内存、时间和并行度
  • 抗多种攻击:同时抵抗 GPU、ASIC 和侧信道攻击

OWASP 2023 推荐参数

CODE
Argon2id: m=65536 KB, t=3, p=4
Argon2id: m=131072 KB, t=4, p=4(高安全)

四种 KDF 深度对比

维度HKDFPBKDF2scryptArgon2
输入类型高熵密钥低熵密码低熵密码低熵密码
内存困难性
抗 GPU/ASIC
抗侧信道不适用不适用强(Argon2i/id)
计算速度极快快(可调)中(可调)
内存需求O(1)O(1)O(N×r)O(m)
标准化RFC 5869RFC 8018RFC 7914RFC 9106
国密适配HMAC-SM3HMAC-SM3可适配可适配
典型用途TLS 密钥派生密码存储密码存储密码存储(首选)

工程选型指南

场景 1:从 DH/SM2 共享秘密派生会话密钥

推荐:HKDF

PYTHON
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes

# 国密场景:使用 SM3
# 注意:Python cryptography 库原生不支持 SM3,需要使用 gmssl 库
hkdf = HKDF(
    algorithm=hashes.SM3(),  # 需要 gmssl 或自定义实现
    length=32,
    salt=None,
    info=b"session-key-v1",
)
key = hkdf.derive(shared_secret)

场景 2:用户密码存储

推荐:Argon2id

PYTHON
from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=3,      # 迭代次数
    memory_cost=65536, # 64MB
    parallelism=4,
    hash_len=32,
    salt_len=16,
)
hash = ph.hash("user_password")
ph.verify(hash, "user_password")

场景 3:从主密钥派生多个子密钥

推荐:HKDF(不同 info 值)

PYTHON
# 派生加密密钥
enc_key = hkdf_derive(master_key, info=b"encryption")
# 派生 MAC 密钥
mac_key = hkdf_derive(master_key, info=b"authentication")
# 派生 IV
iv_key = hkdf_derive(master_key, info=b"iv-generation")

场景 4:国密合规场景

在需要满足国密合规要求的系统中:

  • 密钥派生:使用 GM/T 0003.4-2012 定义的 KDF(基于 SM3)
  • 密码存储:使用 PBKDF2-HMAC-SM3 或 Argon2(底层使用 SM3)
  • 随机数:使用 GM/T 0005-2012 定义的随机数生成器

常见误区

误区 1:直接用哈希函数作为 KDF

PYTHON
# ❌ 错误:直接用 SHA-256/SM3 哈希
key = sha256(password + salt)

# ✅ 正确:使用专用 KDF
key = hkdf(password, salt, info)

直接用哈希函数缺少密钥分离能力,且对低熵输入没有保护。

误区 2:盐值复用

PYTHON
# ❌ 错误:所有用户使用相同盐值
key = pbkdf2(password, fixed_salt)

# ✅ 正确:每个用户使用随机盐值
salt = os.urandom(16)
key = pbkdf2(password, salt)

盐值的作用是防止彩虹表攻击,复用盐值等于没有盐值。

误区 3:迭代次数过低

PYTHON
# ❌ 错误:迭代次数太低
key = pbkdf2(password, salt, iterations=1000)

# ✅ 正确:使用推荐的迭代次数
key = pbkdf2(password, salt, iterations=600000)

迭代次数直接决定暴力破解的成本。

总结

密钥派生函数是密码学基础设施的基石。选择合适的 KDF 需要综合考虑:

  • 输入熵值:高熵用 HKDF,低熵用 PBKDF2/scrypt/Argon2
  • 安全需求:一般场景用 PBKDF2,高安全用 Argon2id
  • 性能约束:资源受限环境用 PBKDF2,一般环境用 Argon2id
  • 合规要求:国密场景需要适配 SM3 哈希函数
在现代密码学工程实践中,HKDF + Argon2id 的组合覆盖了绝大多数场景:Argon2id 用于密码到密钥的转换,HKDF 用于密钥的分离和扩展。

参考来源

  • RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
  • RFC 8018: PKCS #5: Password-Based Cryptography Specification Version 2.1
  • RFC 7914: The scrypt Password-Based Key Derivation Function
  • RFC 9106: Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work
  • GM/T 0003.4-2012: SM2 公钥密码算法 第4部分:密钥交换协议
  • GM/T 0005-2012: 随机性检测规范
  • OWASP Password Storage Cheat Sheet (2023)

相关实践