TLS 1.3 对密钥管理进行了重大改革,其中 HKDF (HMAC-based Key Derivation Function) 是核心组件。本文详细解释 HKDF 的工作原理以及在 TLS 1.3 中的实际应用。

什么是 HKDF?

HKDF 是一种基于 HMAC 的密钥派生函数,由 Hugo Krawczyk 和 Mihir Bellare 设计。它将短的输入密钥材料 (IKM) 扩展为更长的密钥,用于 TLS 握手后的各类密钥派生。

HKDF 由两个阶段组成:

  1. Extract(提取):将输入密钥材料转换为伪随机密钥
  2. Expand(扩展):从伪随机密钥派生出任意长度的输出

HKDF 工作原理

Extract 阶段

1
PRK = HMAC-Hash(Salt, IKM)
  • Salt:可选的盐值(TLS 1.3 中使用空字符串或握手特定值)
  • IKM:输入密钥材料(TLS 1.3 中为 DH 共享密钥)
  • PRK:伪随机密钥(Pseudo Random Key)

Expand 阶段

1
OKM = HMAC-Hash(PRK, Info || T)
  • Info:上下文特定信息(TLS 1.3 中用于区分不同用途的密钥)
  • T:计数器,扩展过程中逐步递增
  • OKM:输出密钥材料

TLS 1.3 密钥派生流程

TLS 1.3 使用 HKDF 构建了清晰的密钥派生层级:

1
2
3
4
5
6
7
DH 共享密钥
    ├──→ Early Secret ──────→ 0-RTT 密钥
    └──→ Handshake Secret ──→ 握手阶段密钥
              └──→ Master Secret ──→ 应用层密钥

派生阶段详解

TLS 1.3 的 Derive-Secret 函数定义如下:

1
2
Derive-Secret(Secret, Label, Messages) = 
    HKDF-Expand-Label(Secret, Label, Hash(Messages), 32)

其中 Label 包含 tls13 前缀,用于区分不同用途:

Label 用途
tls13 early secret 早期密钥材料
tls13 c e traffic 客户端早期流量密钥 (0-RTT)
tls13 s e traffic 服务端早期流量密钥 (0-RTT)
tls13 handshake secret 握手密钥材料
tls13 c h traffic 客户端握手流量密钥
tls13 s h traffic 服务端握手流量密钥
tls13 master secret 主密钥材料
tls13 application traffic 应用层流量密钥

Python 验证 HKDF

使用 Python 的 cryptography 库可以验证 HKDF 派生过程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes

ikm = b'input_key_material_16byte'
salt = b'salt_value_16byte'
info = b'tls13-AES-128-GCM'

hkdf = HKDF(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    info=info,
)
key = hkdf.derive(ikm)
print(key.hex())  # ca0c0017464f617145d3608cfcc20408af2da23ec2251e541e7ed9d3562dd215

输出长度为 32 字节(256 位),可用于 AES-256-GCM 加密。

TLS 1.3 密钥派生实例

以下是从 DH 共享密钥到应用层密钥的完整派生过程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
1. DH 共享密钥 (32 bytes)
       ↓ HKDF-Extract
   Early Secret (32 bytes)
       ↓ HKDF-Expand-Label(c e traffic)
   客户端早期密钥 (32 bytes)

2. DH 共享密钥 + 握手消息
       ↓ HKDF-Extract
   Handshake Secret (32 bytes)
       ↓ HKDF-Expand-Label(c h traffic)
   客户端握手密钥 (32 bytes)

3. Handshake Secret + Finished Key
       ↓ HKDF-Extract
   Master Secret (32 bytes)
       ↓ HKDF-Expand-Label(c ap traffic)
   客户端应用层密钥 (32 bytes)

为什么使用 HKDF?

设计优点

  1. 灵活性:可从任意长度的输入派生出任意长度的输出
  2. 可证明安全性:基于 HMAC 的安全性证明
  3. 抗原像攻击:单向派生,无法从输出反推输入
  4. 上下文隔离:通过 Info 参数区分不同用途的密钥

TLS 1.3 改进

相比 TLS 1.2,TLS 1.3 的密钥派生:

TLS 1.2 TLS 1.3
PRF (MD5/SHA) HKDF (SHA-256/384)
密钥交换与派生耦合 清晰的 HKDF 派生层级
4-RTT 1-RTT 或 0-RTT

实际应用

验证握手密钥

在调试 TLS 1.3 握手时,可以手动验证派生出的密钥:

1
2
# 使用 OpenSSL 查看握手详情
openssl s_client -connect example.com:443 -tls1_3 -debug 2>&1 | grep -A5 "Key Share"

排查 0-RTT 问题

0-RTT 使用 Early Secret 派生的密钥,如果遇到重放攻击问题,检查:

1
2
# 查看是否启用了 0-RTT
openssl s_client -connect example.com:443 -tls1_3 -early_data </dev/null 2>&1 | grep "Early Data"

注意事项

  1. 密钥材料安全:输入密钥材料必须来自安全的密钥交换
  2. Info 参数:不同用途必须使用不同的 Info,防止密钥混淆
  3. 前向安全性:TLS 1.3 每个阶段使用独立的密钥,任何阶段泄露不影响其他阶段

参考来源