6 minute read

On October 17 2026, reports started circulating about some compromised private keys for several intermediate TLS certificates issued by a certificate authority. Before then, you probably hadn’t heard of eMudhra before. I certainly hadn’t.

Eventually, it was clarified that the keys hadn’t been hacked or stolen, they were just extremely weak. And somehow, one of them was the number $4$. 🤯

What does that even mean? TLS certificates and keys are part of a complicated cryptographic scheme called Elliptic Curve Cryptography. How could a key be a single digit number? How does that even work? And if it was indeed a single number, how is it that nobody noticed?

In this article, inspired by a recent, and related, CTF challenge, I’m going to dive into this. We’ll talk about how these certificates, keys, and cryptography all work, and how a key can actually be the number $4$. We’ll see why it’s so hard to notice when a private key is weak. And we’ll learn about an algorithm that can be used to brute force a bunch of options for private keys, without actually having to try them all.

Let’s dive in then, shall we?

Elliptic Curve Primer

First of all, in order to understand what happened, we need to have a bit of an understanding of Elliptic Curve Cryptography (ECC). I won’t go into great depth here, just the high-level basics that are needed in order to understand this issue. If you want to dig into ECC a bit deeper, check out my previous article on the topic.

The important thing to understand in our case is the so-called Elliptic Curve Discrete Logarithm Problem (ECDLP). This is the “mathematically hard” problem that is the basis of ECC. The idea is as follows:

  • We first define a curve that we’re going to use. This is usually defined by an equation something like $y^2 = x^3 + ax + b$.
  • We define mathematical operations on points of this curve. This allows us to do things like add points ($P + Q = R$) and multiply a point by a scalar ($k * S = T$).

I’m not going to get into how point addition and multiplication work (see my previous article if you want to go into more depth). But the operations have some important properties:

  • They “behave nicely”. $P + Q = Q + P$ and $P + P = 2P$, things like that.
  • Trying to “undo” multiplication is hard.

That second point is critical. Say we have a base point $G$ (sometimes called the Generator) and we multiply it by some scalar $d$ in order to get a new point $Q$.

\[Q = d * G\]

Even if we know both $G$ and $Q$, there is no known method to efficiently determine $d$. This is the ECDLP.

In public key crypto systems using ECC, we can use this to generate a key pair. First, we need to agree upon curve parameters and a generator point $G$. There are many standards which specify all of this, including NIST’s P-384. Then, we pick a random integer $d$ and calculate $Q$ using the above equation. Now $d$ is our private key and $Q$ is the corresponding public key.

So yes, the private key is actually a single integer. And yes, it could technically be the number $4$.

Let’s see some of this in action. Under the P-384 standard, what if I just tried a few small values of $d$? What would it look like? Here are some values you would see:

Point G
x = 26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087
y = 8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871

Point 2*G
x = 1362138308511466522361153706999924933599454966107597910086607881313301390679204654798639248640660900363360053616481
y = 21933325650940841369538204578070064804451893403314136885642470114978241170633179043576249504748352841115137159204480

Point 3*G
x = 1150902892488483458936980703033240421996917307006362560128741616924334451190275252566486993672663650518120360101937
y = 31026896179475916024120184231935979451435829475766013456640577102369059008378103381483562093497986423351518602141169

Point 4*G
x = 3002719260124138234864644293005553694107074621611323149430212532410635475904301642043243286192603916432052661917749
y = 31212625517239818344942812004487160470862995565736342725949902243255222118943617085729083408956085866876436550315367

Point 5*G
x = 2750097886727221852871437489830375535902219297598792969027200942344246907638882141082920203987932105239591664846012
y = 22109872175543161315473274033501382113217372000035529196852092508530348707813916169793617815529384830733840621334842

To verify the numbers yourself, install PyCryptodome (pip install pycryptodome) and run the following:

>>> from Crypto.PublicKey import ECC
>>> key = ECC.generate(curve='p384')
>>> G = key._curve.G
>>> G.xy
(Integer(26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087), Integer(8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871))
>>> (2 * G).xy
(Integer(1362138308511466522361153706999924933599454966107597910086607881313301390679204654798639248640660900363360053616481), Integer(21933325650940841369538204578070064804451893403314136885642470114978241170633179043576249504748352841115137159204480))
>>> # etc...

