返回
2026/10/23
2026 年 10 月 20 日,一则消息被公开:印度 CA eMudhra 运营的八个中间证书颁发机构(CA)的私钥可以被任何人恢复出来。浏览器在问题被报告的当天就拦截了这些证书。然而,许多 Java 应用程序至今仍信任它们,而且在有人更新 JDK 或修改其信任库之前,会一直信任下去。
2026 年 10 月 21 日,BellSoft 发布了测试结果,表明许多 Java 运行时仍接受这些证书。本文总结了他们的发现(我们已在自己的测试中验证),并解释为什么浏览器不受影响而 Java 会受影响、如何检查您的系统,以及应该如何应对。
要点
- eMudhra 的八个中间 CA 私钥已泄露。任何持有其中一个私钥的人都可以为任意主机名签发证书,而信任该证书链的客户端会接受它。
- 浏览器已拦截这些证书,但 Java 使用自己的信任库,并且默认既不检查吊销状态,也不检查证书透明度(Certificate Transparency)。
- 多家供应商的 JDK 构建仍信任部分或全部受影响的证书链,容器镜像中的 JDK 也不例外。
- 在供应商发布修复版 JDK 之前,请在测试后从信任库中删除这两个受影响的根证书。启用吊销检查是第二种方案。
发生了什么
eMudhra 在生成其八个中间 CA 证书的私钥时出现错误,导致任何人都能从公开的证书中恢复出这些私钥。自这些中间证书于 2025 年 4 月签发以来,这种情况就一直存在。
2026 年 10 月 17 日,一名报告者用这八个私钥分别对消息“We have a problem here”进行签名,并将签名发送给各浏览器根证书计划,以此证明了该问题。详细信息于 10 月 20 日公开。
- 六个中间证书用于签发 TLS 证书:即 eMudhra 的 DV、OV 和 EV TLS CA,分为 G3B 和 G3C 两代。
- 两个中间证书用于签发代码签名证书。
- 每个中间证书都有两个版本:一个由 eMudhra 的 emSign 根证书签名,另一个由 Sectigo 的
AAA Certificate Services根证书交叉签名。在 JDK 信任库中,这些证书链终止于以下两个根证书之一:emSign ECC Root CA - G3(通过交叉签名)或AAA Certificate Services。
Firefox 和 Chrome 自 10 月 17 日起拦截这些中间证书,Apple 自 10 月 19 日起,Microsoft 自 10 月 20 日起。eMudhra 已于 10 月 18 日和 19 日以 CA_COMPROMISE 为理由吊销了全部八个中间证书。各 Linux 发行版也已发布更新的 CA 证书软件包。
有何影响
任何持有其中一个私钥的人都可以为任意主机名创建证书,例如您的银行、您的云服务提供商或您自己的 API 网关。信任该证书链的客户端会接受这样的证书。如果再结合某种拦截流量的手段,例如被入侵的网络或被篡改的 DNS 记录,攻击者就可以:
- 读取并篡改 Java 应用程序与其所调用服务之间的流量,包括密码、API 密钥和令牌;
- 冒充软件包仓库、更新服务器或合作伙伴的 API;
- 利用那两个代码签名中间证书对代码进行签名,使其看起来来自受信任的发布者。
这些中间证书从未出现在证书透明度(Certificate Transparency,CT)日志中,因此强制执行 CT 的浏览器不可能通过它们受到攻击。目前尚不清楚这些私钥是否曾被用于攻击不检查 CT 的客户端,而 Java 正是这类客户端之一。
为什么 Java 仍然信任这些证书
Mozilla、Google、Apple 和 Microsoft 通过各自的渠道(如 OneCRL、CRLite 和 CRLSets)推送了拦截措施,而 JDK 默认的 TLS 实现不会读取其中任何一个。以下三项默认设置使 Java 仍然面临风险:
- 自己的信任库。除非应用程序设置了
javax.net.ssl.trustStore,否则 Java 会信任$JAVA_HOME/lib/security/jssecacerts(如果该文件存在)中的根证书,该文件不存在时则信任$JAVA_HOME/lib/security/cacerts中的根证书。供应商捆绑的cacerts既不会收到浏览器的拦截列表,也不会收到操作系统的 CA 更新。只有在管理员编辑它,或 JDK 更新将其替换时,它才会发生变化。 - 不检查吊销状态。除非您主动启用,否则 Java 不会检查吊销状态(
com.sun.net.ssl.checkRevocation默认为 false),因此在默认配置下,CA 的吊销操作完全不可见。 - 不检查证书透明度。JDK 不强制执行 CT,因此保护了浏览器用户的那层机制在这里并不适用。
披露页面列出了十二个测试主机,对应每一种中间证书与根证书的组合。BellSoft 于 10 月 21 日在 Linux 上对它们进行了测试,我们于 10 月 23 日重复了该测试:
| 测试的运行时 | 受信任的测试主机 |
|---|---|
| Liberica JDK 21.0.10 | 12 个中有 12 个 |
| Liberica JDK 8u504(容器镜像) | 12 个中有 12 个 |
| Liberica JDK 21.0.12.1(容器镜像) | 12 个中有 12 个 |
| Eclipse Temurin 25.0.4.1 | 12 个中有 6 个(仅 eMudhra 根证书) |
| Eclipse Temurin 21.0.12.1(容器镜像,我们的测试) | 12 个中有 6 个(仅 eMudhra 根证书) |
| Arch Linux OpenJDK 27(系统 CA 存储) | 12 个中有 6 个(仅 eMudhra 根证书) |
区别在于 Sectigo 根证书。Mozilla 此前已移除 AAA Certificate Services,而 Temurin 和 Arch Linux 的信任库基于 Mozilla 列表构建,因此只有 eMudhra 这条路径是开放的。Liberica JDK 的信任库仍包含该根证书,因此两条路径都是开放的。
需要检查哪些系统
- 独立 JDK:从供应商归档文件解压,或使用供应商安装程序安装的 JDK。它们使用自己的
cacerts。 - 附带 JDK 的容器镜像。在 BellSoft 测试的镜像中,更新操作系统的
ca-certificates软件包并不会改变 JDK 的cacerts。拉取更新的基础镜像也无济于事:经测试,10 月 21 日的 Liberica 镜像仍信任全部十二个测试主机。 - 使用自有信任库的应用程序,即通过
javax.net.ssl.trustStore、在应用服务器配置中或在应用程序内部设置的信任库。请检查其中是否包含这两个根证书。 - 发行版打包的 OpenJDK。检查
cacerts指向何处。如果它链接到系统 CA 存储(在 Arch Linux 上,它是指向/etc/ca-certificates/extracted/java-cacerts.jks的符号链接),那么发行版的ca-certificates更新即可修复该问题。披露页面列出了已修补的软件包版本。
如何检查您的 JDK
首先,在信任库中查找这两个根证书。不同供应商使用的别名各不相同(Liberica JDK 使用 emsigneccrootcag3 [jdk],Temurin 使用 cn_emsign_ecc_root_ca__g3,...),因此请按证书主题(subject)搜索。此外还有其他几个 emSign 根证书,但只有 emSign ECC Root CA - G3 受影响。在 JDK 9 及更高版本上:
keytool -list -v -cacerts -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'
Java 8 的 keytool 没有 -cacerts 选项,而且其信任库位于 jre/ 下:
keytool -list -v -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit | grep -E '^Alias name:|^Owner:' | grep -B1 -E '^Owner: CN=(emSign ECC Root CA - G3|AAA Certificate Services),'
每个匹配项都会先打印别名,再打印证书主题。请记下这些别名,修复时需要用到。
然后测试真实连接。下面这个小程序会连接全部十二个测试主机,可在 Java 8 及之后的所有版本上运行:
import java.net.URI;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
public class Check {
public static void main(String[] args) {
for (String level : new String[]{"dv", "ov", "ev"})
for (String gen : new String[]{"g3b", "g3c"})
for (String via : new String[]{"", "-aaa"}) {
String host = level + "-" + gen + via + "-emudhra.we-have-a-problem.com";
try {
HttpsURLConnection c = (HttpsURLConnection) URI.create("https://" + host).toURL().openConnection();
c.setConnectTimeout(10000);
c.setReadTimeout(10000);
c.connect();
c.disconnect();
System.out.println("TRUSTED " + host);
} catch (SSLHandshakeException e) {
System.out.println("TLS_REJECTED " + host);
} catch (Exception e) {
System.out.println("ERROR " + host + " (" + e + ")");
}
}
}
}
在 JDK 11 或更高版本上,使用 java Check.java 运行。在 Java 8 上,或者在只安装了 JRE 的环境中,请先用 javac Check.java 编译(如有需要,可在装有 JDK 的机器上编译),然后运行 java Check。出现 TRUSTED 行表示该 JDK 接受了已泄露的中间证书。ERROR 表示无法访问该主机,这既不能证明存在问题,也不能证明没有问题。请使用与您的应用程序相同的 JDK 和信任库设置运行检查,包括在您的容器内部。
如何修复
1. 删除这两个根证书(在修复版 JDK 发布之前推荐使用)
这两个根证书正是披露页面建议 Linux 和 macOS 用户取消信任的那两个。请以拥有该 JDK 的用户身份,使用检查时找到的别名运行命令。例如,在 Liberica JDK 上:
keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
在 Java 8 上,请将 -cacerts 替换为 -keystore "$JAVA_HOME/jre/lib/security/cacerts"。如果您的信任库只包含这两个根证书中的一个(例如 Temurin 的信任库),则只删除那一个。
如果您无法修改 JDK 安装,可以编辑一份副本,并让 JVM 使用它:
cp "$JAVA_HOME/lib/security/cacerts" ./trimmed-cacerts
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "comodoaaaca [jdk]"
keytool -delete -keystore ./trimmed-cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
java -Djavax.net.ssl.trustStore=./trimmed-cacerts -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar
在我们的测试中,这两种方法都使 Liberica JDK 8 和 21 拒绝了全部十二个测试主机,而 sectigo.com 和 github.com 等普通网站仍可正常连接。
对于容器镜像,请在构建时删除这些根证书,这样每个新镜像都已得到修复:
FROM bellsoft/liberica-runtime-container:jdk-21-musl
RUN keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]" \
&& keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
Distroless 镜像和非 root 镜像需要做两处修改:它们没有 shell,因此请使用 RUN 的 exec 形式,每行一条命令;它们以非特权用户身份运行,因此在编辑时请切换到 USER root,完成后再切换回镜像原本的用户。
提示:删除根证书的影响范围比拦截八个中间证书更广。如果某个合法服务器的证书链只终止于这两个根证书之一,它将无法再正常工作。在推行此修改之前,请测试您的应用程序所调用的服务;并在每次 JDK 更新后再次运行检查,因为更新或重新安装可能会恢复原始的 cacerts。
2. 启用吊销检查(可选)
eMudhra 已吊销全部八个中间证书,因此启用了吊销检查的 JVM 无需修改信任库就会拒绝它们。请启用 CRL 分发点和 OCSP:
echo 'ocsp.enable=true' > revocation.security
java -Dcom.sun.net.ssl.checkRevocation=true \
-Dcom.sun.security.enableCRLDP=true \
-Djava.security.properties=revocation.security \
-jar app.jar
在我们的测试中,这一设置使 Liberica JDK 8 和 21 以及 Temurin 21 拒绝了全部十二个测试主机。不过,它会改变所有证书的验证方式。JVM 会在路径验证期间进行 OCSP 或 CRL 查询,当无法确定吊销状态时,握手可能会失败。在全局启用之前,请测试您的服务器能否访问这些响应服务器、它们会增加多少延迟,以及查询失败时的表现。如果您已经在启用吊销检查的情况下运行,或者将其作为删除根证书的补充措施,可以使用此方法。
3. 修复版构建发布后更新 JDK
BellSoft 表示正在准备删除这些根证书的 Liberica JDK 更新。请关注您所用 JDK 供应商的安全公告。更新发布后,请阅读其发行说明,确认其涵盖了 eMudhra 证书,然后安装更新、重新构建容器镜像并再次运行检查。仅凭更高的版本号并不能证明问题已修复。
修复之后:评估您的暴露情况
这些私钥在大约十八个月内都可以被恢复,因此修复只是关上了门,却无法告诉您是否有人曾经利用过它。对于会发送凭据或敏感数据的系统,请在 TLS 检查、代理和终端日志中搜索由这八个 eMudhra 中间证书签发的证书。如果发现任何可疑情况,请轮换受影响的凭据。
从长远来看,请像对待依赖项一样对待信任库:清楚每个信任库位于何处、由谁负责更新,以及更改多快能到达生产环境。这次事件表明,当您的服务器运行着自己的一份信任列表副本时,仅靠浏览器更新是不够的。
需要帮助?LOGIQ 为 OpenJDK 和 Linux 提供支持服务,包括安全补丁。如果您需要帮助找出环境中的每一个 JDK 和信任库,或安全地推行修复,请联系我们。
Global
Japan
Korea
Taiwan
Vietnam
Thailand
Indonesia
Portugal
Spain
France
Germany
Egypt
Russia