JDK 27 默认开后量子加密,但你和 JDK 17 握手时会静默退回旧算法

3 阅读12分钟

升级到 JDK 27,javax.net.ssl 的 TLS 1.3 握手默认就带后量子算法了。不用改一行代码,不用加一个参数。

然后你会发现,什么都看不出变化。

连接照常建立,日志照常干净,监控照常绿灯。只有在抓包看 key_share 扩展的时候,才知道这次握手到底用的是 X25519MLKEM768,还是悄悄退回了几年前的 X25519。

大部分情况下,是后者。

发生了什么:JDK 27 把混合密钥交换设成了默认首选

JDK 27 在 9 月 15 日 GA,build 27+35,一共九个 JEP,是 JDK 20 以来最少的一次。JEP 527 是其中之一:为 TLS 1.3 引入后量子混合密钥交换。

支持三套方案,都来自 RFC 10024(2026 年 8 月):

名称组合默认状态
X25519MLKEM768X25519 + ML-KEM-768默认启用,列表首位
SecP256r1MLKEM768secp256r1 + ML-KEM-768需显式指定
SecP384r1MLKEM1024secp384r1 + ML-KEM-1024需显式指定

这是三步里最后一步。Java 21 加了 KEM API(JEP 452),Java 24 加了 ML-KEM 实现(JEP 496),JDK 27 把它们接进 TLS。

Oracle 的发布说明写得很直白:「The X25519MLKEM768 group is enabled by default and is the most preferred group. This feature works out of the box.」默认启用、最优先、开箱即用。三个判断都成立,前提藏在后半句:只要双方都支持这个组,且它在对方列表里也排第一。

回移已经排上:Oracle JDK 25 在 10 月,之后是 21、17、11、8。这个顺序值得注意。11 和 8 还在大量生产系统里跑,说明 Oracle 认为这件事对所有版本都重要。

机制:两把钥匙同时协商,任一存活就算安全

先解释"混合"是什么意思。

普通 TLS 1.3 握手,客户端和服务端各出一把 X25519 的临时密钥,算出共享密钥。X25519 的安全性建立在椭圆曲线离散对数问题上,量子计算机用 Shor 算法能解。

混合模式不替换 X25519,是并行再算一份。

客户端在 key_share 扩展里同时放两个公钥:一个 X25519 的 32 字节,一个 ML-KEM-768 的约 1184 字节。服务端各回一份。两边把两个共享密钥拼起来,一起喂给密钥派生函数。

结果是一把"双重锁"的会话密钥:只要 X25519 或 ML-KEM 有一个没被攻破,密钥就安全。

这个设计是为过渡期准备的。ML-KEM 基于格问题,标准化才两年(FIPS 203 是 2024 年 8 月),没有经历过椭圆曲线那种几十年的密码分析。万一哪天格密码被发现结构性弱点,X25519 那一半还在。反过来,量子计算机真造出来了,ML-KEM 那一半挡住。

最坏情况等于现状,最好情况是提前免疫。工程上这笔买卖划算。

为什么对手不支持就静默降级

TLS 的算法协商靠"named group"列表。客户端按偏好顺序列一串,服务端从里面挑一个双方都有的。

这套实现在 27 里把 X25519MLKEM768 放在列表第一位,后面才是 x25519、NIST 曲线、x448、有限域 DH。

问题在于:混合组只有双方都支持才用得上。

服务端升到 27、客户端还是 17 的时候,客户端的列表里没有 X25519MLKEM768,服务端就选下一个能用的,也就是 x25519。握手成功,连接正常,没有任何错误。

标准行为,没有 bug。但对运维来说,"升级了"和"生效了"是两件事,而系统不会告诉你哪件发生了。

Oracle 自己的 JSSE 文档写得很明确:只有在对方也支持同一组时,混合组才会成为首选。文档写了,但没人会在升级时去翻这段话。

为什么打头的是 X25519,不是 P-256

三套方案里只有一套默认开,选的不是最强的那个。

X25519MLKEM768 配的是 X25519,一条 2016 年就在 Chrome 里默认启用的曲线,部署量最大。OpenJDK 的说法是"最快的混合选项,也是 TLS 客户端里最常见的默认值"。选它是因为互操作性,不是因为它最安全。