The first thing to notice is that even with very small values of $d$, the public key $x$ and $y$ coordinates don’t look unusual. They are just big numbers, and they could just as easily have been generated by multiplying $G$ by a very large number. There really isn’t a way to tell what $d$ is, other than guessing values.

You’ll also notice that $4*G$ in included in the examples above. Is that the actual public key of one of the eMudhra certificates? Let’s have a look!

eMudhra Certificates

We can download the compromised certificates here. Taking a look at the EM-OV-TLS-CA-G3C.crt.pem file, we can extract its public key:

% openssl x509 -in EM-OV-TLS-CA-G3C.crt.pem -noout -pubkey | openssl ec -pubin -noout -text
Public-Key: (384 bit field, 192 bit security level)
pub:
    04:13:82:51:cd:52:ac:92:98:c1:c8:aa:d9:77:32:1d:
    eb:97:e7:09:bd:0b:4c:a0:ac:a5:5d:c8:ad:51:dc:fc:
    9d:15:89:a1:59:7e:3a:51:20:e1:ef:d6:31:c6:3e:18:
    35:ca:ca:e2:98:69:a6:2e:16:31:e8:a2:81:81:ab:56:
    61:6d:c4:5d:91:8a:bc:09:f3:ab:0e:63:cf:79:2a:a4:
    dc:ed:73:87:be:37:bb:a5:69:54:9f:1c:02:b2:70:ed:
    67
ASN1 OID: secp384r1
NIST CURVE: P-384

Now, if we look at $4*G$ above and convert the coordinates to hex, we get the following:

>>> x = 3002719260124138234864644293005553694107074621611323149430212532410635475904301642043243286192603916432052661917749
>>> y = 31212625517239818344942812004487160470862995565736342725949902243255222118943617085729083408956085866876436550315367
>>> hex(x)
'0x138251cd52ac9298c1c8aad977321deb97e709bd0b4ca0aca55dc8ad51dcfc9d1589a1597e3a5120e1efd631c63e1835'
>>> hex(y)
'0xcacae29869a62e1631e8a28181ab56616dc45d918abc09f3ab0e63cf792aa4dced7387be37bba569549f1c02b270ed67'

You can see that the public key from the cert is the hex value 04, followed by the value of x (13:82:51:cd:...) and then y (ca:ca:e2:98:...; starting on the 4th line). We can do a similar exercise to verify the values of all of the other certificates as well.

So now we understand what it mean for a private key to be $4$, or $55$, or whatever. And we can see how to verify those values by multiplying $G$ by $d$ to get $Q$ and comparing that with the certificate public key. There is just one piece of the puzzle left: given a certificate with a weak key, how do we crack it?

Cracking the eMudhra Certificates

The obvious answer is just to brute force a bunch of values of $d$. Start by trying $1$, then $2$, then $3$, and so on.

But there is actually a better way: the Baby Step / Giant Step Algorithm.

Perhaps I’ll write more about this in a future post, because it’s fascinating. Let’s say you have a certificate and you want to check to see if the key is any value less than $1$ million (fun fact, you would only have to search as high as $300$ to get all of the eMudhra keys!). As mentioned, one way to do this would be to try every single key between $1$ and $1,000,000$, meaning that you need to perform $1$ million point multiplications. This is an algorithm on the order of $O(n)$, meaning that it is roughly the same size as the problem space.

However, the Baby Step / Giant Step Algorithm reduces this to $O(\sqrt{n})$ instead, by using some clever (but simple) math and an efficient lookup table. This means that in order to check $1,000,000$ private keys, the number of point multiplications is closer to to $1000$ rather than $1,000,000$. This allows you to check a lot more values in less time than the naive approach.

If you’re interested in trying this out for yourself, I wrote a small Python script that implements the Baby Step / Giant Step Algorithm and checks the eMudhra certificates for weak keys. You can find it here.

Summary

Hopefully this has somewhat demystified what is going on under the hood with the eMudhra certificates. Although private keys for P-384 are supposed to be large 384-bit numbers, small numbers are also mathematically valid, and it is hard to tell whether such a number has been used by looking at the public key. Even though there is no efficient algorithm for solving ECDLP, the Baby Step / Giant Step Algorithm lets us guess and check $n$ private key values in $O(\sqrt{n})$ time, making it trivial to crack the weak private keys of the eMudhra certificates.

Updated: