eMudhra CA keys leaked: forged HTTPS certs still pass in old Go, Python, Node.js and Java (CVE-2026-111943)

eMudhra CA keys leaked: forged HTTPS certs still pass in old Go, Python, Node.js and Java (CVE-2026-111943)

How to fix CVE-2026-111943: upgrade Go to 1.27.3 or 1.26.10 and rebuild, then update certifi to 2026.10.19, Node.js to 22.23.4, 24.22.1 or 26.12.1, and your ca-certificates. Eight eMudhra intermediate CA keys can be recovered from the public certificates, so non-browser clients will accept forged HTTPS for any site.

7 min read1,501 words
Contents

Anyone who can sit in the middle of a connection can now mint a trusted HTTPS certificate for any site, and old Go binaries, Python bundles, Node.js runtimes and Java trust stores will accept it. Eight intermediate certificate authorities run by eMudhra (the emSign brand) were created in April 2025 with private keys so weak that they can be recovered from the public certificates in seconds. A researcher proved control of all eight keys to the browser root programs on October 17, 2026, and the details went public on October 20. Chrome, Firefox, Safari and Edge already block these intermediates. Most software outside the browser does not, until you update it and rebuild. In Go this is CVE-2026-111943.

This is our operator read of the Mozilla incident bug, the Chrome Root Program post, the Mozilla security blog, Go issue 82345, the certifi advisory, the Node.js October 21 security release, eMudhra's statement and the disclosure tracker at emudhra.we-have-a-problem.com.

How to fix

Update every trust decision that does not come from a browser, then rebuild and restart. The Go fix lives inside the compiled binary and the Python and Node fixes live inside bundled certificate files, so installing a new toolchain on a laptop changes nothing in production until the images are rebuilt.

What you runFixed inWhat the fix does
Go (crypto/x509)1.27.3, 1.26.10Denylist of the eight compromised public keys. Go 1.25 and older get no fix.
Python certifi (requests, httpx, pip and many SDKs)2026.10.18 or later (2026.10.19 is current)Drops the emSign ECC Root CA - G3 root
Node.js22.23.4, 24.22.1, 26.12.1Rejects chains containing the blocked keys and moves the bundled roots to NSS 3.131.1
Mozilla NSS3.131.1Removes emSign ECC Root CA - G3
Debian / Ubuntu / Alpine / Fedora ca-certificatesSee your distro advisoryUpdated system trust store
Java (OpenJDK cacerts)No JDK release yetManual step below
# Go: upgrade the toolchain, raise the floor in go.mod, rebuild every service
go install golang.org/dl/go1.26.10@latest && go1.26.10 download
go mod edit -go=1.26.10 -toolchain=go1.26.10   # or 1.27.3
docker build --pull -t my-service .            # make sure the builder image is golang:1.26.10 or 1.27.3

# Python
pip install -U 'certifi>=2026.10.19'

# Node.js: move to 22.23.4, 24.22.1 or 26.12.1, rebuild images, then restart
# (Node notes that TLS sessions resumed from before the upgrade are not re-verified)

# Debian / Ubuntu base images and hosts
apt-get update && apt-get install --only-upgrade ca-certificates

Check what is actually running

# Which Go toolchain built this binary? Anything below go1.26.10 still trusts the keys.
go version ./my-binary

# Which certifi does this environment load?
python -c "import certifi; print(certifi.__version__, certifi.where())"

# Which Node is in the container?
node -p process.version

# End-to-end test against the disclosure test host. A certificate error is the good result.
curl -sS https://dv-g3b-emudhra.we-have-a-problem.com/ >/dev/null && echo "STILL TRUSTS" || echo "rejected"

Run the curl line from inside your real containers and CI runners, not only from your laptop. The tracker serves twelve test hosts, one for each chain path, and documents them on its front page.

Java needs a manual step

The JDK reads its own cacerts, does not check revocation by default, and does not enforce Certificate Transparency. None of the browser blocklists reach it. BellSoft tested the JVM against all twelve test chains and recommends removing both roots the bad intermediates chain to:

keytool -delete -cacerts -storepass changeit -alias "emsigneccrootcag3 [jdk]"
keytool -delete -cacerts -storepass changeit -alias "comodoaaaca [jdk]"

Test the services you call first. Removing AAA Certificate Services can break old legitimate chains. A narrower option is listing the sixteen certificate fingerprints in $JAVA_HOME/lib/security/blocked.certs. A JDK upgrade replaces both files, so repeat the step after every update until vendors ship the change.

If your own site uses an eMudhra certificate