另外两套要显式指定:jdk.tls.namedGroups 系统属性,或者按连接调 SSLParameters.setNamedGroups()。

如果你的应用自己设过 named groups,要注意一件事:JDK 27 的默认值不会生效。这种配置会覆盖默认列表,升级之后行为不变,你以为开了其实没开。检查一下代码里有没有 setNamedGroups,或者启动参数里有没有 jdk.tls.namedGroups。

要保留混合组又保留经典回退,最简写是这样:

-Djdk.tls.namedGroups=X25519MLKEM768,x25519,secp256r1

顺序有意义。第一个是首选,后面是回退。

成本:多 2.3KB,多 1.5 毫秒

这套东西不是免费的。代价集中在握手阶段,不在稳态吞吐。

一份 2026 年的 SSRN 基准测试(300 次全新握手)给了这组数:

指标X25519 + ECDSAX25519MLKEM768 + ECDSA变化
握手中位延迟0.666 ms0.877 ms+31.6%
ClientHello 大小188 字节1364 字节约 7 倍
key_share 载荷32 字节约 1216 字节约 38 倍
单核最大连接数/秒65005200-20%

互联网尺度上的数字温和得多。多项部署统计给出的中位数增量在 1.5 毫秒左右,尾部 7 毫秒,连接成功率没有可测下降。原因是握手延迟主要由网络往返(RTT)决定,多出的那点计算量淹没在里面。

有个反直觉的点:ML-KEM 本身比 X25519 快。Cloudflare 的基准里,ML-KEM-768 的单核处理能力是客户端 29000 次/秒、服务端 45000 次/秒,而 X25519 两边都是 19000 次/秒。

所以瓶颈不在算力,在带宽。多出来的 2.3KB 要过网线,这才是成本。

对高频短连接的服务,这笔账会放大。每个连接都多 2.3KB,连接数上去了总量很可观。对长连接和 CDN 边缘,几乎无感。Cloudflare 到 2025 年底,入站 TLS 流量里已经有约 52% 是后量子保护的。

对照:密钥交换换了,证书没换

这是最容易被误读的一点,很多"全面后量子"的宣传都栽在这里。

JEP 527 只动了密钥交换。它保护的是"会话密钥怎么协商出来",防的是"今天录下流量,等量子计算机出来再解密"(harvest now, decrypt later)。

它没有动证书。服务端证书还是 RSA 或 ECDSA 签的,身份认证这一环仍然是量子可破的。

层是否后量子防什么
密钥交换(JEP 527)✅ 混合录流量后解密
证书签名❌ 仍是 RSA/ECDSA伪造身份
应用层数据加密沿用 AES-GCM/ChaCha20不变
TLS 1.2 及以下❌ 不覆盖—

所以准确的说法是"密钥交换后量子化",不是"TLS 后量子化"。真要全链路,还得等 ML-DSA(FIPS 204)的证书铺开。目前只有测试证书,浏览器侧还在实验阶段。

混合签名的标准化进度比密钥交换慢一档。IETF 的 composite signatures 草案还在推进,CA 只有 DigiCert 和 Entrust 发过测试证书。这也是为什么 JEP 527 先做密钥交换:这一层不需要 PKI 改动,改配置就能上。

对谁有影响

Java 后端团队。 最直接。升级 JDK 27 之后,服务间调用、对外 API、数据库连接(如果走 TLS),握手行为都可能变。要确认是否真生效,两个办法:

# 从外部看协商结果
openssl s_client -connect your-service:443 -groups X25519MLKEM768 -brief

# 从 JVM 内部看
-Djavax.net.debug=ssl:handshake
# 在 ServerHello 的 key_share 扩展里找 named group

如果打了 -groups X25519MLKEM768 但服务端回的是 x25519,说明对端不支持。原因大概率是旧版本 JDK、旧版 OpenSSL,或者中间有设备在改写。

运维和网络团队。 ClientHello 从 188 字节涨到 1364 字节,加上其他扩展,很可能超过单个 TCP 包的 1400 字节 MTU,触发分片。

