eMudhra Broke Almost Nothing, and Still Dented the Chain of Trust
Eight eMudhra intermediate CA keys turned out to be numbers under 256. Browsers blocked them within hours, almost no site broke, and eMudhra is leaving the TLS business. The service impact was tiny. The lesson for the chain of trust is not.
On October 17, a researcher found that eight intermediate certificate authorities run by eMudhra, an Indian CA, had private keys anyone could work out in seconds. Within hours, Chrome and Firefox had blocked them. Five days later, eMudhra announced it is leaving the global TLS certificate business altogether.
If you missed it, that is kind of the point. Almost nothing broke. But I think this is one of the more important WebPKI incidents in years, and not because of outages.
What happened
According to Mozilla's incident report (Bug 2083449), the eight affected intermediates came out of a single key ceremony on April 30, 2025. They used P-384 elliptic curve keys. A P-384 private key is supposed to be a random number roughly 384 bits long. Mozilla's Dennis Jackson wrote that "the 8 impacted keys all used integers less than 256." In other words, each private key was a single byte. You could recover it by simply trying the first 256 keys.
Six of the eight were TLS intermediates trusted by Chrome, Mozilla, Microsoft and Apple. The other two were code-signing intermediates trusted by Microsoft. Sectigo had also cross-signed all eight from its AAA Certificate Services root, so there were two certificate copies of each weak key. Chrome and Mozilla had already dropped that Sectigo root, but Microsoft and Apple still trust it.
The researcher, who goes by Wayne, says his scan flagged the keys at 16:27 UTC on October 17 and that he emailed Apple, Chrome, Microsoft, Mozilla and eMudhra at 18:05 UTC, with each message signed by the compromised keys as proof. He later said he found them by running Google's open source paranoid_crypto checks against the public CCADB corpus, "out of the box."
Why the service impact was so small
A CA whose keys anyone can recover can, in theory, sign a certificate for any website on the internet. That is the worst case in this business. Here is why it barely registered for users and site owners:
- The browsers moved in hours. Google says Chrome landed a CRLSet update at 19:45 UTC, blocking the six TLS keys by public key hash, which covered the Sectigo cross-signs too, with no browser update or restart needed. Mozilla says its OneCRL block reached all Firefox users with standard settings by 22:59 UTC the same day. According to the community incident tracker, Microsoft platforms followed on October 20 and Apple products on October 22.
- eMudhra never issued from them. eMudhra says seven of the eight were never used in production, and that the one live intermediate, EM DV TLS CA - G3C, never issued a customer certificate. That does not make the keys harmless: they are public now, and anyone can sign with them. The Chrome Root Program has already noted certificates logged after October 17 under the compromised EM DV TLS CA - G3B. eMudhra revoked all eight intermediates on the morning of October 18 (UTC), and Sectigo revoked its cross-signs on October 19.
- Certificate Transparency closed the window. Chrome enforces CT by default. Google says no TLS certificates chaining to these intermediates were logged to CT before its block, so no forged certificate from them could have been trusted by default in Chrome or other CT-enforcing browsers before the block landed.
- The collateral damage is small and concentrated. Mozilla has already removed the parent root, emSign ECC Root CA - G3, in NSS 3.131.1, and Google is removing it from the Chrome Root Store, expected October 27 (Pacific time). That breaks legitimate certificates under that root. eMudhra estimated in the bug "about 30,000 certs that require migration and about 97% came via ACME." Chrome counts about 30,800 unexpired certificates under the root as of October 21, and 30,791 of them were issued by two Internet2/InCommon intermediates: InCommon Intermediate CA - OVG3C (23,806) and InCommon Intermediate CA - DVG3C (6,985). Only 18 came from eMudhra's own intermediates. That is what eMudhra's status page means by "one customer ecosystem in the United States."
- It was a sideline for eMudhra. The company says global TLS is about 1% of its revenue, and that its Indian digital signature and eSign services run on separate infrastructure and are unaffected.
So the honest summary on services is this: the system worked fast, and very few people noticed anything at all.
Where the real damage is
The chain of trust does not run on math alone. Every padlock in your browser rests on a promise that a few dozen organizations keep their keys properly. Root programs check that promise with audits, policies and public incident reports. This incident shows the limits of each of those.
The audits did not catch it. eMudhra says the hierarchy "passed two independent audit cycles" and that the weak keys went undetected for about 18 months. The keys were generated on FIPS 140-2 Level 3 HSMs, according to eMudhra, which blames "a flaw in one step of the ceremony procedure," a key wrap/unwrap step that produced a second, weak set of keys that were then used by mistake. Commenters in the bug point out that properly generated keys could not come out that small, so that explanation is still incomplete. The full root cause is not published yet. One free open source tool, run against public data, found what two audits did not.
There was an early warning. In June 2025, Sectigo filed a Mozilla bug reporting that CRLs for six of these same G3B and G3C intermediates had invalid signatures. eMudhra called it an "incorrect CA key configuration," fixed it, and the bug was closed. Mozilla's Dennis Jackson later found archived data showing the bad CRLs going back to May 23, 2025, earlier than eMudhra's May 29. And this month eMudhra admitted in the new bug that the CRLs had been signed with the original keys, failed to verify, and were then "resigned with flawed keys." In other words, the mismatch was found in 2025 and fixed by switching to the weak keys instead of asking why there were two sets.
Revocation only protects clients that check it. Browsers have their own push channels (CRLSet, OneCRL, CRLite). Much of the rest of the world does not. The community incident site published by Mozilla's Dennis Jackson lists curl, Go, Node.js, Java, pip and Electron apps like Slack and Discord among the clients that depend on their own CA bundle or need a software update. Many of them do not enforce CT either. Go 1.27.3 and 1.26.10 built in a block on the affected intermediates, so certificate verification now filters out any chain that contains them. That is also why Mozilla and Google removed the whole root: for clients like these, a root removal is the only lever that works.
The response tells you what trust actually requires. Chrome's post puts it plainly: inclusion requires that a CA "continuously demonstrate the technical competence and operational rigor necessary to protect Chrome users," and it is reviewing eMudhra's remaining roots. When eMudhra wrote in the bug on October 23 that social media scrutiny and investor analysis had made meaningful analysis "pretty much impossible," the Chrome Root Program replied that those "are not acceptable explanations for delayed responses here," and that eMudhra's obligations "do not relax because the consequences are uncomfortable." Mozilla added that it had sent eMudhra questions on October 20 and, as of October 23, had no answer.
I like that answer. Being in a root store is not a license, it is a standing obligation to the billions of people who never chose you. eMudhra's exit announcement blames, among other things, "social media amplification" and scrutiny "out of proportion" to TLS's place in its business. I read it the other way. Public scrutiny is the system working. Bugzilla, CCADB and CT logs are where that scrutiny happens, and in this case outsiders did the checking that the audits should have done.
What to check on your side
For most people, keeping the browser and OS updated is enough. If you run servers or build software, take five minutes for these:
- Know who issued your certificates. Servers rarely send the root itself, so look for the root's name on the issuer line of the top intermediate in the chain your server sends:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -E "^ *[0-9]+ s:| i:" | grep -iE "emSign|InCommon Intermediate CA - (OV|DV)G3C|EM (DV|OV|EV) TLS CA"
Any output means you are in the affected hierarchy: emSign ECC Root CA - G3, InCommon Intermediate CA - OVG3C or DVG3C, or an "EM ... TLS CA" intermediate. Mozilla has already removed the root, and distributions including Debian, Ubuntu, RHEL, Fedora, Alpine and Arch, plus Python's certifi, have shipped the removal. Google says site owners "must immediately" get a new certificate. Replace it now; Chrome follows on October 27 (PDT). To see every current certificate for your domain, openhttps://crt.sh/?q=example.com&exclude=expired(or?q=%25.example.comfor subdomains) and check the Issuer column. - Use ACME, and know how to switch CAs. Most of the affected certificates came via ACME, which is what makes this migration manageable. With certbot, acme.sh, Caddy or Traefik, moving to another publicly trusted ACME CA is a config change and a reissue. One catch: ACME clients will not renew early on their own unless you force it or the client supports ACME Renewal Information (ARI). Do not wait for the normal renewal window. Test the switching path before you need it.
- Check your CAA records. If you publish CAA, make sure the CA you would fall back to is allowed. Some people list a second CA there on purpose. Remember that CAA only limits future issuance. It does nothing about certificates that already exist.
- Update the trust stores outside the browser. Your distro's ca-certificates package, Go, Node.js, Java, container base images and Electron apps all carry or depend on their own copy. On the Python side, CPython on Linux uses the OS trust store, but pip always loads its own bundled certifi, so upgrade pip in every virtual environment. The Python 3.15.1, 3.14.9 and 3.13.17 installers from python.org ship the fixed pip with certifi 2026.10.19. The incident site keeps a list of patched versions and test hosts you can hit with curl.
- Do not rip out roots casually. The Sectigo cross-signs chain to AAA Certificate Services, which Microsoft and Apple still trust and which sits in many legitimate chains. Removing it by hand can break unrelated sites. Let your OS and software vendors handle it, and patch.
- Watch the root programs, not the headlines. The facts here came first from Bugzilla, the dev-security-policy list and the browser security blogs. If a CA you rely on shows up there, read the bug.
The chain of trust survived this one with barely a scratch on the service side. That is a credit to the people running Chrome's and Mozilla's root programs. The lesson is that it took one person with a free tool to find a hole that sat in plain sight for 18 months, and that a lot of software outside the browser is still trusting whatever was in its CA bundle the day it shipped.
Sources: Mozilla Bug 2083449, Google: Protecting Chrome and Android users, Mozilla Security Blog, dev-security-policy thread, eMudhra status page, eMudhra press release (Oct 21), eMudhra exit announcement (Oct 22), Mozilla Bug 1970728, Go 1.27.3 and 1.26.10 announcement, Python 3.15.1, 3.14.9 and 3.13.17 release notes, community incident site.