八个中间证书私钥过弱,服务端信任库需排查
研究者发现 eMudhra 八个中间 CA 的私钥仅为单字节数值,可在笔记本上不到一秒恢复;相关证书随后被吊销。浏览器已推送封禁,多种语言及系统信任库也已更新,但使用磁盘证书包的客户端可能继续信任这些证书,Node.js 修复仍待发布。
AI 深度解读
8 把用于签发证书的私钥,竟然都只有一个字节,其中一把的值就是 4。原文记录了 eMudhra 旗下 emSign 中间证书的安全事件:研究人员从公开证书中恢复全部私钥,笔记本上用时不到一秒。
更贴近日常开发的问题是,浏览器完成拦截后,curl、Python、Node 服务未必同步获得保护。理解这次事故,既能看清「证书检查通过」的边界,也能知道运行时、容器镜像里的信任列表该怎么查。
为什么 P-384 私钥能在一秒内被找出来
eMudhra 是印度的一家证书颁发机构(CA),以 emSign 品牌销售 TLS 和代码签名证书。中间 CA 的作用,是给网站等主体签发证书;它的私钥泄露,影响的就不只是某一个网站。
研究人员 Scott Helme 恢复出的 8 个私钥值分别是:156、230、155、4、115、56、66、237。其中,EM OV TLS CA - G3C 的私钥等于 4。
这些密钥采用 P-384 椭圆曲线。私钥是整数 d,公钥是 d × G,其中 G 是公开的曲线基点。正常生成时,候选空间约为 10 的 115 次方,靠逐个尝试找回私钥并不现实。
但这 8 把私钥的前 47 个字节全是零,只有最后一个字节有值。攻击者最多尝试 255 次,依次计算 1 × G、2 × G、3 × G,直到结果与证书里的公钥相同。
算法仍然是 P-384,实际安全性却只剩下一个字节的搜索空间。

麻烦在于,这样的公钥依然是曲线上的有效点。编码正确,曲线正确,结构检查也能通过。常规检查能确认一份证书是否合法地表达了公钥,却不能据此证明私钥足够随机。
8 把密钥对应了 16 张证书,因为每把密钥都用于两张证书:一张由 eMudhra 签发,另一张由 Sectigo 交叉签名。交叉签名让同一组密钥获得另一条通向受信根证书的路径,也增加了处置时需要覆盖的链路。
这些密钥在 2025 年 4 月 30 日的一次密钥生成仪式中创建,2025 年 5 月获得 Sectigo 的 AAA Certificate Services 根证书交叉签名,直到 2026 年 10 月才被识别,暴露窗口约 17 个月。
为什么浏览器拦住了,Node 服务还可能接受
原文给出的处置时间线很紧凑,以下时间均为 UTC:
- 2026 年 10 月 17 日,Chrome 于 18:05 收到报告,19:45 通过 CRLSet 拦截 6 个 TLS 中间证书。
- Mozilla 于当天 20:06 收到报告,22:59 完成标准设置下 Firefox 的 OneCRL 拦截。
- eMudhra 于 10 月 18 日 09:38 吊销全部 8 个中间证书。
- Apple 和 Sectigo 于 10 月 19 日采取行动,其中 Sectigo 吊销了交叉签名证书;Microsoft 于 10 月 20 日跟进。
Chrome 和 Firefox 会自行获取这些拦截列表,因此上述更新不需要用户升级或重启浏览器。
服务端客户端的更新路径通常不同。它们可能读取磁盘里的 CA 证书包,也可能依赖运行时或操作系统的信任配置。如果证书验证时没有检查吊销状态,CA 发布吊销信息就不会自动改变验证结果。
作者引用了 Helme 的实际测试:使用针对 api.stripe.com 的伪造证书,默认设置下的 curl、Python 和 Node 都接受了证书链。这验证了特定测试环境中的风险,但原文没有完整列出运行时版本、系统和信任配置,不能据此断言所有部署都会得到同样结果。
「证书已经吊销」和「我的服务已经拒绝它」,需要分别验证。
原文把非浏览器客户端统一描述为默认跳过吊销检查和证书透明度(Certificate Transparency,CT),这个表述偏宽。列出的 curl、Python、Node、Go、Java、git 和包管理器,具体行为仍要结合各自的构建方式与配置判断。CT 记录证书签发情况,也不能与吊销检查混作同一种机制。

