Menu

The Key Was 4: eMudhra, Certificate Authorities and the Padlock We Trust

By Saai Syvendra ·

If you work anywhere near tech, this week has been a lot.

On Wednesday, details of two critical PHP flaws (nicknamed PHP2Shell) leaked before they were meant to, and government websites around the world went offline while everyone rushed to patch. The same day, someone used stolen credentials to push a backdoored version of LiteLLM, a popular Python library for building AI apps, quietly stealing cloud and AI keys from anyone who installed it. State Bank of India confirmed that 375,000 customer records had leaked onto the dark web. TechCrunch's headline summed it up: "The internet is having a very bad cybersecurity day."

And somewhere in the middle of all that, there was a short message, sent a few days earlier to Google, Mozilla and the other browser makers:

"We have a problem here - Wayne"

This one wasn't a hack. Nobody broke in anywhere. It was a bug and an oversight. But the signature on that message was the scary part. Wayne had signed it with the private keys of eight certificate authorities belonging to eMudhra, one of India's largest certificate authorities. Those keys are supposed to be some of the most closely guarded secrets on the internet.

Wayne didn't need to steal them. One of the keys was the number 4.

Not a number starting with 4. Not 4 followed by a hundred random digits. Just... 4.

What happened: eMudhra's signing keys were tiny guessable numbers, so anyone could forge a trusted ID for any website. Browsers fixed it in hours, but Python, Node.js and others kept trusting it

Why should you care?

Those keys let you convincingly pretend to be any website. Anyone holding one could create a certificate for google.com, your bank or your email, and most software would believe it. Sit on the same café Wi-Fi as you, and they could read everything you send, passwords included, while your browser told you the connection was secure.

So who did it hit?

  • Everyone with a browser, in theory. Chrome, Firefox, Safari and Edge all trusted these keys until last week. They were patched within hours though, so if your browser is up to date, you're fine.
  • Every developer and every server, in practice. Python, Node.js, Go, Java, curl and Docker images carry their own copy of the trust list, and they didn't get those overnight patches. These are the ones still exposed.
  • eMudhra itself. Its shares dropped about 20% in a day, and five days after the report, its board decided to exit the global website certificate business altogether. Its customers get free replacement certificates from partner CAs, while its digital signature and eSign services carry on.

The keys had been weak since April 2025. That's eighteen months. There's no evidence anyone abused them, but nobody can prove that nobody did either.

When this broke, I saw a lot of people confused about what the problem actually was. The headlines said Python and Node.js were affected, so plenty of people assumed Python and Node had been hacked. They weren't. The real problem sat a few layers below, in how the internet decides who to trust, and that's something I learned about at university. So I thought, why not explain what actually happened, and why it spread the way it did. Let's start with something you see every day without noticing.

That little padlock

Look at the address bar at the top of your browser, right now. Just to the left of the website's address there's a small icon. In most browsers it's a padlock (newer versions of Chrome swapped it for an icon that looks like two little sliders). Click it, and you'll see something like "Connection is secure". The address itself starts with https://, and that s stands for secure.

So what is it actually promising? When you type yourbank.com, your request hops through your router, your ISP, maybe a café's Wi-Fi. Any of them could pretend to be your bank. HTTPS encrypts the conversation, but encryption alone isn't enough. A perfectly encrypted chat with an impostor is still a chat with an impostor.

So the real question that padlock answers is: am I actually talking to yourbank.com?

The answer comes as a certificate, a small file the website hands your browser saying "I'm yourbank.com, and here's my public key." Anyone can make one of those though. What makes it believable is who signed it.

Certificate Authorities: the passport office of the internet

Think of a passport. You believe it's really mine not because I say so, but because a government you trust issued it, and it's hard to forge.

A Certificate Authority (CA) plays that role for websites. It checks that whoever is asking for a certificate for yourbank.com actually controls yourbank.com, then signs it. Let's Encrypt, DigiCert, Sectigo and eMudhra are all CAs.

But a CA doesn't sign everything with its most important key. There's a chain:

The chain of trust, from root CA to intermediate CA to website certificate
  • The root CA sits at the top. Its private key is kept offline, often in a literal vault, and only comes out on special occasions. The public half ships with your device, in what's called the root store. Your phone has around 150 of these.
  • The root signs a few intermediate CAs, which do the day-to-day signing. Like regional passport offices, so the government doesn't have to stamp every passport itself.
  • The intermediate signs the website's certificate.

Your browser walks that chain upwards. If it ends at a root in your root store, the whole chain is trusted.

This is the bit to hold on to: an intermediate is trusted exactly as much as its root. Whoever holds an intermediate's private key can sign a certificate for any website, and every device that trusts the root will accept it.

eMudhra's eight broken keys were intermediates.

Keys, quickly

Signing works with a pair of keys. The private key creates signatures and must stay secret. The public key checks them, and it's printed right inside the certificate for anyone to read. The maths makes it easy to go from private to public, and practically impossible to go back.

For the kind of key eMudhra used (P-384, an elliptic curve key), the private key is just a number. A random number somewhere between 1 and a 116-digit monster. So how do you "break" one? There's no clever trick. You guess. Pick a number, work out its public key, compare it with the one in the certificate, repeat.

With a properly random key, you'd never get there. And I mean never.

There are roughly 1080 atoms in the observable universe. Every star, every planet, every grain of sand, every person, all of it. The number of possible P-384 keys is about 10115. That's not a little bigger. It's about 1035 times bigger, as in a 1 followed by 35 zeros. Write one key on every atom in the universe, and you'd need another 1035 universes' worth of atoms before you ran out of keys.

Still too abstract? Turn every single atom in the universe into a computer. Have each one guess a billion keys a second, starting at the Big Bang, 13.8 billion years ago. Today, all of them together would have about a one in a billion chance of having found the key.

That's what a private key is supposed to be, which is why nobody bothers attacking the maths. They go after the randomness, the storage or the people instead.

With eMudhra's:

All eight keys turned out to be whole numbers below 256: 4, 56, 66, 115, 155, 156, 230 and 237. Security researcher Scott Helme recovered every one of them on a laptop in under a second.

What your browser checks, every time

Every time you open an HTTPS site, your browser runs through something like this before the page even starts loading:

  1. Was the site's certificate signed by the intermediate?
  2. Was the intermediate signed by a root in my store?
  3. Does the name match, and is it still in date?
  4. Has it been revoked? Is it in the public Certificate Transparency logs?

A certificate forged with a leaked intermediate key sails through the first three. Its signature is genuine, after all. Only the fourth one catches it.

Certificate Transparency (CT) logs are public, append-only lists of every certificate a CA issues. Chrome, Safari, Edge and Firefox refuse certificates that aren't logged. So to fool a browser, an attacker's fake would have had to show up in a public log where the whole world could see it. None did, which is how the browser makers could say browsers were never attackable.

Revocation is the other half. Browsers keep their own blocklists (Chrome calls its one CRLSets, Firefox has OneCRL) that update quietly in the background, no restart needed. Chrome got the report at 18:05 UTC on the 17th and had the block live by 19:45. Firefox followed the same night, Apple on the 19th and Microsoft on the 20th.

So how did this happen?

First things first: nobody at eMudhra sat down and wrote privateKey = 4. No engineer in their right mind would, and that's not what happened.

eMudhra created these keys at a key ceremony on the 30th of April 2025. That's a formal, audited and recorded event where CA keys are generated inside Hardware Security Modules (HSMs), tamper-resistant boxes built to create keys and never let them out. And the real keys came out fine. Strong, random, exactly what they should be.

The trouble was one extra step. According to eMudhra's own root cause statement, that day's ceremony included an approved change to test wrapping and unwrapping the keys, basically packaging them up securely so they could be exported. A flaw in that script quietly created a second pair of keys on the HSM next to each real one, and that second pair was weak. The two had similar labels. When it came time to build the certificate requests, the weak pair got picked by mistake. The root signed them, Sectigo cross-signed them, and off they went to every device in the world.