Chrome is removing emSign ECC Root CA - G3 from its root store on October 27, 2026, and Mozilla has removed it from NSS. eMudhra says about 30,000 certificates need to move, about 97% of them issued through ACME. If your server chain ends at that root, replace the certificate now from another CA:

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -E '^ *[si]:' | grep -i emsign

What went wrong

At an April 30, 2025 key ceremony, eMudhra created 24 intermediates. According to Mozilla's analysis in the bug, the eight P-384 keys in the B and C series used private keys that were integers below 256. That is effectively a key of all zeros except the last byte. Anyone can find it by generating the first 256 P-384 keys and comparing public keys. Mozilla recovered the keys independently within two hours of the report, and noted that a local AI agent did it on the first try with a one-line CTF prompt.

eMudhra's account in the bug: an approved deviation to test a key wrap/unwrap step created a second, weak key pair on the HSM, the weak keys had similar labels, and they were used by mistake for the certificate requests. Those same requests went to Sectigo, which cross-signed all eight under its old AAA Certificate Services root. So every bad intermediate exists twice, with two different roots above it.

Six of the eight are TLS issuing CAs (EM DV, OV and EV TLS CA, G3B and G3C). Two are code-signing CAs trusted by Microsoft. eMudhra revoked its eight certificates on October 18 between 09:24 and 09:39 UTC. Sectigo revoked the cross-signs on October 19.

Why the root removal matters more than the revocation

Revocation only helps clients that check it. Browsers got real-time blocks: Chrome pushed a CRLSet blocking the keys at 19:45 UTC on October 17, Firefox finished its OneCRL push by 22:59 UTC the same day, Apple followed on October 19 and Microsoft on October 20. Most HTTP clients in Go, Python, Node, Java, curl and Rust never read those lists. On Linux the trust store is a PEM file with no place to mark a single intermediate as bad. That is why Go hardcoded a denylist, why certifi and NSS dropped the whole root, and why Filippo Valsorda wrote in the Go issue that "the keys are compromised and their certificates revoked" and there was no other way to reach Linux applications.

Our read on the cross-sign: removing the emSign root alone is not a full fix for every client. A trust store that still holds AAA Certificate Services can build the second path. Chrome and Mozilla dropped that root years ago, but Apple's and Microsoft's stores still carry it, and so do many JDK cacerts and macOS's /etc/ssl/cert.pem. Go's denylist matches on the public key, so it blocks both copies of each intermediate. Check which kind of fix your stack received.

Who is not in scope

  • Up-to-date Chrome, Firefox, Safari and Edge users. They had a block within hours, and these browsers enforce Certificate Transparency. Chrome and the tracker both say no certificate from these intermediates was logged before the block, so a forged certificate would have been rejected.
  • Certificates from eMudhra's RSA hierarchy (emSign Root CA - G1). Mozilla is not removing that root, and certifi 2026.10.19 still ships it.
  • eMudhra's India DSC and eSign services, which eMudhra says run on separate infrastructure.

What this is not

It is not a breach of eMudhra's servers and not a bug in Go, OpenSSL or Python code. It is a CA key-generation failure. Nobody has shown a forged certificate used against a real target yet, but Mozilla says several people recovered the keys on their own, and a forged certificate works against any client that skips Certificate Transparency and revocation. That covers most server-side code.

Timeline (UTC)

  • 2025-04-30: eMudhra key ceremony creates the eight weak intermediates.
  • 2025-05-14: Sectigo cross-signs them under AAA Certificate Services.
  • 2026-10-17 18:05: Researcher reports proof of key control for all eight to Apple, Chrome, Microsoft, Mozilla and eMudhra.
  • 2026-10-17 19:45: Chrome CRLSet blocks the six TLS keys. Firefox OneCRL push completes at 22:59.
  • 2026-10-18 09:24 to 09:39: eMudhra revokes all eight with reason caCompromise. certifi 2026.10.18 drops the emSign ECC G3 root at 16:21.
  • 2026-10-19: Sectigo revokes the cross-signs. Apple blocks. Mozilla begins notifying downstream trust stores.
  • 2026-10-20 02:11: Go 1.27.3 and 1.26.10 released (CVE-2026-111943). Public disclosure, Mozilla and Chrome announce removal of emSign ECC Root CA - G3, Microsoft blocks, NSS 3.131.1 and Firefox 158.0.1 ship.
  • 2026-10-21: Node.js 22.23.4, 24.22.1 and 26.12.1 ship the block.
  • 2026-10-27: Chrome Root Store removal of emSign ECC Root CA - G3 scheduled.

The HOL Guard CVE-2026-111943 evidence pack holds the source record for the Go advisory.

References

Continue reading

All posts