The eMudhra CA Keys Were One Byte Long. Does Your Container Still Trust Them?
Key takeaways
- Eight eMudhra intermediate CA private keys were tiny numbers such as 4, 56 and 115, so recovering them meant trying 1, 2, 3 and so on.
- Chrome blocked the intermediates through CRLSet within hours, and Mozilla's OneCRL reached standard Firefox in under three hours.
- Per Endor Labs, default TLS stacks in Python, Go, Node and most CLI tools do not check revocation, so only a trust store update removes the risk.
- certifi 2026.10.19 and Alpine ca-certificates 20261020-r0 carry the change, while Java should be treated as unconfirmed until your JDK vendor says otherwise.
- Rebuild base images on a schedule, for example weekly in CI with docker build --pull --no-cache, so trust store versions are something you can query.
Eight private keys behind eMudhra intermediate CAs turned out to be single-byte numbers, guessable in milliseconds. Chrome and Firefox blocked them the same day. Your base image, your Python bundle and your JVM keystore blocked nothing until someone rebuilt them.
TL;DR (for non-technical readers)
- A certificate authority, one of the organizations that vouch for websites being genuine, created eight signing keys so weak that anyone could have worked them out.
- Web browsers shut the door within hours. But servers, scripts, build systems and apps keep their own list of who to trust, and those lists do not update themselves.
- The risk for a business is that an attacker could impersonate a trusted service to your software, not to your customers’ browsers.
- Ask your tech team: “Where do our services get their list of trusted certificate authorities, and when was it last refreshed?”
What happened
The facts, from eMudhra’s statement, Scott Helme’s analysis and the dev.to summary:

- A researcher reported on October 17, 2026 that private keys of eight eMudhra intermediate CAs were compromised.
- Helme found the keys were not random 384-bit values. They were tiny numbers such as 4, 56 and 115, so the “search” is trying 1, 2, 3 and so on. Google’s wording is that the keys were generated with insufficient randomness. That is technically true and a big understatement.
- eMudhra says a flaw in the key wrap/unwrap step of a ceremony on April 30, 2025 produced a second, weak key set next to the strong one, and the weak set was what got published to root programs.
- eMudhra also says root keys were not affected, and that none of the eight intermediates issued a certificate for a real site. One was in production but had issued nothing.
- Chrome blocked the intermediates through CRLSet within hours. Mozilla’s OneCRL reached standard Firefox in under three hours.
- According to Google’s security blog, the emSign ECC Root CA - G3 is also being removed from the Chrome Root Store, rolling out October 27, 2026.
The gap

Browser -> fast distrust lane (CRLSet, OneCRL) -> blocked in hours
Debian/Ubuntu img -> ca-certificates package, baked in -> stale until rebuilt
Python (certifi) -> its own bundle -> stale until upgraded
Java (cacerts) -> JDK keystore -> wait for JDK vendor
Vendored (Go, Node, mobile, firmware) -> frozen snapshots -> depends on release
Per Endor Labs, certifi 2026.10.19 and Alpine ca-certificates 20261020-r0 carry the change, and Java should be treated as unconfirmed until your JDK vendor says otherwise. Their key point: default TLS stacks in Python, Go, Node and most CLI tools do not check revocation, so only a trust store update removes the risk.
What practitioners are saying
- Process failure. On LWN, one commenter asked how an HSM could ever produce a one-byte key, suspecting the problem runs deeper than one buggy script.
- Marketing versus reality. Another noted the CA advertised post-quantum and zero-trust language while shipping what amounted to one byte of entropy.
- Storage doubts. A third pointed out that eMudhra’s trouble verifying its own CRL signatures suggests keys were not held in HSMs the way they should be.
- A structural fix. One commenter proposed requiring HSM attestation for every root and intermediate, so key generation can be proven.
- The “it was contained” view. Not everyone saw an emergency. Because no certificate chaining to the six TLS intermediates had been logged in Certificate Transparency, browsers that require CT would not have accepted a forged one anyway. Helme’s counterpoint is that curl and Python happily accepted a forged chain in his proof of concept.
My take
I side with the people who say the CA is the story and the people who say the trust store is the lesson, and I think both are right at different layers.
The browser response was excellent and I do not want to undersell it. Hours, not weeks. But that speed only protects the one client type built for it. The honest framing is that the web PKI has a fast lane for browsers and a slow lane for everything else, and most backend traffic is in the slow lane.
Where I disagree with the “contained” view: no certificate being logged is not a guarantee about the future. The keys are public, so the only thing standing between your internal client and a forged cert is whether your trust store still lists the parent. That is something you can fix with a rebuild, so do it.
For teams, this argues for one boring policy: base images are rebuilt on a schedule, and trust store versions are something you can query, not something you hope about. It also supports CA diversity and short certificate lifetimes, because swapping a CA is much easier when you have practiced it.
Try it in 15 minutes
1. List what makes outbound TLS calls

