In-depth articles on technology shaping what comes next.

eMudhra's Weak CA Keys: 5 PKI Myths, Busted

A trusted CA shipped private keys crackable in milliseconds. We test five beliefs about PKI, key generation and auditing against the eMudhra incident.

A massive open vault door secured only by a tiny toy padlock
Seventeen months of certificate authority security rested on eight bits of entropy.

One of eMudhra's certificate authority private keys is the number 4. Not a 384-bit number that starts with 4 — just 4, with 47 zero bytes padding it out to look respectable. This is a certificate authority key generation failure of a kind I'd have told you was impossible, and it survived audits, cross-signing by Sectigo, and seventeen months in production-adjacent infrastructure before anyone tried the obvious: brute-forcing a single byte. Here are five beliefs about PKI that this incident kills stone dead.

Myth 1: "A structurally valid certificate is a safe certificate"

Every automated check in the pipeline said these certificates were fine. And they were right, in a narrow and useless sense. The public key corresponding to d = 4 is a perfectly valid point on the P-384 curve. The ASN.1 encoding parses. The signature verifies. The chain builds. Every linting tool, every structural validator, every compliance checklist that walks a certificate and confirms it is well-formed will wave these through, because they are well-formed.

That's the trap. Structural validation answers the question "is this a correctly formatted certificate?" It does not answer "was the key inside generated correctly?" For an elliptic curve key, those are almost completely independent properties. The private key is just a scalar d; the public key is d × G, where G is the curve's generator point. The whole security argument is that recovering d from the public point requires solving the discrete logarithm problem — but only if d was actually chosen from the full 384-bit space. Choose d from 8 bits instead and the "impossible" problem becomes a loop that runs at most 255 iterations.

Nothing about the resulting certificate hints at this. The public point derived from d = 4 looks exactly as random as one derived from a properly generated key. You cannot eyeball it. You cannot lint it. The only way to know is to try small values and known-weak patterns — which is precisely what happened: the reporter ran Google's paranoid_crypto test suite against the Common CA Database corpus, out of the box, and got a hit. A check that takes milliseconds on a laptop, against keys that anchor trust for real browsers.

So the first lesson: validity is necessary and nowhere near sufficient. If your PKI testing stops at "the certificate parses and chains," you are testing the envelope and ignoring the letter.

Myth 2: "Baseline requirements and audits catch key-generation bugs"

CAs live under the CA/Browser Forum Baseline Requirements and get audited against WebTrust or ETSI criteria. There's ceremony documentation, HSM requirements, key length requirements. eMudhra's keys were the right length — 384 bits on paper. The audit gap is that compliance verifies the representation of the key, not the process entropy that produced it. A key padded with 47 zero bytes still occupies 48 bytes on disk.

According to eMudhra's own account, posted to Mozilla's incident ticket, the root cause reads like a dark comedy: a bug in the ceremony script generated a second, weak key pair on the HSM; the two pairs had similar names; a human picked the wrong one for the CSR. Three separate failures — a script bug, a naming collision, a selection mistake — and at no point did any control ask "does this key actually have the entropy we paid the HSM for?"

This is the part that should worry every engineer who runs their own internal PKI. If a publicly trusted CA, with root programs breathing down its neck and audits on the calendar, can ship a private key equal to 4, what's inside your Vault PKI backend or your Active Directory Certificate Services templates? The fix is cheap and there's no excuse not to run it. After generating any key, and before signing anything with it, run it through paranoid_crypto or at minimum a trivial sanity check:

from cryptography.hazmat.primitives.asymmetric import ec
CURVE_ORDER_BITS = 384
def sanity_check_private_key(key: ec.EllipticCurvePrivateKey) -> None:
d = key.private_numbers().private_value
# Reject keys whose effective entropy is far below the curve size.
# A healthy P-384 key should have a bit length close to 384.
if d.bit_length() < CURVE_ORDER_BITS - 32:
raise ValueError(
f"Suspiciously small private scalar: {d.bit_length()} bits"
)
# Also reject known bad values outright.
if d < 2**16:
raise ValueError("Private scalar is trivially brute-forceable")

Thirty seconds of work. It would have caught every one of eMudhra's eight keys. Audits verify that processes exist; they do not verify that processes worked on any given Tuesday in April. Only output testing does that.

Myth 3: "A compromised CA key can be contained"

When a CA key is "compromised" in the usual sense, there's a thief. Someone exfiltrated the key at a particular moment, and you can reason about the blast radius: certs issued after the theft, by parties with the stolen key. Ugly, but bounded. We've written before about what happens when an intermediate CA compromise snaps a trust chain, and the standard playbook — revoke, distrust, reissue — assumes a before and an after.

