eMudhra CA 私钥安全事件:Java 运行环境(JRE)风险评估与修复指南

Published On: 2026年10月22日Categories: 帮助文档, 常见问题和回答Views: 10

© All rights reserved. • 西安知行软件有限公司 • 陕ICP备09022277号

适用对象:运行 Java 应用(使用 JRE/JDK 建立 HTTPS/TLS 连接)的系统管理员与运维人员
推荐方案:从 JRE 证书库 cacerts 中删除受影响的根证书。我们提供 Windows 与 Linux/Docker 命令行脚本,见第 4 节。

摘要

  • 发生了什么: eMudhra 的 8 个中间 CA 私钥因随机数缺陷可被轻易算出,任何人都能用它们伪造任意网站的 HTTPS 证书。相关证书已被吊销,Chrome、Firefox 和操作系统已完成拦截。
  • 为什么 Java 应用仍受影响: Java 使用自己的证书库(JRE 中的 cacerts),不使用操作系统证书库,并且默认不检查证书吊销。我们实测,未修复的 JRE 仍会接受这些伪造链路上的证书。
  • 风险程度: 攻击者需要能拦截 Java 应用的网络流量(如同一内网、DNS 劫持、恶意代理)。目前未见实际利用的报告,整体风险较低,但建议尽快处理。
  • 您需要做什么:
    1. 找到 Java 应用实际使用的 JRE(4.2 节)。
    2. 运行我们提供的脚本,先检查再修复(Windows 见 4.3 节,Linux/Docker 见 4.4 节)。
    3. 重启 Java 应用,并按 4.6 节验证。
  • 请记住: 升级或重装 JRE 后,cacerts 会被覆盖,需要重新运行检查。如果您使用的软件自带 JRE,请关注软件供应商发布的修复版本。

1. 问题简介

2026 年 10 月 17 日,安全研究人员报告 eMudhra 的 8 个中间 CA(Intermediate CA)私钥可被轻易恢复。这些 P-384 椭圆曲线私钥的随机性严重不足,实际私钥整数均小于 256。8 个密钥中,6 个用于 TLS 证书签发,2 个用于代码签名。eMudhra / Sectigo 已吊销相关证书,Chrome 和 Firefox 也已部署针对性的信任阻断。

本事件涉及中间 CA,并非 eMudhra 根 CA 私钥泄露。虽然官方称受影响中间 CA 没有签发最终实体证书,但这不等于尚未更新的 TLS 客户端没有被伪造证书攻击的可能。

2. 影响范围和安全风险

2.1 可能受影响的运行环境

  • Java 服务使用默认 JRE/JDK cacerts 或自定义 TrustStore,且其信任锚可建立通往上述受影响中间 CA 的证书链。
  • 运行的 Java TLS 客户端未可靠验证吊销状态(包括完全未检查 CRL/OCSP,或吊销服务不可达时允许继续连接)。
  • 使用 JSSE / HTTPS / HTTP 客户端 / TLS Socket 的出站连接;也可能涉及 Java 应用充当 TLS 服务端并使用客户端证书认证的情形(取决于其信任链验证方式)。
  • 使用独立证书库和验证器的组件(如原生 OpenSSL、嵌入式运行时或第三方 TLS Provider)需要分别检查,不应假定修改 Java cacerts 就能覆盖。

已知可能涉及的信任路径如下。Mozilla 公告列出了 3 个 TLS 根,另有 1 个根只涉及代码签名。指纹见附录表 C。

  1. emSign Root TLS CA - G3:6 个 TLS 中间 CA 的直接签发者。
  2. emSign ECC Root CA - G3:可经 emSign Root TLS CA - G3 等下级链,连到受影响的中间 CA。
  3. AAA Certificate Services:Sectigo 对全部 8 个中间 CA 做了交叉签名。
  4. emSign Root CS CA - G3:2 个代码签名中间 CA 的签发者,仅影响代码签名。

