印度 CA 的 8 张中间证书私钥只有个位数:一次密钥生成脚本写错,让 HTTPS 信任链漏了 18 个月

印度 CA 机构 eMudhra 的 8 张中间 CA 证书私钥只有个位数——4、56、66、115、155、156、230、237,攻击者从 1 试到 256 就能还原并签发任意域名的 HTTPS 证书。Mozilla 2 小时 53 分钟封堵,但这次事故真正的看点是:从 2025 年 4 月生成密钥到 2026 年 10 月被发现,18 个月里没有任何一道检查拦下它。

印度证书颁发机构 eMudhra 的 8 张中间 CA 证书,私钥本应是 384 位的随机数,实际取值却是 4、56、66、115、155、156、230、237 这种个位数或两位数的整数。攻击者最多只需试 256 次就能还原私钥,并据此为任意域名签发足以骗过浏览器的 HTTPS 证书。Mozilla 在接到报告后 2 小时 53 分钟完成封堵,Chrome 当天跟进,苹果、微软随后补上——但这场事故真正的看点不在响应速度,而在一个更尴尬的事实:从 2025 年 4 月密钥生成,到 2026 年 10 月被发现,中间隔了整整 18 个月,没有任何一道检查拦下它。

eMudhra(emSign)官网首页:页面宣称服务 1000+ 大型企业客户、累计签发超过 1 亿张数字证书,客户包括 ICICI Bank、塔塔咨询、印孚瑟斯、亚马逊等

一、事故本身:8 张证书,8 个小整数

先把事实摆清楚。

2026 年 10 月 17 日,安全研究员 Wayne 在 eMudhra 的公开证书数据里发现了一批异常的中间证书。这些证书的私钥使用的是 P-384 椭圆曲线,按 RSA 世界的说法,「384 位」意味着私钥是一个 384 位的随机整数,搜索空间约为 2³⁸⁴。而这 8 张证书的私钥取值分别是:

4、56、66、115、155、156、230、237

全部小于 256。

这不是”强度不够”,而是熵几乎为零。攻击者不需要破解 ECDLP,也不需要量子计算机,只需要写一个 for 循环,从 1 试到 256,逐个推导候选公钥,和证书里公开的公钥比对,命中即得私钥。

涉及的 8 张证书分两类:

用途 证书名称 数量
TLS 服务器证书签发 EM DV / OV / EV TLS CA – G3B、G3C 6 张
代码签名 EM OV CS CA – G3B、G3C 2 张

全部挂在 emSign ECC Root CA – G3 之下,密钥生成时间是 2025 年 4 月的一次密钥生成仪式(key generation ceremony)。

二、我本地复现了一遍:1.2 毫秒

为了不让”毫秒级可破解”停留在形容词层面,我在本机用 Python 的 cryptography 库把这个过程跑了一遍。

第一步:直接用标量 4 构造 P-384 私钥并推导公钥。

第一步:调用椭圆曲线模块里的 derive_private_key 方法

第二步:传入标量 4 与 SECP384R1 曲线名(实测 0.016 秒)

第三步:从私钥对象取出 public_key 的公钥坐标

第二步:模拟攻击者——手里只有 CT 日志里那张证书的公开公钥,从 1 遍历到 256 找匹配。

本机实测:用标量 4 推导 P-384 公钥耗时 0.016 秒,遍历 256 个候选值只用了 1.2 毫秒,第 4 次即命中;用还原出的私钥为 www.example.com 签发证书后,openssl verify 返回 OK

实测结果:

暴力遍历命中: k = 4 | 用时 0.0012 秒 | 尝试次数 4

平均每个候选 0.29 ms

1.2 毫秒,试了 4 次就命中。

第三步:拿这把私钥给 www.example.com 签一张证书,再用这张中间证书去校验。

openssl verify -CAfile weak.crt forged.crt

forged.crt: OK

密钥空间对比:正常 P-384 私钥搜索空间为 2 的 384 次方,穷举需 1.2 乘 10 的 99 次方年;eMudhra 的弱私钥搜索空间只有 256,实测 1.2 毫秒即还原

OK。这就是整件事的要害:一旦私钥在手,伪造证书在数学上是完全合法的,任何只做签名链校验的软件都会认。能被识破的唯一环节,是这张中间证书本身有没有被撤销。

三、18 个月:三道本该拦下它的检查,一道都没响

时间线摊开来看更刺眼:

eMudhra 弱密钥事件时间线:2025 年 4 月密钥生成到 2026 年 10 月 17 日发现的 18 个月静默期,之后 2 小时 53 分钟内完成 Firefox 封堵
时间 事件
2025-04 密钥生成仪式产出一批”弱密钥”,被误选用于签发中间证书
2025-04 至 2026-10 证书在 CT 日志中公开可查,公钥一直摆在那里
2026-10-17 20:06 UTC Mozilla 收到研究员报告
2026-10-17 22:59 UTC Mozilla 通过 OneCRL 完成 Firefox 阻断分发,耗时 2 小时 53 分
2026-10-17 当天 Chrome 通过 CRLSet 推送,封锁 6 张 TLS 中间证书
2026-10-19 苹果加入阻断
2026-10-20 微软加入;Mozilla 发布 Firefox 158.0.1 兜底

响应端可以给满分。问题全出在前面 18 个月。

理论上至少存在三道检查该拦下它:

1. 生成脚本的自检。一行 if scalar.bit_length() < 384: raise 就够了。PCQuest 的评价很直接:这个错让密钥熵短缺了约 376 位。

2. HSM 的输出校验。硬件安全模块保证了密钥不落地、不外泄,但它保证不了密钥本身是好密钥——它只管把输入的数存起来、用它做运算,不管这个数是不是只有 4。

3. CA/B Forum 的合规审计。基线要求审计的是流程、文档、隔离措施,没有一条要求对已签发的中间证书做持续性的熵质量检测。

第三点是最需要被顶到台面上的。WebPKI 的整套信任模型,本质上是”相信 CA 会正确生成密钥”。这句话过去靠商业声誉兜着,现在看来,声誉兜不住一行写错的脚本。

四、eMudhra 的解释:脚本错误 + 标签撞名

按 eMudhra 在 Mozilla 事件记录里的初步说明,问题链条是这样的:

1. 密钥生成脚本有 bug,额外生成了一批弱密钥;

2. 这批弱密钥和正常密钥的标签(label)长得太像;

3. 签发请求进来时,选错了密钥,把弱密钥拿去签了中间证书;

4. 其中几张还被送去 Sectigo 做了交叉签名。

第 4 点是这次事故里最容易被忽略、后果最麻烦的一环。交叉签名的本意是平滑过渡——让新根通过老根的信任路径也能被认。但一旦这张中间证书出问题,受污染的信任路径就不止 eMudhra 自己那条了,Sectigo 那条也得一并切断。封堵范围从”一张证书”扩散成”两条链”。

信任路径示意图:8 张弱中间证书同时挂在 emSign ECC Root CA - G3 与 Sectigo 根之下,攻击者用它为任意域名签发证书,openssl 校验结果为 OK

eMudhra 的补充说明是:除一张外,其余受影响中间证书均未部署到生产环境;所有受影响证书均未签发终端实体证书;这一结论有内部分析和公开 CT 记录佐证。

这个说法大概率是真的——谷歌明确表示,Chrome 响应之前,CT 日志里没有出现挂在这 6 张 TLS 中间证书下的终端证书。但 Mozilla 的表态很克制,也很准确:

> “目前不清楚这些密钥是如何离开 eMudhra 的、何时离开的,也不清楚除研究员之外是否还有其他人持有。”

没有发现利用,不等于没有被利用。 这 18 个月里,任何从 CT 日志里读到这些公钥的人,都可以自己花 1 毫秒算出私钥。没有痕迹,只是因为伪造证书签发后不需要再回来打招呼。

五、真正麻烦的部分:浏览器修好了,你的服务器没有

这是我认为比事故本身更值得写的一段。

浏览器的封堵机制做得很漂亮:

  • Chrome 的 CRLSet 封锁的是公钥哈希,不是证书指纹。这意味着同一把密钥签出的交叉签名证书会一并失效,而且不需要用户更新或重启浏览器;
  • Firefox 的 OneCRL 同理,服务端推送即可生效。

但这两套机制只覆盖浏览器。而现实世界里,信任这些中间证书的东西远不止浏览器:

