2. TLS 1.3 握手与密钥调度回顾
TLS 1.3 是后量子密码(PQC)迁移的主战场,原因不只是它被广泛部署,更在于它的握手结构把“密钥交换”“身份认证”“记录层保护”和“密钥派生”拆成了边界清晰的阶段。理解这些边界,才能判断哪些环节可以替换为 ML-KEM、ML-DSA 等 PQC 算法,哪些环节必须保持兼容,以及 0-RTT 为什么在 PQC 迁移中格外棘手。
握手消息与两条主线
TLS 1.3 的完整握手可以概括为两条并行推进的主线:一条是密钥交换主线,负责让双方在不安全信道上协商出共享秘密;另一条是认证主线,负责证明服务器(可选客户端)的身份。两条主线在密钥调度处汇合,最终派生出记录层所需的各类密钥。
握手从 ClientHello 开始。它携带客户端支持的密码套件、supported_groups(密钥交换组)、signature_algorithms(签名算法)、随机数 client_random,以及一个或多个 key_share 扩展。在 TLS 1.3 中,key_share 已经携带了客户端对某个群的临时公钥,例如 X25519 的 32 字节公钥。服务器收到后,如果接受该群,就在 ServerHello 中返回自己的 key_share,双方各自用 ECDH 计算出共享秘密 ECDHE。这一步完成后,握手已经具备前向保密性。
ServerHello 之后,服务器发送 EncryptedExtensions、Certificate、CertificateVerify、Finished。其中 Certificate 携带证书链,CertificateVerify 用证书私钥对握手转录(transcript)签名,证明服务器确实持有证书对应私钥。客户端验证证书链和签名后,发送自己的 Finished,握手完成。此后应用数据在记录层被加密传输。
HKDF 密钥调度:从共享秘密到记录层密钥
TLS 1.3 的密钥派生完全基于 HKDF,分为 HKDF-Extract 和 HKDF-Expand 两步。Extract 把输入密钥材料“压缩”成固定长度的伪随机密钥(PRK),Expand 再把 PRK 扩展成所需长度的输出密钥材料。整个调度过程可以理解为一条链:
0 -> HKDF-Extract(salt=0, IKM=0) = Early Secret
Early Secret -> Derive-Secret(., "derived", "") -> salt
salt + ECDHE -> HKDF-Extract = Handshake Secret
Handshake Secret -> Derive-Secret(., "derived", "") -> salt
salt + 0 -> HKDF-Extract = Master Secret
Handshake Secret -> Derive-Secret(., "c hs traffic", transcript) = client_handshake_traffic_secret
Handshake Secret -> Derive-Secret(., "s hs traffic", transcript) = server_handshake_traffic_secret
Master Secret -> Derive-Secret(., "c ap traffic", transcript) = client_application_traffic_secret_0
Master Secret -> Derive-Secret(., "s ap traffic", transcript) = server_application_traffic_secret_0
这里有几个关键点。第一,Early Secret 在没有 PSK 时由全零输入派生,它对应 0-RTT 和 PSK 场景。第二,Handshake Secret 由 ECDHE 共享秘密参与 Extract,因此它绑定了本次会话的密钥交换结果。第三,Master Secret 不再直接参与记录层加密,而是作为派生应用流量密钥的根。第四,每个 traffic secret 都通过 Derive-Secret 绑定当前握手转录哈希,这意味着握手消息一旦被篡改,派生出的密钥就会不同,Finished 校验会失败。
记录层使用 AEAD 算法(如 AES-128-GCM 或 ChaCha20-Poly1305)。每个方向的流量密钥由 traffic secret 经 HKDF-Expand-Label 派生出 key 和 iv,再配合序列号构造 nonce。握手阶段的记录使用 handshake traffic secret,应用阶段切换到 application traffic secret。密钥更新(KeyUpdate)则通过对当前 traffic secret 再做一次 Derive-Secret 得到下一代密钥。
0-RTT 机制及其迁移影响
0-RTT 允许客户端在第一个飞行中就携带应用数据,前提是双方此前建立过会话并共享 PSK(通常来自 NewSessionTicket)。客户端用 PSK 派生出 client_early_traffic_secret,直接加密 early data。服务器若接受,就用同一 PSK 解密;若不接受,则丢弃 early data 并回退到完整握手。
0-RTT 的安全属性明显弱于 1-RTT:early data 不具备前向保密性,且可能被重放。服务器通常需要应用层配合做重放防护。在 PQC 迁移中,这带来两个直接问题:其一,PSK 本身若由经典算法派生,迁移后需要重新协商或升级 ticket 格式;其二,early data 的密钥派生路径依赖 Early Secret,若把密钥交换换成 ML-KEM,Early Secret 的输入结构不变,但 ticket 中绑定的参数(如 psk_key_exchange_modes、early_data 扩展)需要与新的密钥交换组协调。
PQC 迁移的切入点
把 TLS 1.3 的各个环节映射到 PQC 迁移,可以得到一张清晰的“可替换性地图”:
- 密钥交换:X25519/ECDHE 可替换为 ML-KEM-768 或混合模式 X25519MLKEM768。TLS 1.3 的
key_share和supported_groups已经为多组协商留出空间,混合模式是当前最稳妥的过渡方案。 - 认证:RSA/ECDSA 签名可替换为 ML-DSA 或 SLH-DSA。证书链和 CertificateVerify 的签名算法由
signature_algorithms协商,但证书本身需要重新签发,这涉及 PKI 改造。 - 密钥调度:HKDF 本身是哈希无关的,只要底层哈希(SHA-256/SHA-384)保持安全,调度结构不需要改变。PQC 迁移主要影响输入密钥材料的来源,而非 HKDF 逻辑。
- 记录层:AEAD 算法(AES-GCM、ChaCha20-Poly1305)目前不受量子威胁直接影响,可保持不变。真正需要关注的是密钥长度和 nonce 构造是否与新的密钥交换输出兼容。
- 0-RTT:迁移期间建议谨慎启用或暂时关闭,因为 PSK 与 early data 的重放风险在混合密钥交换下更难分析。
下面这张图把 TLS 1.3 完整握手的消息流、密钥调度链和 PQC 可替换点放在同一视图中,便于对照理解。
从这张图可以读出迁移的核心判断:HKDF 调度链本身不需要改动,因为它是哈希无关的通用构造;真正需要替换的是输入到 Extract 的密钥交换材料,以及 CertificateVerify 使用的签名算法。0-RTT 的 Early Secret 路径虽然结构不变,但它绑定的 PSK 和 ticket 参数需要与新的密钥交换组重新协调,因此在迁移过渡期应优先保证 1-RTT 完整握手的正确性,再逐步评估 0-RTT 的启用条件。
key_share 可能同时携带 X25519 和 ML-KEM 两个公钥,导致 ClientHello 体积显著增大。若超过路径 MTU 或服务器限制,可能触发 HelloRetryRequest 或分片,需要在部署前评估网络路径的承载能力。