4. TLS 1.3中的PQC扩展

好,咱们进入正题。TLS 1.3 是目前最主流的加密握手协议,PQC 迁移绕不开它。说白了,我们要在 TLS 1.3 的框架里,把传统的密钥交换算法换成抗量子的,或者跟它们混着用。

我个人习惯把这一章拆成四个部分:先回顾 TLS 1.3 的握手流程,再看密码套件怎么定义,然后重点讲混合密钥交换的实现,最后聊扩展字段的适配。嗯,这样脉络比较清楚。

4.1 TLS 1.3 握手协议回顾

先快速过一遍 TLS 1.3 的握手。跟 1.2 比,它精简了很多。一次往返(1-RTT)就能完成握手,0-RTT 模式甚至能复用之前的会话。

核心流程是这样的:

  • ClientHello:客户端发支持的密码套件、密钥交换参数(比如 ECDHE 的公钥)、扩展字段。
  • ServerHello:服务端选套件、发自己的密钥交换参数、证书。
  • 密钥派生:双方用 (EC)DHE 算出共享密钥,再通过 HKDF 派生出会话密钥。
  • Finished:验证握手消息的完整性。

为什么提这个?因为 PQC 的密钥交换要嵌入到 ClientHello 和 ServerHello 的扩展字段里。你想想看,TLS 1.3 的扩展机制非常灵活,这给 PQC 的接入提供了天然的入口。

关键点:TLS 1.3 的握手是"先密钥协商,再身份认证"。PQC 的密钥交换必须在密钥协商阶段完成,不能等到证书验证之后。

4.2 支持PQC的密码套件定义

密码套件在 TLS 1.3 里简化了。它只定义 AEAD 算法和 HKDF 哈希算法。密钥交换算法是单独在扩展字段里指定的。

举个例子,传统的 TLS_AES_128_GCM_SHA256 套件,它只告诉你:用 AES-128-GCM 做加密,用 SHA-256 做 HKDF。至于密钥交换是 X25519 还是 P-256,那是 key_share 扩展的事。

那 PQC 的套件怎么定义?我建议这样看:

套件名称 AEAD HKDF 密钥交换(示例)
TLS_AES_128_GCM_SHA256 AES-128-GCM SHA-256 X25519 + Kyber-512
TLS_CHACHA20_POLY1305_SHA256 CHACHA20-POLY1305 SHA-256 P-256 + Kyber-768
TLS_AES_256_GCM_SHA384 AES-256-GCM SHA-384 X448 + Kyber-1024

你看,套件本身没变,变的只是 key_share 扩展里携带的密钥交换算法。我在项目中遇到过有人试图在套件名称里直接写 "Kyber",结果发现 TLS 1.3 的套件注册表根本不支持这种定义方式。嗯,这是个坑。

我的建议:不要修改现有的 TLS 1.3 密码套件。把 PQC 的算法放在扩展字段里,这样兼容性最好。

4.3 混合密钥交换在TLS中的实现

混合密钥交换是过渡期的核心。说白了,就是同时跑一个传统算法(比如 X25519)和一个 PQC 算法(比如 Kyber-512),然后把两个共享密钥合并成一个。

为什么要混合?因为 PQC 算法还没经过足够长时间的实战检验。万一哪天 Kyber 被攻破了,传统算法还能兜底。反过来也一样。

在 TLS 1.3 里,混合密钥交换的实现方式是这样的:

  1. 客户端:在 key_share 扩展里同时放 X25519 和 Kyber-512 的公钥。
  2. 服务端:在 key_share 扩展里同时放对应的公钥。
  3. 密钥合并:双方各自计算两个共享密钥,然后用 HKDF-Extract 把它们合并成一个 "共享秘密"。

代码示例(伪代码):

// 客户端
ss_trad = ECDHE(X25519, client_priv_trad, server_pub_trad)
ss_pqc = Kyber.Decaps(client_sk_pqc, server_ct_pqc)
ss_combined = HKDF-Extract(salt=0, ikm=ss_trad || ss_pqc)

// 服务端
ss_trad = ECDHE(X25519, server_priv_trad, client_pub_trad)
ss_pqc = Kyber.Encaps(server_pk_pqc)  // 得到共享密钥和密文
ss_combined = HKDF-Extract(salt=0, ikm=ss_trad || ss_pqc)

这里要注意:合并的顺序必须一致。我曾经见过一个实现,客户端先拼传统再拼 PQC,服务端先拼 PQC 再拼传统,结果握手失败。排查了半天才发现是顺序问题。

避坑指南:混合密钥的合并顺序要在规范里写死。我建议统一为 "传统 || PQC",这样调试起来方便。

4.4 TLS扩展字段的PQC适配

TLS 1.3 的扩展字段是 PQC 迁移的主战场。我们需要适配以下几个关键扩展:

  • key_share:携带密钥交换参数。PQC 的公钥和密文都放在这里。
  • supported_groups:声明支持的密钥交换算法组。比如 "X25519Kyber512"。
  • signature_algorithms:声明支持的签名算法。PQC 签名算法(如 Dilithium)也在这里。
  • post_handshake_auth:用于后握手认证,PQC 证书可能会用到。

我画了一张图,帮你理解这些扩展在握手流程中的位置:

TLS 1.3 PQC扩展适配流程图 TLS 1.3 PQC扩展适配流程 客户端 服务端 ClientHello key_share, supported_groups ServerHello key_share, signature_algorithms 密钥派生 HKDF-Extract(传统 || PQC) 扩展字段说明 • key_share: 携带X25519 + Kyber-512公钥/密文 • supported_groups: 声明支持的混合组,如 X25519Kyber512 • signature_algorithms: 声明PQC签名算法,如 Dilithium3 • post_handshake_auth: 用于PQC证书的后握手认证

从图里可以看到,key_share 和 supported_groups 是 PQC 适配的核心。我个人习惯在 supported_groups 里用 "X25519Kyber512" 这种命名方式,一目了然。

小技巧:在 key_share 扩展里,PQC 的公钥和密文可以用 opaque 类型直接传输。TLS 1.3 的扩展字段支持任意二进制数据,这给 PQC 留了很大的灵活性。

嗯,关于 TLS 1.3 的 PQC 扩展,核心就是这些。记住:不改套件,只改扩展。这样既能兼容现有系统,又能平滑引入 PQC。