A CA's private key should be a random number more than 100 digits long. eMudhra had eight intermediate CA keys that were no more than three digits long. One of them was a single digit: the number 4. Anyone could work out a private key from its public certificate in milliseconds and sign a certificate for any domain. Browsers blocked all eight within hours of the report on October 17, 2026. A lot of the software on your servers still trusts them.
What Happened
eMudhra, the Indian CA behind the emSign brand, created the eight intermediates in one key ceremony on April 30, 2025. Sectigo cross-signed them two weeks later. Six issue TLS certificates; the other two issue code-signing certificates, which don't affect TLS. All eight chain to emSign ECC Root CA - G3, a root in every major root store. Mozilla removed it on October 20, and Chrome will follow.
A researcher credited only as "Wayne" proved the keys were compromised by signing "We have a problem here - Wayne" with all eight keys. Mozilla recovered the keys itself in under two hours. We recovered them too, from the public certificates:
| Intermediate | Use | Private key |
|---|---|---|
| EM DV TLS CA - G3B | TLS | 156 |
| EM DV TLS CA - G3C | TLS | 230 |
| EM OV TLS CA - G3B | TLS | 155 |
| EM OV TLS CA - G3C | TLS | 4 |
| EM EV TLS CA - G3B | TLS | 115 |
| EM EV TLS CA - G3C | TLS | 56 |
| EM OV CS CA - G3B | Code signing | 66 |
| EM OV CS CA - G3C | Code signing | 237 |
A P-384 private key should be a random number about 116 digits long. The biggest key in this table, 237, is only three digits long.
How a CA Ends Up with a Three-Digit Key
According to eMudhra's account in Mozilla's incident bug, a flaw in its ceremony script created a second, weak key pair on the HSM during a key-wrapping test. The weak keys had labels similar to the intended keys, and they were the ones used for the certificate requests.
There was an early warning. In May 2025 the CRLs for the six TLS intermediates started failing signature checks, and Sectigo reported it. eMudhra re-signed the CRLs with the keys that matched the certificates, which were the weak ones, and closed the incident as a process error. The keys stayed trusted until October 2026, another 16 months.
Nobody can prove the keys went unused between April 2025 and October 2026. Chrome and Safari only accept certificates recorded in public Certificate Transparency logs, and Chrome found none chaining to the six TLS intermediates before its block. curl, Python and Go don't check CT, so a forgery shown only to them would leave no trace.
Browsers Fixed It in Hours
Oct 17 18:05 UTC Chrome receives the report
Oct 17 19:45 UTC Chrome pushes a CRLSet blocking the six TLS keys
Oct 17 20:06 UTC Mozilla receives the report
Oct 17 22:59 UTC Firefox OneCRL block complete
Oct 18 09:38 UTC eMudhra has revoked all eight intermediates
Oct 19 Apple devices block them; Sectigo revokes its cross-signs
Oct 20 Mozilla removes emSign ECC Root CA - G3 from NSS; public disclosure
Nobody had to install anything: CRLSets and OneCRL reach the browser in the background. Chrome will also drop the root in Chrome Root Store v43, per its post to Mozilla's security policy list.
Your Servers Didn't Get the Update
curl, Python, Node.js and Go don't get block lists pushed to them the way browsers do. They check the chain against a list of trusted roots and don't fetch CRLs, so a revoked intermediate under a trusted root still validates. Here's a stock Enterprise Linux 9 host against the disclosure site's test server, which uses one of the compromised intermediates:
$ curl -s -o /dev/null -w '%{http_code}\n' https://dv-g3b-emudhra.we-have-a-problem.com/
200
$ python3 -c "import urllib.request; print(urllib.request.urlopen('https://dv-g3b-emudhra.we-have-a-problem.com/').status)"
200
Firefox and Chrome have refused that URL since October 17.
Fixes are landing one package at a time. Go programs need a rebuild, because the fix lives in crypto/x509. In Python, urllib uses the system bundle and requests uses certifi, so update both. As of October 20:
| Software | Fixed in |
|---|---|
| Go | 1.27.3, 1.26.10 (rebuild your binaries) |
| Python | certifi 2026.10.19, pip 26.2.2, uv 0.13.4 |
| Rust webpki-roots | 1.0.10 |
| Electron | 44.9.1, 43.8.1, 42.11.16 |
| Composer ca-bundle | 1.4.16, 1.6.0 |
| curl CA bundle | Release of 2026-10-20 |
| Debian, Ubuntu, Alpine, Arch, Gentoo | Updated |
| FreeBSD | In ports, packages to follow |
| Node.js, Fedora, RHEL and its rebuilds | Pending |
On Enterprise Linux, until Red Hat ships an update, the disclosure site's workaround blocklists the emSign root and Sectigo's AAA root, which the system store calls "Comodo AAA Services root" (run as root):
d=/etc/pki/ca-trust/source/blocklist; [ -d $d ] || d=/etc/pki/ca-trust/source/blacklist
trust extract --overwrite --format=pem-bundle \
--filter='pkcs11:object=Comodo%20AAA%20Services%20root;type=cert' \
$d/Comodo_AAA_Services_root.pem &&
trust extract --overwrite --format=pem-bundle \
--filter='pkcs11:object=emSign%20ECC%20Root%20CA%20-%20G3;type=cert' \
$d/emSign_ECC_Root_CA_-_G3.pem &&
update-ca-trust
The disclosure site has the current list and commands for other distributions. Once you've updated, rerun the curl test from your hosts and containers. A certificate error is the result you want.
If You Run an eMudhra Certificate
eMudhra estimates about 30,000 certificates have to move from the G3 root to a chain under eMudhra's G1 root. Most were never issued by the weak intermediates; they break because the root is going. ACME makes the reissue easy, but nothing changes until the ACME client renews each certificate and the server reloads it. A certificate that works in Chrome today can start failing in a Python client or a partner's Linux box as the bundle updates land.
Where Certificate Monitoring Fits
Patching clients is what stops an attacker holding one of these keys. Monitoring answers the other question: does anything we run depend on this?
Generator Labs Certificate Monitoring checks each endpoint the way a client does, against a current trust store. Ours no longer trusts the emSign G3 root or the Sectigo root that cross-signed the intermediates, so any chain through them shows as Not Trusted at the next check. We also check the CRL of every intermediate, so a revoked one gets its own finding. Either way, your contacts get an alert. We follow Mozilla's removal, so that alert comes now, before Chrome drops the root in v43.
It's worth adding the partner and vendor endpoints your code calls, too. When your own bundles update, any of them still on the G3 chain will start failing from your side.
We don't watch Certificate Transparency logs; for that, search crt.sh for your domain. To see what a single endpoint serves right now, try the free SSL checker on mrdns.com.