重要: 只删除其中一个根是不够的,只要还信任其他任一路径,风险就仍然存在。例如,已从 cacerts 中删除 emSign ECC Root CA - G3,但仍信任 AAA Certificate Services,那么经 Sectigo 交叉签名的受影响中间 CA 证书链依然有效。所以修复时必须把这些根一起删除(第 4 节脚本会自动处理)。但不要删除所有 Comodo/Sectigo 根 CA,那样会影响正常 HTTPS 连接,只需删除 AAA Certificate Services 这一个。

2.2 安全风险

  • TLS 服务器身份冒充: 攻击者能够为任意域名构造被某些未修复客户端接受的伪造证书。
  • 中间人攻击(MITM): 如攻击者还能控制 DNS、代理或网络流量,可能读取或修改 TLS 通信。
  • 代码签名信任风险: 两个受影响的代码签名 CA 对相关代码签名验证场景也有潜在影响;普通 HTTPS 客户端的主要关注对象是 6 个 TLS 中间 CA。

私钥可恢复不等于已经发生实际入侵;是否成功攻击仍取决于证书用途、具体信任路径、证书透明度、吊销检查和网络条件。

3. 安全风险自测方法

以下方法用于初步排查。TrustStore 中存在某个根证书并不单独证明可以遭受攻击;TrustStore 中不存在它,也不证明其他信任路径或 TLS 实现已经安全。

3.1 检查 JDK 版本及实际使用的 TrustStore

java -version
keytool -list -cacerts -storepass changeit

确保使用的是应用实际使用的 JRE 中的 java 和 keytool(查找方法见 4.2 节),并核对是否通过 -Djavax.net.ssl.trustStore=... 设置了自定义库(或应用代码自行加载了其他 TrustStore)。多个 JDK/JRE、容器镜像和应用自带运行时应逐一排查。

3.2 检查常见相关根 CA

Linux / macOS:

keytool -list -cacerts -storepass changeit | grep -Ei 'emsign|emudhra|aaa_certificate_services'

Windows PowerShell:

keytool -list -cacerts -storepass changeit | Select-String 'emsign|emudhra|aaa_certificate_services'

重点观察 emSign ECC Root CA - G3 和 AAA Certificate Services。注意:名称匹配仅用于粗略排查,更准确的方法是直接运行第 4 节脚本的检查模式,它按证书指纹匹配,不会修改任何文件。

3.3 用测试站点确认实际连接行为(可选)

第三方安全研究人员提供了两个测试站点,它们使用的正是受影响中间 CA 签发的证书:

  • https://dv-g3b-emudhra.we-have-a-problem.com/:eMudhra 原签发路径。
  • https://dv-g3b-aaa-emudhra.we-have-a-problem.com/:Sectigo(AAA Certificate Services)交叉签名路径。

如果应用支持配置出站 HTTPS 连接(如 HTTP/REST 连接、Webhook、连接测试按钮),可以临时配置一个指向上述地址的连接,再观察结果:

结果 含义
连接成功 存在风险:该 JRE 仍信任受影响的证书链,需要按第 4 节修复
报证书错误,如 PKIX path building failed、unable to find valid certification path to requested target 该 JRE 已不信任受影响的证书链
超时或无法解析域名 网络不通(如需代理或防火墙限制),无法据此判断

注意:

  • 这些证书已被吊销,但 Java 默认不检查吊销状态,因此未修复的 JRE 通常仍会连接成功。我们在 Temurin 17.0.5 上实测:修复前两个地址都能正常访问,执行第 4 节脚本后都报 PKIX path building failed。
  • 浏览器、curl 和 Windows 自带工具会使用操作系统的证书库并检查吊销,通常已经拒绝这两个地址。它们的结果不能代表 Java 应用的状态。
  • 两个站点都要测试,只有两个都失败才说明两条信任路径均已阻断。
  • 站点由第三方提供,可能随时下线。无法访问时,以第 4 节脚本检查模式的结果为准。
  • 测试完成后请删除临时添加的连接配置。

4. 安全修复

4.1 修复思路

