TLS 1.3 密钥调度
TLS 1.3 用 HKDF 把握手密钥分阶段派生,形成一条密钥调度链:Early Secret(用于 0-RTT 早期数据)、Handshake Secret(保护握手消息)、Master Secret(派生应用数据密钥)。每个阶段通过 HKDF-Extract 与 HKDF-Expand-Label 派生出客户端与服务端各自方向的流量密钥,两个方向的密钥并不相同。会话中需要更换密钥时,任一方发送 KeyUpdate 消息即可在线更新,不必重新握手,也不影响连接上的业务数据。
基本信息
| 派生机制 | HKDF-Extract 与 HKDF-Expand-Label |
|---|---|
| 三个阶段 | Early Secret → Handshake Secret → Master Secret |
| 方向分离 | client_application_traffic_secret / server_application_traffic_secret |
| 密钥更新 | KeyUpdate 消息触发,无需重新握手 |
| 前向安全 | 阶段密钥用完即从内存清除,共享密钥不能长期留存 |
典型使用场景
- 协议分析与抓包解密:需要 keylog 文件才能解开 TLS 1.3 流量
- 排查「两个方向密钥不同」引起的实现误判与自研协议对接问题
- 评估长连接的密钥更新策略是否按预期触发
- 理解 0-RTT 早期数据为何存在重放风险:它由会话票据派生的早期密钥保护
- 协议实现自测:对照 RFC 8446 的标签名称核对派生结果
观察密钥更新与版本
观察密钥更新与版本
openssl s_client -connect example.com:443 -tls1_3 -keylogfile tls.keys </dev/null # 导出 NSS key log
# 把 tls.keys 配给 Wireshark 即可解密该会话(仅限受控调试环境)
# 确认协商到的版本与套件
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>&1 | grep -E 'Protocol|Cipher'三阶段的密钥用途
| 阶段 | 来源 | 保护的数据 |
|---|---|---|
| Early Secret | 会话票据 / PSK | 0-RTT 早期数据,缺乏完整前向安全且可重放 |
| Handshake Secret | ECDHE 共享密钥 | 握手消息:证书、Finished 等 |
| Master Secret | Handshake Secret | 应用数据,派生后仍可 KeyUpdate 更新 |
| KeyUpdate | 当前应用密钥 | 更新后同一连接继续传输,旧密钥可丢弃 |
SSLKEYLOGFILE 产出的 keylog 是明文流量密钥:泄漏它等于该会话可被完整解密。只允许在受控调试环境临时开启,调试结束立即删除;抓包解密只对当时记录的新会话有效,历史流量无法回溯。
实现侧要点:派生用的标签常量必须逐字节一致,包括上下文里的握手哈希;任何一端把「握手消息」与「应用数据」的标签混用,都会得到不同密钥并导致 Finished 校验失败。自研协议栈时建议先用已知测试向量对齐,再接入真实流量。