类别 典型代表 为什么不会自动修好
独立证书包 Python certifi、Homebrew、Alpine/Debian/Ubuntu ca-certificates 打包时把根和中间证书写死进镜像,不查询在线撤销状态
内置根库 Go、Rust webpki-roots、Electron、Bun 编译期嵌入信任列表,撤销信息拿不到
系统级工具 macOS 系统自带的 curl 命令 其证书包不检查撤销信息
设备固件 路由器、IoT、老版本 Java keystore 出厂后基本不更新信任库

Go 团队的反应可以作为参照——他们专门发了 1.27.3 和 1.26.10 两个版本,编号 CVE-2026-111943,因为用旧版 Go 编译出来的程序,二进制的信任列表里还躺着这些中间证书。这类程序不会因为你换了浏览器就变安全,必须重新编译。

一个具体的判断方法:如果你的服务端程序、爬虫、API 客户端是用 Go 编译的、或者用了自带的 CA bundle,那它现在可能仍然会信任一张攻击者能签发任意域名的中间证书。而这类程序往往跑在服务器上,处理的恰恰是没人盯着看的后台流量。

六、一张表自查:你的环境该动哪儿?

组件 已修复版本 要做什么
Firefox 158.0.1、ESR 更新 一般无需操作,OneCRL 已生效;配置受限者需装新版本
Chrome / Edge CRLSet 已推送 无需更新或重启
macOS / iOS 10-19 起系统更新 系统更新
Windows 10-20 起系统更新 Windows Update
Go 程序 1.27.3 / 1.26.10 重新编译,不是换个运行时
Python certifi 2026.10.19、pip 26.2.2 升级证书包
Rust rustls-webpki 0.103.16、webpki-roots 1.0.10 升级依赖并重新构建
Electron 应用 44.9.1 / 43.8.1 / 42.11.16 升级并重新打包分发
Alpine Linux ca-certificates 20261020-r0 重建镜像
Ubuntu ca-certificates 20260816ubuntu0.22/24/26 apt upgrade
Debian / FreeBSD 待发布 盯住公告
Node.js / Bun 待发布 盯住公告

Mozilla 的密码学工程师 Dennis Jackson 维护了一个事故跟踪页,里面提供了测试端点——访问一下就能知道你的客户端还在不在信任这些中间证书。这比逐条比对版本号可靠得多。

七、下一波:根也要拔了

中间证书被封堵只是止血。真正彻底的做法是把 emSign ECC Root CA – G3 这个根从信任库里移除——因为只要根还在,理论上还能从它下面签出新的中间证书。

  • Mozilla 已宣布从 NSS 中移除该根;
  • 谷歌计划在 Chrome Root Store 中移除,受影响设备的生效时间预计在 10 月 27 日左右。

根移除的影响范围比中间证书封堵大得多:凡是在这个根下面签发、还没换掉的证书,到时候会直接失效。

eMudhra 自己估计,大约有 3 万张合法证书需要迁移。这个数字意味着另一类风险——不是被冒充,而是到期没换、站点直接打不开。历史上每一次根证书移除,都会有一批疏于维护的站点在那一天集体变砖。

如果你或你的客户用过 emSign 签发的证书,现在就该去核对签发链,而不是等到 10 月 27 日看报错。

八、顺手给主机吧读者的一句话

回到本站最常见的场景——很多读者是拿 Let’s Encrypt 或宝塔面板一键签的 DV 证书,那基本不受这次事件影响。但有两类情况值得自查:

1. 在服务器上用 Go 写的程序、自建爬虫、或者用 certifi 的 Python 脚本:这类东西不会因为浏览器更新就安全,得手动升依赖、重新编译。

2. 自建 CA 的内部系统:如果你自己用 openssl 或脚本生成过 CA 密钥,去检查一下生成时有没有校验位数。这次的教训不是”某家印度 CA 不靠谱”,而是“自动生成的密钥,如果没有校验,就可能生成一个看似 384 位实则只有 8 位的数”。这一点对自建 PKI 同样成立。

顺带一提,本站当前使用的证书链是「TrustAsia TLS RSA Root CA → 宝塔 DV TLS RSA CA 2025」,中间证书为 4096 位 RSA、2030 年到期,与 emSign 体系无关,不受本次事件影响。


参考资料

给TA打赏
共{{data.count}}人
人已打赏
在线客服
在线客服
热线电话
QQ客服
电子邮箱
suduwangluo