Java 验证 HTTPS 证书时,只信任 JRE 证书库 cacerts(或应用另行指定的 TrustStore)中的根证书。受影响的 8 个中间 CA 并不在 cacerts 里:它们由服务器在握手时发送,Java 再沿着证书链追溯到 cacerts 中的根。因此,最直接的修复方法是:从 cacerts 中删除能为这些中间 CA 担保的根证书。这样无论攻击者走 eMudhra 原签发路径,还是走 Sectigo 交叉签名路径,证书链都无法建立。

可任选以下一种方式:

  1. 升级软件:如果软件供应商已发布内置修复后 JRE 的新版本,直接升级。请使用完整安装包,确保内置 JRE 一并更新。升级后仍建议用 4.3 或 4.4 节脚本的检查模式确认。
  2. 运行修复脚本:使用 4.3 节(Windows)或 4.4 节(Linux / Docker)的脚本,删除证书库中的受影响证书。

脚本按证书 SHA-256 指纹精确匹配,不按名称匹配(指纹见附录表 B、表 C),会删除以下证书:

  • 3 个 emSign 根:emSign ECC Root CA - G3、emSign Root TLS CA - G3、emSign Root CS CA - G3。
  • 1 个 Sectigo 旧根:AAA Certificate Services。
  • 证书库中如果手工导入过受影响的 16 张中间 CA 证书,也一并删除。

不同 JRE 版本内置的根不同。例如 Temurin 17.0.20 只含 emSign ECC Root CA - G3,而 17.0.5 和 8u332 还含 AAA Certificate Services。脚本只删除实际存在的条目,其他证书不受影响。

兼容性影响: 删除后,证书链只能追溯到上述 4 个根的网站将无法通过该 JRE 访问。emSign 根主要服务于 eMudhra 签发的证书。AAA Certificate Services 是 Sectigo(原 Comodo)的旧根,现在的 Sectigo 证书通常链接到 USERTrust 等其他根,一般不受影响。脚本不会删除其他 Sectigo/Comodo 根。如果业务需要连接使用 eMudhra 证书的服务器,请先在测试环境确认。

为什么不只使用 blocked.certs: JDK 的 lib/security/blocked.certs 只对 Java 自带的 TLS 实现(JSSE)生效。部分第三方 TLS 组件只读取 cacerts,会忽略它。此外它必须列全 16 个中间证书指纹(含交叉签名),且不是所有 JDK 发行版都支持手工修改。删除根证书对所有读取 cacerts 的组件都有效。

4.2 准备:确认实际使用的 JRE

一台服务器上可能装有多个 JRE(系统 Java、软件安装目录自带的 jre、容器镜像等),必须修复应用实际使用的那一个。最可靠的办法是查看正在运行的 Java 进程。

Windows(以管理员身份打开 PowerShell,否则看不到服务进程):

Get-Process | ForEach-Object {
  $jvm = $_.Modules | Where-Object ModuleName -eq 'jvm.dll' | Select-Object -First 1
  if ($jvm) { [pscustomobject]@{ PID = $_.Id; Name = $_.ProcessName; JavaHome = (Split-Path (Split-Path (Split-Path $jvm.FileName))) } }
} 2>$null | Format-Table -AutoSize

该命令也能找到以 Windows 服务方式内嵌运行、进程名不是 java.exe 的 Java 程序。输出中的 JavaHome 就是 4.3 节需要的路径。

Linux:

for p in $(pgrep -x java); do
  echo "PID $p  Java home: $(sudo readlink -f /proc/$p/exe | sed 's#/bin/java$##')"
done

另外请注意:

  • 如果启动参数或环境变量 JAVA_TOOL_OPTIONS 中带有 -Djavax.net.ssl.trustStore=<路径>,说明应用使用自定义证书库,需要用脚本的 -Keystore(Windows)或 KEYSTORE=(Linux)参数另外处理它。如果该证书库密码不是默认的 changeit,还需传入 -StorePass 或 STOREPASS=。
  • 软件安装目录下自带的 JRE 通常位于 <安装目录>\jre\ 下的某个子目录。
  • JDK 8 可直接传入 JDK 目录或其下的 jre 目录,脚本会自动找到 jre\lib\security\cacerts。
  • 修改证书库前,应用不必停止,但修改后必须重启才会生效。