Services, workers, CI runners, scripts. Ask “which trust store”, not “which version”.
2. Check the OS bundle in a running container
docker exec <container> sh -c 'ls -l /etc/ssl/certs/ca-certificates.crt; grep -c "BEGIN CERTIFICATE" /etc/ssl/certs/ca-certificates.crt'
docker exec <container> dpkg -l ca-certificates
docker exec <container> sh -c 'ls /etc/ssl/certs | grep -i emsign'
Compare the ca-certificates version with what your distro ships now (Endor lists Debian and Ubuntu fixed versions). A file date older than October 17 on a long-lived image means rebuild.
3. Check language bundles
python -c "import certifi; print(certifi.__version__, certifi.where())"
pip install --upgrade certifi
keytool -list -cacerts -storepass changeit | grep -i emsign
requests uses certifi unless you override verify=, so one machine can hold two different truths. For Java, changeit is the default cacerts password. The fix is a newer JDK build, not hand-editing the keystore.
4. See what a server presents
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
openssl x509 -in certificate.pem -noout -issuer -subject -fingerprint -sha256
If an issuer names an eMudhra or emSign intermediate, find out why and when it renews.
Make it stick
docker build --pull --no-cache -t myapp:$(date +%Y%m%d) .
Run that weekly in CI. Fresh base images carry fresh trust stores.
What to learn next
- eMudhra’s own incident statement, for the cause as the CA tells it.
- Scott Helme’s write-up, for how the keys were recovered and what non-browser clients accepted.
- The Chrome Root Program policy, to see why a root can be pulled on a date.
Question
If a CA you depend on were distrusted tomorrow, could you say in under ten minutes which of your services would still trust it?
Originally published on DEV. Discussion happens there.
Frequently asked questions
- How do I check if my container still trusts the eMudhra CA?
- Run docker exec <container> sh -c 'ls /etc/ssl/certs | grep -i emsign' and check the ca-certificates version with dpkg -l ca-certificates. A file date older than October 17 on a long-lived image means rebuild.
- Why do curl and Python still trust compromised CAs after browsers block them?
- Browsers have a fast distrust lane (CRLSet, OneCRL), but Python, Go, Node and most CLI tools do not check revocation. Their trust stores only change when you update or rebuild.
- How do I update the Python and Java trust stores?
- For Python, run pip install --upgrade certifi. For Java, the fix is a newer JDK build, not hand-editing the keystore.
- Were eMudhra's root keys affected?
- eMudhra says root keys were not affected, and that none of the eight intermediates issued a certificate for a real site. One was in production but had issued nothing.
About the author
Security Engineer & Technical Writer
Security engineer and technical writer based in Batam, Indonesia. I review and harden MCP servers and AI agents, build security automation, and write up what holds and what fails. I also train on DNS security as a PANDI .id Academy trainer.
Related articles
-
6 min read
PHP2Shell: The Patch Is Out. Is It Running in Your Containers?
PHP2Shell patches for CVE-2026-109618 and CVE-2026-109619 are out. Five checks to prove the fix is running in your containers, not just released.
- Security Engineering
- Deployment
-
6 min read
UUIDv7 Is a Great Database Key and a Bad Clock: 4 Tests Before You Migrate
UUIDv7 is a great database key but a poor clock. Four quick tests, what the embedded timestamp leaks, and a rule of thumb for using UUIDv7 as a primary key.
- Security Engineering
- Engineering Practice
-
10 min read
I Traced 29 CVEs in One MCP Server to 4 Root Causes
29 mcp-atlassian CVEs traced to 4 root causes: DNS rebinding, credential fallback, tool allowlist and file_path. The safe version floor is 0.23.1, not 0.22.0.
- MCP Security
- Security Engineering
All writing | RSS | Markdown