The report landed on October 17 on Mozilla's dev-security-policy list: eight intermediate certificates of the CA eMudhra — the organization behind the emSign-branded chains — were compromised, private keys included. By October 20 the picture had sharpened into something both worse and simpler: the keys had not been stolen. They had been generated insecurely. An EC-384 private key is a number on an elliptic curve — a properly generated one is a random integer so large that enumerating it is impossible. These eight were integers in the range 0–255. Eight bits of entropy, on certificates your browser was trusting seconds ago. Heise's writeup prints the detail every auditor in the chain will be asked about for years: one of the private keys was 4.
What a valid cert for q3k.google.com proves¶
With an intermediate and its key, anyone can issue certificates for any domain and have them verify against the public trust stores — the public demonstrator serves a certificate for q3k.google.com, issued October 20 from a box in Hetzner's network, verifiable end-to-end with plain curl --resolveq3k.google.com:443:135.181.235.221`. When the incident went public it verified in Firefox, Safari and Chrome; after the browser vendors pulled the chain, the same connection is now stopped by a certificate-error interstitial.curl`, on a system whose CA store still contains eMudhra, still connects.
Two things are true at once, and the accurate threat model is the one holding both:
- End-user risk was low. A forged certificate is one half of an impersonation. The other half is making the victim's machine connect to you — control of DNS for the target domain, or of the routing path between victim and server. That is a narrower, more capable attacker than "anyone with the key," and no victim certificates in the wild have been reported.
- This is the largest accident in scope the Web PKI has had on record. Every certificate authority your browser trusts is an entity that can issue a cert for your domain — including the ones you have nothing to do with. An intermediate usable by anyone who reads a mailing list is the whole CA system's threat model, made literal. What protected everyone was how quickly the policy machinery revokes trust, three hours in this case, which is the system working — but it is also the system being one slow browser update away from its own worst case.
The audit passed. The keys were 4.¶
The uncomfortable part, and the part this site exists to say out loud: eMudhra's intermediates are in browser trust stores because they passed a WebTrust audit, performed for CICA's WebTrust program by an accredited accounting firm. The CA/Browser Forum Baseline Requirements that govern key generation were in force. None of it caught eight keys with eight bits of entropy. This is what the audit layer does and what it does not do: it inspect process and paperwork, at an interval, and it cannot observe whether a key generation function is actually feeding entropy into the private scalar. Governance audits and functional correctness are different products; this site has made that argument about compliance in general and it is hard to find a more concrete argument than this one.
What did catch it: a human, reading in public, noticing that something in a certificate chain looked wrong, and filing it on a public mailing list. The Mozilla thread is readable by anyone, as is the root-removal announcement that followed on October 20, taking the two eMudhra roots (emSign Root CA - C1, emSign ECC Root CA - C3) out of the trusted set. Every repair action — OneCRL entries within roughly three hours of the report, roots removed in three days, distribution packages within a week — was itself auditable, in public, in real time. This is the difference between auditability as a property of infrastructure and audit as a business process: the second produced nothing; the first produced a fix you can watch land.
The CVE issued for the incident, CVE-2026-111943, is a signal worth reading: an identifier assigned to a CA-side key-generation failure so that every ecosystem — language runtimes, operating systems, package maintainers — has a shared token for "remove this trust anchor." That is the shape of a working coordination process.
The cleanup that is actually yours¶
The browser path handles itself: updates flow, OneCRL entries land silently, roots are removed. Everything that does TLS trust decisions without a browser update channel is the homework, and it is short if you know your estate:
- Debian, Ubuntu and other distro-packaged OpenSSL: the
ca-certificatespackage now removes eMudhra's roots entirely. Update it — this is whatwget,curl,git,apt,nginxand every distro OpenSSL consumer on the box reads. The updated Debian package shipped October 21, replacing an interim stopgap. - Go programs: the standard library verifier ships with the platform's root set, so rebuild and redeploy anything that carries its own binary — golang/go#82345 tracks the update.
- Python programs:
certifiis the trust store most pip-installed applications actually use, and its advisory is the record. The part that makes this one sneaky: certifi ships bundled inside virtualenvs, so an application installed six months ago carries six-month-old trust.pip install -U certifiinside each venv is the fix and venv-by-venv is the loop. - NetBSD and friends: manual removal, exactly as published:
sh certctl untrust /usr/share/certs/mozilla/all/emSign_Root_CA_-_C1.pem certctl untrust /usr/share/certs/mozilla/all/emSign_ECC_Root_CA_-_C3.pem - Anything with its own trust store — appliances, LDAP-over-TLS services, internal tooling pinned to specific bundles — gets enumerated and checked by hand. There is no OneCRL for the box on the shelf, and the appliance inventory lesson in this week's php2shell post is the same: admin surfaces and their trust stores are your perimeter.
Verify with this loop on every server-side box, and treat the browser test site (emudhra.we-have-a-problem.com) as the human-readable version of the same check:
sh
for i in dv-g3b-emudhra dv-g3c-emudhra ov-g3b-emudhra ov-g3c-emudhra \
ev-g3b-emudhra ev-g3c-emudhra dv-g3b-aaa-emudhra dv-g3c-aaa-emudhra \
ov-g3b-aaa-emudhra ov-g3c-aaa-emudhra ev-g3b-aaa-emudhra ev-g3c-aaa-emudhra
do printf '%s: ' "$i"; curl -sS "https://$i.we-have-a-problem.com"; echo " -> $?"
done
Any iteration that prints Your client successfully connected to the site. That's bad! — or exits 0 — is a box still trusting eMudhra's chain. That string is the whole point of running your own trust policy: the check is twelve lines, it is offline-posable, and it does not depend on anyone's dashboard being honest.
The standing lesson¶
Nobody in the Web PKI's design assumed intermediates would be bulletproof; the design assumes they will fail, and it names the remedy precisely. The system worked: a community member filed publicly, a revocation channel responded in hours, roots came out in days, distributions propagated the same week, and at no point did a private company get to decide the truth in private. That is the working form of trust: not confidence, but a repair process fast enough and public enough to absorb a failure this large without anyone being harmed. What did not work was the assurance layer everyone points at as the reason trust is justified — the audit — and that is the part of the story your infrastructure inherits every time you accept "it was audited" as a substitute for "here is the check you can run yourself." Run the loop on your boxes tonight; it is cheaper than trusting paperwork that did not stop a key named 4.