4.3 Windows 修复脚本

  1. 将下面的脚本保存为 Remove-eMudhraCA.ps1。

  2. 以管理员身份打开 PowerShell,进入脚本所在目录。

  3. 先用检查模式查看,不做任何修改(将路径替换为 4.2 节找到的 JavaHome):

    powershell -ExecutionPolicy Bypass -File .\Remove-eMudhraCA.ps1 -JavaHome "C:\path\to\jre" -CheckOnly
    
  4. 确认后去掉 -CheckOnly 执行删除:

    powershell -ExecutionPolicy Bypass -File .\Remove-eMudhraCA.ps1 -JavaHome "C:\path\to\jre"
    
  5. 重启使用该 JRE 的所有 Java 应用或服务。

可选参数:-Keystore <路径>(自定义证书库)、-StorePass <密码>(默认 changeit)。不传 -JavaHome 时使用环境变量 JAVA_HOME。

param(
  [string]$JavaHome = $env:JAVA_HOME,
  [string]$Keystore,
  [string]$StorePass = 'changeit',
  [switch]$CheckOnly
)
$ErrorActionPreference = 'Continue'

# SHA-256 fingerprints: 4 roots + 8 eMudhra intermediates + 8 Sectigo (AAA) cross-signs
$Distrusted = @{
  '86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B' = 'emSign ECC Root CA - G3'
  'D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4' = 'AAA Certificate Services'
  '7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D' = 'emSign Root TLS CA - G3'
  'F2EDAAFF3D9B701FB4D14C9F285F8C0BAD90FC3BCC1FA07D47F4718EDABA34A5' = 'emSign Root CS CA - G3'
  'E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838' = 'EM EV TLS CA - G3B'
  '98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B' = 'EM OV TLS CA - G3B'
  '42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493' = 'EM DV TLS CA - G3B'
  '432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3' = 'EM EV TLS CA - G3C'
  'CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F' = 'EM OV TLS CA - G3C'
  '0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19' = 'EM DV TLS CA - G3C'
  '9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4' = 'EM OV CS CA - G3B'
  '4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1' = 'EM OV CS CA - G3C'
  '579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512' = 'EM EV TLS CA - G3B (AAA cross-sign)'
  '5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699' = 'EM OV TLS CA - G3B (AAA cross-sign)'
  '45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE' = 'EM DV TLS CA - G3B (AAA cross-sign)'
  'FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F' = 'EM EV TLS CA - G3C (AAA cross-sign)'
  '7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489' = 'EM OV TLS CA - G3C (AAA cross-sign)'
  '62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72' = 'EM DV TLS CA - G3C (AAA cross-sign)'
  'A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8' = 'EM OV CS CA - G3B (AAA cross-sign)'
  '84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD' = 'EM OV CS CA - G3C (AAA cross-sign)'
}

if (-not $JavaHome) { Write-Host 'ERROR: Java home not found. Pass it with -JavaHome.'; exit 1 }
$keytool = Join-Path $JavaHome 'bin\keytool.exe'
if (-not $Keystore) {
  $Keystore = Join-Path $JavaHome 'lib\security\cacerts'
  $jdk8 = Join-Path $JavaHome 'jre\lib\security\cacerts'
  if (-not (Test-Path $Keystore) -and (Test-Path $jdk8)) { $Keystore = $jdk8 }
}
if (-not (Test-Path $keytool))  { Write-Host "ERROR: keytool not found: $keytool"; exit 1 }
if (-not (Test-Path $Keystore)) { Write-Host "ERROR: keystore not found: $Keystore"; exit 1 }
Write-Host "Keystore: $Keystore"

function Get-DistrustedEntries {
  # Force English output so parsing works on Chinese Windows.
  $out = & $keytool '-J-Duser.language=en' '-J-Duser.country=US' -list -v -keystore $Keystore -storepass $StorePass 2>$null
  $total = 0; $alias = $null
  foreach ($line in $out) {
    if ($line -match '^Alias name: (.+)$') { $alias = $Matches[1]; $total++ }
    elseif ($alias -and $line -match '^\s*SHA256:\s*([0-9A-F:]+)') {
      $fp = $Matches[1] -replace ':', ''
      if ($Distrusted.ContainsKey($fp)) { [pscustomobject]@{ Alias = $alias; Name = $Distrusted[$fp] } }
      $alias = $null
    }
  }
  if ($total -eq 0) { throw 'Could not read any entry from the keystore (wrong password or path?).' }
}