This incident has no before and after. The key was effectively public from the moment the certificate was created, because anyone who ever looked at the certificate could recover it in milliseconds. There is no theft event to bound. The window is the entire lifetime of the certificates — April 2025 onward — and there is no way to know who, if anyone, walked through the open door. eMudhra's statement that the intermediates issued no end-entity certificates, corroborated by Certificate Transparency logs, is genuinely reassuring for what those CAs did. It says nothing about what anyone holding d=4 could have done opportunistically on a network path.

Worse, the response itself can't fully land. Chrome and Firefox blocked the intermediates within hours via CRLSet and OneCRL, and both require CT logging, so forged TLS certs would never have validated in those browsers anyway. Good. But that protection ends at the browser boundary, which brings us to the next myth.

Myth 4: "If the browsers are safe, the ecosystem is safe"

A glowing armored browser window surrounded by hundreds of exposed connected devices in the dark
Browsers got patched in hours; everything else in the TLS ecosystem is still exposed.

Browsers are the best-case scenario in the TLS ecosystem, and they're a minority of the TLS clients that matter to you operationally. Think about what actually makes HTTPS connections in your infrastructure: curl, Python's requests, Node, Go binaries, Java services, git, package managers, cloud SDKs. These clients share three traits that stack badly here: they trust a CA bundle on disk, they don't check revocation by default, and they don't check Certificate Transparency. All three protections that saved Chrome users are absent.

Scott Helme demonstrated this concretely: with a forged certificate for api.stripe.com chaining to one of the weak eMudhra intermediates, default curl reported SSL certificate verify ok, Python completed the handshake, Node reported the connection authorized, and Windows CryptoAPI accepted the chain. On every Linux and macOS box tested. A forged cert for any hostname, accepted by the default tooling your deployment scripts and CI runners are built from. That's not a browser problem; it's a your-infrastructure problem.

The exposure map is worse than most people picture:

  • curl | sh installers and self-updaters. rustup, nvm, the Homebrew installer, countless "install.sh" pages. A forged cert here means running attacker code, frequently as root.
  • CI/CD pipelines. Runners fetch dependencies and push artifacts while holding cloud credentials and signing keys, often on stale base images.
  • Server-to-server traffic. Payment webhooks, OAuth token exchanges, cloud SDK calls carrying secrets in headers.
  • IoT and embedded devices. Frozen firmware with CA bundles that won't change until the certificates expire — in July 2039.
  • Windows code signing. Two of the eight weak keys belonged to code-signing CAs; malware signed with them looks like software from a legitimate publisher.

The actionable advice is boring and correct: update everything that ships a CA bundle. Your OS ca-certificates package, certifi, Node, Homebrew — and rebuild your container images once base images pick up the distrust, because layers remember. The un-actionable part is the device you can't update. That smart TV will trust a key equal to 4 for another decade and a half. If you needed one more argument for network-segmenting your IoT gear, this is it.

Myth 5: "Someone must already be checking this"

The most quietly damning line from the incident came from the reporter: the discovery came from running paranoid_crypto against the CCADB corpus as a quick corpus test before moving on to subscriber certificates. The low-hanging fruit. The reporter's own words: "Most of my time has been spent going over the basics that everyone assumed someone else had already done — a critical mistake to ever make near security."

Read that again. Eight CA private keys — the crown jewels of a publicly trusted authority, cross-signed into Sectigo's AAA Certificate Services root — sat brute-forceable for seventeen months, and they were found not by an auditor, not by a root program, not by the CA itself, but by one person running an open-source tool out of the box. The ecosystem collectively assumed the basics were covered. Nobody covered the basics.

This is a broader pattern in security infrastructure, and it's why I keep banging on about defensive engineering — systems that resist catastrophe rather than systems that assume correct operation. The checks that would have caught this are almost embarrassingly cheap:

  1. Run paranoid_crypto or equivalent weak-key checks on every key at generation time and on every CSR before signing.
  2. Verify entropy empirically: check the private scalar's bit length and reject anything with implausibly low effective entropy, as in the code sample above.
  3. Test the ceremony, not just the config. Dry-run your key ceremonies and inspect the actual artifacts produced, including which named HSM object ends up in the CSR.
  4. Continuously scan your issued certificates, not just your templates. The certificate is the ground truth; everything upstream is intention.

The certificate is the ground truth. Everything upstream — scripts, ceremonies, audits — is just intention.

Eight Bits Is All It Takes

There's something almost comforting about a catastrophe with a silly cause. A script bug, two similarly-named keys on an HSM, a human picking the wrong one — no nation-state, no novel cryptanalysis, just a Three Stooges episode with a seventeen-month fuse. But the silliness is the warning. If the weakest link in a publicly trusted CA can be a single byte, then the strength of your PKI isn't set by your curve choice or your HSM budget. It's set by whether anyone, anywhere in the pipeline, actually tested the output.

The browser vendors did their part within hours. The rest is on us: audit your own key generation, run the cheap checks, update every CA bundle you can reach, and segment the devices you can't. And stop assuming someone else already checked the basics. Near security, that's the most expensive assumption there is.