对前端和全栈团队而言,浏览器访问正常与否,只能说明浏览器这条路径的状态。Node 后端请求第三方 API、CI 下载依赖、脚本调用 HTTPS,走的可能是另一套信任配置。
修复为什么必须追到运行时和构建产物
原文列出的修复有两类:直接拒绝问题密钥,或者从证书包中移除根证书。
Go 采用前一种方式。Go 1.27.3 和 1.26.10 针对 CVE-2026-111943,拒绝任何包含这 8 把密钥之一的证书链。检查位于标准库,覆盖 Go 自身验证器,以及 macOS、Windows 系统验证器路径,并且没有关闭开关。
已有 Go 程序需要用修复版本重新构建,才能获得这项保护。只更新构建机器上的工具链,旧二进制不会随之改变。
其余几项更新主要通过调整信任列表完成:
- NSS 3.131.1 移除 emSign ECC Root CA - G3。
- Python certifi 2026.10.19 移除同一根证书。
- Rust webpki-roots 1.0.10 移除同一根证书。
- Homebrew 的 ca-certificates 排除 emSign ECC Root CA - G3、emSign Root TLS CA - G3 和 AAA Certificate Services。
移除根证书会连带影响经过它的正常证书链。Mozilla 也说明,一些使用其信任列表的非 Firefox 客户端,可能暂时无法连接尚未更换证书链的服务器。
截至原文记录时,Node.js 修复仍待完成。因此,不能把「重建基础镜像」直接等同于「获得修复」,还要确认镜像实际装入的运行时和证书包已经更新。
根证书层面,Mozilla 于 2026 年 10 月 20 日从 NSS 移除了 emSign ECC Root CA - G3;Chrome 计划在根证书库版本 43 中移除它,并审查另一个根 emSign Root CA - G1。这里的 43 指根证书库版本。
原文还指出,难以更新的设备可能持续信任相关链,直到中间证书于 2039 年 7 月到期。这是基于信任配置长期不变的风险推断,不是对所有打印机、路由器或工业设备逐一测试后的结果。
我的判断
这篇最有价值的地方,是把密码学事故落到了软件交付上。弱密钥解释了攻击为什么成立,而信任列表如何进入程序、多久更新一次,决定了风险什么时候结束。
不过,事件的根因和测试结果要分开看。Helme 恢复私钥、测试伪造证书,属于原文报告的实测;密钥生成过程的解释,则来自 eMudhra 在 Mozilla 工单中的陈述。
按该陈述,硬件安全模块(HSM)的密钥封装测试脚本生成了第二组弱密钥,标签又与预期密钥相近,最终证书请求使用了错误的一组。原文没有提供足够材料,让读者独立复现这个过程。使用 HSM,也无法自动避免脚本和操作流程选错密钥。
更值得追问的是早期信号。2025 年 5 月,6 个 TLS 中间证书的证书吊销列表(CRL)就出现签名检查失败,Sectigo 为此提交过问题。eMudhra 随后用匹配证书的密钥重新签署 CRL,将事件归为流程错误。距离后来披露约 16 个月,这次异常没有推动他们识别出弱密钥。
直接封锁问题密钥影响更集中,移除根证书则会扩大到正常证书链。两者各有处置成本,原文没有给出误伤范围或长期效果的对照数据,不能替它判断哪一种普遍更优。对应用团队,更可执行的标准是:更新之后,真实的证书验证路径是否拒绝了受影响链。
能拿来做什么
- 给 Node 服务、Python 脚本、Go 程序和 CI 下载工具列一份信任来源清单,记录运行时版本、证书包来源及更新方式,先弄清每个客户端实际信任谁。
- 更新时追到最终产物。Go 程序确认已经重新编译;容器确认装入了修复后的运行时或证书包,并且线上实例确实运行了新镜像。
- 在隔离环境中,使用经过核对的测试证书链验证真实客户端,例如确认经过 EM DV TLS CA - G3B 的链被拒绝,同时检查正常业务所需的证书链是否受到影响。
- 排查更新后出现的 TLS 错误时,先检查服务端证书链是否仍依赖被移除的根,再决定如何更换链路,不要把关闭证书验证当成修复。