try {
  $found = @(Get-DistrustedEntries)
  if ($found.Count -eq 0) { Write-Host 'OK: no affected certificate in this keystore.'; exit 0 }
  $found | ForEach-Object { Write-Host "Found: $($_.Name)  [alias: $($_.Alias)]" }
  if ($CheckOnly) { Write-Host "$($found.Count) affected certificate(s) found. Run again without -CheckOnly to remove."; exit 2 }

  $backup = "$Keystore.bak-$(Get-Date -Format yyyyMMddHHmmss)"
  Copy-Item $Keystore $backup -ErrorAction Stop
  Write-Host "Backup: $backup"
  foreach ($e in $found) {
    & $keytool '-J-Duser.language=en' -delete -keystore $Keystore -storepass $StorePass -alias $e.Alias 2>$null
    if ($LASTEXITCODE -ne 0) { throw "Failed to delete '$($e.Name)'. Run PowerShell as Administrator." }
    Write-Host "Removed: $($e.Name)"
  }
  $left = @(Get-DistrustedEntries)
  if ($left.Count -gt 0) { throw "Still present: $(($left | ForEach-Object Name) -join '; ')" }
  Write-Host 'OK: all affected certificates removed. Restart every Java application that uses this JRE.'
  exit 0
} catch {
  Write-Host "ERROR: $_"
  exit 1
}

4.4 Linux / Docker 修复脚本

  1. 将下面的脚本保存为 remove-emudhra-ca.sh。如果是在 Windows 上编辑后再复制到 Linux,请先转换换行符:sed -i 's/\r$//' remove-emudhra-ca.sh。

  2. 检查模式(只查看,不修改):

    sudo sh remove-emudhra-ca.sh /path/to/jre --check
    
  3. 执行删除:

    sudo sh remove-emudhra-ca.sh /path/to/jre
    
  4. 重启使用该 JRE 的所有 Java 应用或服务(例如 sudo systemctl restart <服务名>)。

自定义证书库或密码:sudo KEYSTORE=/path/to/truststore.jks STOREPASS=<密码> sh remove-emudhra-ca.sh /path/to/jre。