大多数栈能处理。但一些老的负载均衡、深度包检测设备、TLS 中间盒,默认假设 ClientHello 一个包能装完。装不下就丢包或者截断。症状很有辨识度:只在客户端提供混合组时握手失败,把混合组去掉就正常。

这类问题不会在直连测试里出现,只在完整生产链路上出现。上线前必须在真实路径上测。

合规和安全团队。 "harvest now, decrypt later"针对的是需要长期保密的数据:医疗记录、身份信息、合同。今天录走的流量,十年后可能被解开。金融、政务、医疗这几个行业已经在把后量子迁移写进规划。JDK 27 让 Java 侧的第一步变得几乎零成本,这是好事,但别把它当成"做完了"。

迁移顺序建议这么排,每一步都能回退:

  1. 先在一台 staging 上开混合组,确认能协商成功
  2. 走完整生产链路再测一遍,专门抓中间盒和 MTU 问题
  3. 低风险服务先上,保留经典回退
  4. 在 TLS 遥测里记录实际协商的组,算出真实覆盖率
  5. 覆盖率达到预期后,再推到对外和高价值服务
  6. 盯住客户端库版本,覆盖率是动态的

第 4 步最容易被跳过,也最关键。没有"实际协商组"这个指标,你没法回答"我们现在有多少流量是后量子保护的",只能回答"我们升级了 JDK"。这两个答案的区别,决定了安全报告能不能写实话。

边界:几件必须说清楚的事

第一,基准测试的结论不一致。 SSRN 那份测出握手中位延迟 +31.6%,另一份 arXiv 的研究说密钥交换算法对握手延迟的影响"可忽略"。两者不矛盾,测试环境、硬件、是否走 loopback 都不同。别把任何一个数字当成自己系统的预期值,要测自己的。

第二,回移排期是"计划",不是承诺。 Oracle JDK 25 的 10 月回移是明确说的,21/17/11/8 用的是"announced plans"。具体版本号和时间点要等发布说明。

第三,JDK 27 不是唯一的门槛。 混合握手需要双方都支持。服务端升到 27,客户端还在 17,等于没升。真正的迁移工作是清点对端:内部服务、第三方 API、SDK、移动端。这件事没法靠改一个 flag 完成。

第四,命名有过变动。 早期草案里这个组的标识和码点跟最终的 X25519MLKEM768 不一样。客户端和服务端可能"都支持 ML-KEM",但因为码点不同协商不上。跨库版本混用(OpenSSL 3.4 与 3.5、不同 BoringSSL 构建)时要实测互通,别假设兼容。

第五,它对 TLS 1.2 无效。 大量存量系统还在 TLS 1.2。JEP 527 只覆盖 TLS 1.3,走 javax.net.ssl 之外的网络栈(比如某些 native 库)也不在范围内。

接下来盯什么

三个可以验证的信号。

一,跑一条命令看自己的服务。 openssl s_client -connect <host>:443 -groups X25519MLKEM768,看返回的 Server Temp Key。这是唯一不需要猜的方式。建议现在就跑一遍,作为基线。不然后面改了什么,你没法判断有没有效果。

二,JDK 21 和 17 的回移什么时候落地。 这两个版本在企业里的装机量远大于 27。回移一发,混合握手的覆盖率会跳一个台阶。看 Oracle 的发布说明里 JEP 527 出现在哪个 update 版本。

三,证书侧什么时候能跟上。 密钥交换是这几年的活,证书签名是下一个五年。关注 CA/B 论坛对 ML-DSA 证书的讨论,以及 Chrome、Firefox 什么时候默认信任后量子根。这一步动了,"全链路后量子"才有意义。

在那之前,最实际的判断标准只有一条:你的连接日志里,能看出这次握手用的是什么组吗? 看不出来,就等于没部署。

顺带说一个容易踩的坑。JDK 27 的回移会覆盖到 11 和 8,但那是 Oracle 的 JDK。用 OpenJDK 发行版(Temurin、Corretto、Zulu)的团队,要看各自厂商的跟进节奏,不是 Oracle 发版就等于你能用。这一条在打补丁季尤其明显:同一个 JEP,不同发行版落地能差几个季度。

来源