如今每一次 TLS 1.3 握手都要在 X25519MLKEM768 与 X25519 之间做出密钥交换上的选择。单独使用 X25519 速度快、数据量极小,但无法抵御量子计算机,这意味着现在被记录下来的流量将来可能被解密。X25519MLKEM768 同时运行 X25519 和 ML-KEM-768,因此只要两者没有同时被攻破,会话的机密性就能得到保证。
概览
| X25519 | X25519MLKEM768 | |
|---|---|---|
| TLS 支持的组 | x25519,29(0x001D) | X25519MLKEM768,4588(0x11EC),IANA 推荐(Recommended) |
| 规范 | RFC 7748、RFC 9846 | RFC 10024(2026年),框架为 RFC 9954 |
| 组成部分 | X25519 | ML-KEM-768(FIPS 203)和 X25519 |
| 客户端密钥共享(key share) | 32 字节 | 1,216 字节(1,184 字节的 ML-KEM 封装密钥 + 32) |
| 服务器密钥共享 | 32 字节 | 1,120 字节(1,088 字节的 ML-KEM 密文 + 32) |
| 送入密钥调度的共享密钥 | 32 字节 | 64 字节(ML-KEM 共享密钥在前,X25519 共享密钥在后) |
| 抗量子能力 | 否 | 是,前提是 ML-KEM-768 未被攻破 |
| 经典安全性 | X25519 | 至少与 X25519 相当,即使 ML-KEM 被攻破也是如此 |
| 每次握手的 CPU 开销(OpenSSL 3.6.5) | 每秒约 52,000 次 X25519 运算 | 每秒约 19,000~24,000 次密钥生成、封装或解封装 |
| 下文实测的 ClientHello | 306 字节 | 1,484 字节 |
| 下文实测的 ServerHello | 122 字节 | 1,210 字节 |
吞吐量在 Apple M4 Max 的单个核心上用 openssl speed 测得;握手消息大小取自下文的抓包结果。
为什么现在就要用混合方案
密钥交换面临“先收集后解密”攻击:攻击者现在记录加密的会话,等到密码学相关的量子计算机出现后,再从记录下来的 X25519 交换中恢复会话密钥,解密这些会话。前向保密对此无能为力,因为每一次被记录的交换都可以单独破解。凡是机密性必须维持到这种计算机出现之后的数据,现在就已经面临风险;这正是密钥交换率先迁移、而签名可以再等一段时间的原因。
混合设计也能对冲另一个方向的风险。ML-KEM 于2024年8月完成标准化,与 X25519 相比还很年轻,因此出现数学上或实现上的破解并非不可想象。X25519MLKEM768 把两个共享密钥拼接后送入 TLS 密钥调度,所以攻击者必须同时攻破两者。对于这个组,RFC 10024 把 ML-KEM 的密钥共享和共享密钥放在前面;基于 NIST 曲线的混合组(SecP256r1MLKEM768,0x11EB;SecP384r1MLKEM1024,0x11ED)则不同,它们把 ECDH 放在前面。
网络传输上的变化
客户端发送的密钥共享是 1,216 字节,而不是 32 字节,因此典型的 ClientHello 会从约 300 字节增长到约 1.5 KB,加上浏览器的所有其他扩展后,已无法装进一个网络数据包。服务器则回复 1,120 字节的密钥共享。下面用 OpenSSL 3.6.5 抓取了与 Cloudflare 的实时握手,可以看出其中的差别:
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519MLKEM768 -brief 2>&1 | grep -E "Protocol|group"
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519MLKEM768 -msg 2>&1 | grep -E "ClientHello|ServerHello"
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519 -msg 2>&1 | grep -E "ClientHello|ServerHello"
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768
>>> TLS 1.3, Handshake [length 05cc], ClientHello
<<< TLS 1.3, Handshake [length 04ba], ServerHello
>>> TLS 1.3, Handshake [length 0132], ClientHello
<<< TLS 1.3, Handshake [length 007a], ServerHello
也就是说,ClientHello 是 1,484 字节,而使用 X25519 时为 306 字节;ServerHello 是 1,210 字节,而使用 X25519 时为 122 字节。更大的 ClientHello 暴露了服务器和中间设备中的缺陷:它们假定这条消息会在一次 TCP 读取或一个数据包内完整到达。Chrome 在 124 版(2024年)中默认启用混合 Kyber 密钥共享时,多款防火墙和代理产品不得不为此修复,这类问题记录在 tldr.fail 上。据 Cloudflare 和 Google 报告,多出的字节使握手延迟的中位数增加了百分之几,这远不及 TLS 1.3 本身省下的往返次数重要。
部署现状
- 浏览器:Chrome 和 Edge 自 131 版(2024年11月)起默认提供 X25519MLKEM768,桌面版 Firefox 自 132 版起提供,Safari 和 Apple 的系统 TLS 自 iOS 26 和 macOS 26(2025年)起提供。它们此前使用的预标准
X25519Kyber768Draft00(0x6399)已被弃用。 - 库:OpenSSL 3.5(2025年4月)默认发送 X25519MLKEM768 和 X25519 两个密钥共享;Go 1.24 在
crypto/tls中默认启用它;JDK 27(2026年9月,JEP 527)默认将其列为首选;BoringSSL、rustls 和 NSS 都支持它。 - 服务器和 CDN:Cloudflare、Google 等大型服务商会与支持它的客户端协商使用它;nginx、Apache 和 HAProxy 则通过所链接的 OpenSSL 获得支持。
- SSH:对应的
mlkem768x25519-sha256自 OpenSSH 10.0 起成为默认算法(RFC 10042)。
用浏览器后量子检测可以查看你自己的浏览器实际协商的结果。
如何选择
- 公开的 HTTPS 和 API:首选 X25519MLKEM768,其次是 X25519;所有现代客户端都会协商使用混合方案,旧客户端则会回退。
- 内部服务网格和 mTLS:默认使用混合方案,需要长期保持机密性的数据尤其如此。
- 面向 FIPS 的部署:如果你所用模块的批准清单要求 NIST 曲线,就选
SecP256r1MLKEM768或SecP384r1MLKEM1024;在 ML-KEM 是获批组件的场合,X25519MLKEM768 也符合要求。 - 每个数据包都很宝贵的受限链路:先测量;混合方案会让每个方向多出一个数据包,这通常可以接受。
- 单独使用 X25519:仅用于无法使用混合方案的对端,切勿在新部署中主动选择它。
迁移说明
- 升级 TLS 库(OpenSSL 3.5 或更高版本、Go 1.24 或更高版本、最新的 BoringSSL 或 rustls);在 OpenSSL 3.5 中,默认的组已经包含混合方案。
- 如果你显式设置了组,就加上它:例如在 nginx 中写
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;,或在 OpenSSL 配置文件中写Groups = X25519MLKEM768:X25519:P-256。 - 测试整条路径,而不只是服务器:负载均衡器、WAF 和执行 TLS 检查的代理必须能处理分散在多个数据包和多次读取中的 ClientHello;它们升级之后要重新测试。
- 验证实际协商的结果,可以使用
openssl s_client -groups X25519MLKEM768或浏览器开发者工具,并监控混合握手所占的比例。 - 单独规划签名方面的迁移:混合方案只保护机密性;在 WebPKI 采用后量子签名之前,证书和握手签名仍然使用经典算法。