#!/bin/sh
# Usage: sh remove-emudhra-ca.sh <java-home> [--check]
# Optional env: KEYSTORE (default <java-home>/lib/security/cacerts), STOREPASS (default changeit)
set -u
if [ $# -lt 1 ]; then echo "usage: sh remove-emudhra-ca.sh <java-home> [--check]"; exit 1; fi
JAVA_DIR=$1
MODE=${2:-}
KEYTOOL="$JAVA_DIR/bin/keytool"
if [ -z "${KEYSTORE:-}" ]; then
  KEYSTORE="$JAVA_DIR/lib/security/cacerts"
  [ ! -f "$KEYSTORE" ] && [ -f "$JAVA_DIR/jre/lib/security/cacerts" ] && KEYSTORE="$JAVA_DIR/jre/lib/security/cacerts"
fi
STOREPASS=${STOREPASS:-changeit}

# SHA-256 fingerprints: 4 roots + 8 eMudhra intermediates + 8 Sectigo (AAA) cross-signs
DISTRUSTED='
86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B emSign ECC Root CA - G3
D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4 AAA Certificate Services
7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D emSign Root TLS CA - G3
F2EDAAFF3D9B701FB4D14C9F285F8C0BAD90FC3BCC1FA07D47F4718EDABA34A5 emSign Root CS CA - G3
E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838 EM EV TLS CA - G3B
98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B EM OV TLS CA - G3B
42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493 EM DV TLS CA - G3B
432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3 EM EV TLS CA - G3C
CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F EM OV TLS CA - G3C
0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19 EM DV TLS CA - G3C
9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4 EM OV CS CA - G3B
4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1 EM OV CS CA - G3C
579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512 EM EV TLS CA - G3B (AAA cross-sign)
5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699 EM OV TLS CA - G3B (AAA cross-sign)
45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE EM DV TLS CA - G3B (AAA cross-sign)
FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F EM EV TLS CA - G3C (AAA cross-sign)
7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489 EM OV TLS CA - G3C (AAA cross-sign)
62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72 EM DV TLS CA - G3C (AAA cross-sign)
A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8 EM OV CS CA - G3B (AAA cross-sign)
84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD EM OV CS CA - G3C (AAA cross-sign)
'

[ -x "$KEYTOOL" ] || { echo "ERROR: keytool not found: $KEYTOOL"; exit 1; }
[ -f "$KEYSTORE" ] || { echo "ERROR: keystore not found: $KEYSTORE"; exit 1; }
echo "Keystore: $KEYSTORE"

# Prints "<name>|<alias>" for each affected entry; exits non-zero if the keystore cannot be read.
find_distrusted() {
  "$KEYTOOL" -J-Duser.language=en -J-Duser.country=US -list -v -keystore "$KEYSTORE" -storepass "$STOREPASS" 2>/dev/null |
  awk -v list="$DISTRUSTED" '
    BEGIN { n = split(list, l, "\n"); for (i = 1; i <= n; i++) if (length(l[i]) > 65) bad[substr(l[i], 1, 64)] = substr(l[i], 66) }
    /^Alias name: / { alias = substr($0, 13); total++; next }
    alias != "" && /^[ \t]*SHA256:/ { fp = $2; gsub(":", "", fp); if (fp in bad) print bad[fp] "|" alias; alias = "" }
    END { if (total == 0) exit 2 }'
}

FOUND=$(find_distrusted) || { echo "ERROR: could not read the keystore (wrong password or path?)"; exit 1; }
if [ -z "$FOUND" ]; then echo "OK: no affected certificate in this keystore."; exit 0; fi
echo "$FOUND" | while IFS='|' read -r name alias; do echo "Found: $name  [alias: $alias]"; done
if [ "$MODE" = "--check" ]; then echo "Affected certificate(s) found. Run again without --check to remove."; exit 2; fi

BACKUP="$KEYSTORE.bak-$(date +%Y%m%d%H%M%S)"
cp -p "$KEYSTORE" "$BACKUP" || { echo "ERROR: backup failed (run as root?)"; exit 1; }
echo "Backup: $BACKUP"
echo "$FOUND" | while IFS='|' read -r name alias; do
  if "$KEYTOOL" -J-Duser.language=en -delete -keystore "$KEYSTORE" -storepass "$STOREPASS" -alias "$alias" >/dev/null 2>&1
  then echo "Removed: $name"; else echo "Failed to delete: $name (run as root?)"; fi
done

LEFT=$(find_distrusted) || { echo "ERROR: could not re-read the keystore"; exit 1; }
if [ -n "$LEFT" ]; then echo "ERROR: still present:"; echo "$LEFT"; exit 1; fi
echo "OK: all affected certificates removed. Restart every Java application that uses this JRE."

Docker 镜像: 修改正在运行的容器只是临时措施,容器重建后会恢复原状。请在 Dockerfile 中加入以下步骤并重新构建镜像(如果镜像以非 root 用户运行,请在此步骤前临时切换为 USER root):

COPY remove-emudhra-ca.sh /tmp/
RUN sh /tmp/remove-emudhra-ca.sh "$(dirname "$(dirname "$(readlink -f "$(command -v java)")")")" \
 && rm /tmp/remove-emudhra-ca.sh

4.5 脚本结果说明

退出码 含义 处理方式
0 证书库中已没有受影响证书(本次已删除,或原本就没有) 如有删除,重启相关 Java 应用
1 出错:找不到 JRE 或证书库、密码错误、权限不足等 按提示处理。权限问题请以管理员或 root 身份运行
2 仅检查模式:发现受影响证书,未做修改 去掉检查参数再运行一次

脚本删除前会自动备份为 cacerts.bak-<时间戳>,删除后会重新读取证书库,确认受影响证书已全部清除。脚本可重复运行。

4.6 修复后验证

  1. 再次运行检查模式,应输出 OK: no affected certificate in this keystore.,退出码为 0。
  2. 确认已重启使用该 JRE 的所有 Java 应用或服务。
  3. 确认应用常用的 HTTPS 连接(如合作方接口、云服务)仍然正常。
  4. (可选)按 3.3 节再测试一次两个测试站点,两个都应报证书错误。

4.7 回滚与注意事项

  • 回滚: 将备份文件复制回原位置并重启应用。
    • Windows:Copy-Item "<cacerts路径>.bak-<时间戳>" "<cacerts路径>" -Force
    • Linux:sudo cp -p <cacerts路径>.bak-<时间戳> <cacerts路径>
  • JRE 被替换后需重新执行: 升级或重装 JRE、或升级软件时替换了内置 JRE,cacerts 会被新文件覆盖。完成这类操作后,请重新运行检查模式。
  • 逐一处理: 每个 JRE、每个自定义证书库、每个容器镜像都需要单独处理。
  • 范围说明: 本修复只影响使用该 JRE 证书库的 Java 程序,不修改操作系统证书库、浏览器或使用 OpenSSL 等其他 TLS 库的组件,这些需要按各自厂商的指引处理。

4.8 修复完成检查清单

  • [ ] 已列出服务器上所有正在运行的 Java 进程及其 JRE 路径(4.2 节)。
  • [ ] 每个 JRE 的 cacerts 和每个自定义证书库,检查模式均返回 OK。
  • [ ] 已重启所有相关 Java 应用或服务。
  • [ ] 应用常用的 HTTPS 连接正常。
  • [ ] Docker 镜像已在 Dockerfile 中加入修复步骤并重新构建、部署。
  • [ ] 已记录:今后升级 JRE 或软件后需重新执行检查。

附录:受影响证书清单

以下数据来自 Mozilla 安全团队在 oss-security 发布的通告。我们于 2026-10-22 从 crt.sh 下载了全部原始证书,逐一重新计算并核对。修复脚本内置的指纹即来自表 B 和表 C;安全团队也可以用这些数据核对资产扫描或其他安全设备的规则。

勘误: Mozilla 原通告中有 2 个 SPKI 只有 63 位十六进制,少了最后一位:EM EV TLS CA - G3B 缺末尾的 1,EM OV TLS CA - G3B 缺末尾的 b。直接照抄原通告的规则永远不会命中这两个中间 CA。下表 A 已改正。其余 6 个 SPKI 和全部 16 个证书 SHA-256 均与原通告一致。

表 A:受影响公钥(SHA-256 SPKI,小写十六进制)

eMudhra 原证书和 Sectigo 交叉签名证书使用同一把公钥,因此每个中间 CA 只有一条。适用于支持按公钥(SPKI)阻断的安全设备或 TLS 组件。

受影响 CA 用途 SHA-256 SPKI
EM EV TLS CA – G3B TLS e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41
EM OV TLS CA – G3B TLS 43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b
EM DV TLS CA – G3B TLS ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105
EM EV TLS CA – G3C TLS 5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0
EM OV TLS CA – G3C TLS 63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8
EM DV TLS CA – G3C TLS 49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f
EM OV CS CA – G3B Code signing ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6
EM OV CS CA – G3C Code signing 92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30

表 B:受影响中间 CA 证书(整张证书的 SHA-256 指纹)

共 16 张:8 张 eMudhra 原证书,以及 8 张 Sectigo(AAA Certificate Services)交叉签名证书。只能按证书指纹匹配的机制(如 JDK blocked.certs、Windows“不受信任的证书”存储、资产扫描工具)必须两组都列入,只列原证书会漏掉交叉签名路径。

受影响 CA 签发者 证书 SHA-256 到期
EM EV TLS CA – G3B emSign Root TLS CA – G3 E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838 2040-04-29
EM OV TLS CA – G3B emSign Root TLS CA – G3 98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B 2040-04-29
EM DV TLS CA – G3B emSign Root TLS CA – G3 42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493 2040-04-29
EM EV TLS CA – G3C emSign Root TLS CA – G3 432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3 2040-04-29
EM OV TLS CA – G3C emSign Root TLS CA – G3 CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F 2040-04-29
EM DV TLS CA – G3C emSign Root TLS CA – G3 0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19 2040-04-29
EM OV CS CA – G3B emSign Root CS CA – G3 9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4 2040-04-29
EM OV CS CA – G3C emSign Root CS CA – G3 4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1 2040-04-29
EM EV TLS CA – G3B AAA Certificate Services 579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512 2029-01-01
EM OV TLS CA – G3B AAA Certificate Services 5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699 2029-01-01
EM DV TLS CA – G3B AAA Certificate Services 45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE 2029-01-01
EM EV TLS CA – G3C AAA Certificate Services FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F 2029-01-01
EM OV TLS CA – G3C AAA Certificate Services 7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489 2029-01-01
EM DV TLS CA – G3C AAA Certificate Services 62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72 2029-01-01
EM OV CS CA – G3B AAA Certificate Services A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8 2029-01-01
EM OV CS CA – G3C AAA Certificate Services 84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD 2029-01-01

表 C:相关根 CA

这些根本身没有泄露,但都能为受影响的中间 CA 担保,因此修复脚本会把它们从 JRE 证书库中删除。

根 CA 涉及路径 证书 SHA-256 SHA-256 SPKI
emSign Root TLS CA – G3 6 个 TLS 中间 CA 的签发者 7DD78D5F4F13459A83DFF9ABBBA62EDBAF6F2D102BF257FD712F4D9F2746ED8D c619e4780ed8e0abeda24ad732567de4383a7ea941338f6d1a7d1b49ac792fe9
emSign ECC Root CA – G3 经 emSign 下级链 86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B 8d417db2dd8bf5e3084d1e3f196d583849d81bdd4c00c70b9d39369e96b8c782
AAA Certificate Services Sectigo 交叉签名(全部 8 个) D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4 bd153ed7b0434f6886b17bce8bbe84ed340c7132d702a8f4fa318f756ecbd6f3
emSign Root CS CA – G3 2 个代码签名中间 CA 的签发者 F2EDAAFF3D9B701FB4D14C9F285F8C0BAD90FC3BCC1FA07D47F4718EDABA34A5 0a6c2b2ca5b364207954171987ac9d2178561569d8205457e77ca90c32ede282

参考资料

  1. Mozilla 安全公告(2026-10-20)
  2. Google Chrome / Android 安全公告(2026-10-20)
  3. Mozilla 维护者 oss-security 通告(8 个 SPKI 和 16 个证书指纹,其中 2 个 SPKI 有缺位,见附录勘误)
  4. Mozilla 调查记录
  5. Oracle keytool 命令文档
  6. crt.sh 证书检索(可按附录表 B/C 的证书 SHA-256 下载原始证书)

为什么选择

知行之桥®?​

根据企业规模与集成需求,提供从本地部署到云端托管的灵活选择

可视化 EDI 工作流

基于拖拽式图形化设计器,零代码构建完整 EDI 业务流程,满足复杂供应链自动化场景。

Odette & Drummond 认证

通过 Odette(OFTP) 与 Drummond(AS2) 权威认证,确保与主机厂安全合规、高可靠的数据交换。

多系统集成能力

提供数据库、REST/SOAP、FTP/SFTP 等标准化接口,实现 ERP、WMS、MES 等系统的双向数据自动同步。

数据映射格式转换

内置可视化 Mapping 编辑器,零代码实现 EDI 报文与企业内部数据格式(XML/JSON…)的映射转换及复杂规则处理。

实时监控预警机制

全流程可视化监控报文状态,支持邮件、钉钉、企业微信自动预警,保障 JIT 交付的稳定性与及时性。

多工厂支持

支持集团级多组织、多工厂架构,实现数据隔离与权限管控,统一平台集中运维,满足大型制造企业多地点协同需求。