The strong keys were sitting right there the whole time. eMudhra has been upfront that "there were human errors in multiple steps of the process", and has said it will re-verify every key ceremony it has ever done.

So this is less "a CA that didn't understand randomness" and more a buggy test script plus a mix-up between two similarly named things. Honestly, most of us have shipped the wrong config at some point. The difference is the stakes.

The bit that sticks with me: the hardware was certified, the ceremony was audited, and the CA passed independent audits two years in a row. None of it checked the final output, whether the key that actually got signed was the strong one. Nobody looked at the key itself until Wayne did, eighteen months later.

There was even an early warning sign. A CA regularly publishes a revocation list, a signed list of the certificates it has cancelled. Back in 2025, eMudhra signed those lists with the real, strong keys, and they failed to verify, because the certificates out in the world held the weak keys. That mismatch was the clue. Instead of digging into why, the lists were re-signed with the weak keys, and the error went away.

eMudhra says most of the eight were never used in production, and that it found no sign of them issuing certificates to real websites.

What one of those keys could have done

An attacker on the same Wi-Fi presents a forged yourbank.com certificate. Browsers block it, but scripts and servers accept it

Six of the eight could issue TLS certificates, the website padlock kind. That's the café Wi-Fi attack above.

The other two were for code signing. That's the signature your operating system checks before installing software, the thing that says "Verified publisher". Malware signed with one of those would have looked like it came from a legitimate company.

The part nobody talks about: your code isn't a browser

This is where it gets interesting for developers, and honestly the reason I wanted to write this.

Your browser isn't the only thing on your computer making HTTPS connections. Your Python script calling an API. Your Node server talking to Stripe. pip install, npm install, curl. Every one of them verifies certificates too. But which root store do they use?

Often, their own.

  • Python's requests doesn't use your operating system's list. It uses certifi, a package that is literally a copy of Mozilla's root list in one file, cacert.pem.
  • Node.js compiles Mozilla's root list into the node binary itself. Updating Chrome does nothing for it. The only fix is a new Node version.
  • Java ships its own cacerts keystore inside the JDK.
  • Go had to hard-code a block for these eight keys straight into its standard library, so your Go binaries need rebuilding with the new version.
  • Docker images copy the trust list in when they're built. Patch your server, and your containers still carry the old list until you rebuild them.

Why bake the list in at all? Portability. Your Node app behaves the same on Ubuntu, Alpine and Windows, no matter what the operating system has. The trade-off shows up in weeks like this one.

And here's the kicker. Almost none of these do check number 4. Python, Node, Go, Ruby and most command-line tools don't check revocation or CT logs by default. They build the chain, see a root they trust, and connect. eMudhra revoking the certificates means nothing to them. The fix only reaches them when the list inside them changes, which means someone has to update it. Even the curl that comes with macOS was still trusting the chain days later.

What should you do?

Not a developer? Update your browser, phone and computer. That's it. You were most likely protected before you even heard about this.

A developer? Update the trust lists you didn't know you had: newer versions of Node.js, Python's certifi (and pip), Go, your Linux distro's ca-certificates package, and rebuild any Docker images made before the fix. Rather than me listing version numbers that will go stale, the disclosure site, emudhra.we-have-a-problem.com, keeps track of the fixes and workarounds for each platform. It also has test servers you can point your tools at, to see whether they still trust these keys.

Wrapping up

The whole system of trust on the web rests on a few hundred organisations keeping a few numbers secret, and random. This time, one of them was 4.

And the fix wasn't some emergency invention either. Revoking certificates has been part of the process from day one. Keys get lost, CAs make mistakes, and the system was built expecting that: cancel the bad certificate, tell everyone, move on. Browsers just got very good at the "tell everyone" part. Our scripts, servers and containers, not so much.

We live in a world with so many layers of abstraction that we don't know, and honestly don't need to know, what happens in the background. We type an address, we see a padlock, and we move on. Behind it sits an enormous amount of trust we've quietly placed in companies most of us have never heard of. Most days, that's completely fine. It's only when something like this happens that you realise just how much is resting on it.


#Security
#PKI
#HTTPS
#eMudhra

More from My Space