eMudhra CA compromise (CVE-2026-111943): Eight intermediate certificates with keys you can count
An ECDSA key on the P-384 curve is only as secret as the number it is derived from, and that number has to be drawn at random from a space of roughly 2384 possibilities. For eight intermediate certificates of the Indian certificate authority eMudhra, it was below 256, and in one case it was simply 4. Anyone who knew the public key could therefore find the private key simply by trying the candidates and use it to sign server certificates for any domain. Browsers were protected within hours because their vendors distribute blocklists centrally and enforce Certificate Transparency. Neither applies to curl, Go, Java, Python and Node in your containers, and that is where you find out whether the incident affects you.
TL;DR — 90 seconds
- What: Eight ECDSA P-384 intermediates of eMudhra, six for TLS and two for code signing, were created during a key ceremony on 30 April 2025 with private keys smaller than 256. The private key could be determined from the public key with negligible effort.
- Severity: The CA incident itself has neither a CVE nor a CVSS score. Go tracks its fix as
CVE-2026-111943, Firefox asCVE-2026-113470. I rate the incident as critical because the keys could sign server certificates for any domain, which many non-browser clients trust until they are updated. - Who is affected: Services that validate TLS without a Certificate Transparency check and use a trust store from before 20 October 2026, which covers most container images, Go and Java services, Python and Node applications and Kubernetes controllers. It can only be exploited by someone who can get into the connection. Chrome and Firefox have been protected since 17 October, and Apple, Android and Microsoft followed by 20 October.
- What to do: Update ca-certificates and language-specific bundles such as certifi, Composer ca-bundle and Java cacerts, rebuild images, recompile Go services with Go 1.27.3 or 1.26.10 and move Node to 22.23.4, 24.22.1 or 26.12.1.
- What would have protected you: a private CA for internal connections, centrally managed and versioned trust bundles in the cluster and regular rebuilds of all images. CAA records and revocation checks do not help against this mechanism.
Eight keys from a tiny number space
With elliptic curves, the private key is a single large number d. The public key is the point Q you get by combining a fixed, publicly known base point G of the curve with itself d times, in short Q = d·G. In this direction the calculation is quick. Going back from Q to d is considered practically impossible, but only under one condition: d must be drawn at random from the curve's entire number space, which for P-384 holds roughly 2384 values. All of the security rests on nobody being able to search that space.
This is exactly where things went wrong. According to Scott Helme's analysis, d for the eight affected intermediates consisted of 47 zero bytes and a single byte with content. The Bugzilla entry states that all eight keys were integers smaller than 256, and according to Helme the key of the intermediate EM OV TLS CA - G3C was simply 4. Instead of 2384 candidates there were at most 255, and one calculation per candidate is enough to compare it with the published key. How strong a curve is mathematically no longer matters when the key comes from such a small slice.
The treacherous part is that such keys pass every formal check. The point Q lies correctly on the curve and is cleanly encoded in the certificate. What a CA, a browser or a certificate linter checks on a public key concerns its form, not the origin of the secret value behind it. The weakness was found by a community member who only goes by “Wayne”. He signed the sentence “We have a problem here - Wayne” with each of the eight keys and proved possession without issuing a single certificate.
How the keys came about
According to the preliminary root cause analysis eMudhra published in the Bugzilla entry, the error goes back to the ceremony of 30 April 2025. An approved deviation from the procedure allowed a test of the wrap and unwrap process, and a flaw in the code created a second, weak key pair on the Utimaco HSMs. Because the key labels were similar, this pair ended up in the certificate signing requests. eMudhra speaks of human errors in multiple steps. Whether the keys were really generated inside the HSM or imported from outside is still open in the discussion, and several participants have asked exactly that. What I take from it: an HSM protects a key from being extracted, but it does not protect against a script making it work with the wrong parameters.
A warning sign from 2025
There had been an early signal. At the end of May 2025, eMudhra published revocation lists with invalid signatures for the six TLS intermediates, which Rob Stradling reported in Bugzilla based on crt.sh. eMudhra attributed this to a non-matching key configuration and published re-signed lists on 6 June. As things are described today, that happened with the weak keys from the certificates, after which the signatures matched and the actual error was hidden. The mismatch between the key that signed and the key in the certificate was visible. It was simply read as an operational problem and not as a sign of a wrong key.
What the fixes change
Mozilla, Go and Node intervene at different points, and comparing them says a lot about the nature of the problem. Mozilla blocks the intermediates via OneCRL and, with NSS 3.131.1, removes the root emSign ECC Root CA - G3, through which they were anchored in the common trust stores. Go takes a different path with commit 7f86f957. In crypto/x509, Certificate.Verify now checks every chain against a compiled-in denylist holding the SHA-256 hashes of the eight public keys and discards chains with a match, even when verification runs through the platform verifiers on macOS and Windows. Node follows the same principle with PR #66871 and, since 22.23.4, 24.22.1 and 26.12.1, rejects every verified chain with a blocked key as CERT_REVOKED. Chrome also blocks via the key hash in its CRLSet.
The difference between certificate and key is particularly telling. Eight keys were compromised, but sixteen certificates had been issued, because each intermediate existed twice. Once it sits under eMudhra's own G3 hierarchy, which is cross-signed by emSign ECC Root CA - G3. Once it is a Sectigo cross-sign under the old root AAA Certificate Services, which according to the disclosure site is still trusted by Apple and Microsoft. If you remove only the emSign root, you close the first path and leave the second open. A block on the public key hits both paths at once, because it blocks what has actually been lost. In the Web PKI, trust is anchored in the key, and one key can appear under several certificates and roots.
Forged certificates and who would have accepted them
An attack needs two things. The first is one of the six TLS keys, and since the disclosure anyone can compute it. The second is a network position from which the attacker can get between client and server, for example via a foreign Wi-Fi network, manipulated DNS or a compromised intermediate hop. Only then can they present the client with a self-issued certificate for the target domain that leads to a trusted root. The two code signing keys can also sign Windows programs, because Microsoft carries emSign Root CS CA - G3 directly in its store.
Browsers had an additional layer of protection. Chrome, Firefox and Safari require proof from Certificate Transparency logs, so-called SCTs, for server certificates, which means a secretly issued certificate would have to be logged publicly. According to Google, no TLS certificate under the six intermediates had been logged before the block. CT only makes a forgery visible, though; it does not prevent it, and it only works where a client actually requires SCTs. Code signing certificates are not logged in CT at all, so whatever was signed there cannot be traced publicly.
For your infrastructure, a different point matters. curl, the TLS stacks of Go, Java, Python and Node and therefore Kubernetes clients, operators and CI tools check neither SCTs nor revocation status by default. They trust any chain that ends at a root in their trust store. Scott Helme demonstrated this before the updates: curl, Python and Node on Linux and macOS connected without warning to a server presenting a forged certificate. For such clients, the block in browsers was meaningless. They are only protected once their trust store has been updated or their runtime blocks the keys itself.
What is really at risk, then, is any service that opens outbound TLS connections to public endpoints and still uses an old trust store, provided someone can intercept the connection. That covers API calls and webhooks from applications, package and image downloads in CI pipelines and controllers that talk to external services. Whoever gets in there reads tokens and credentials or delivers manipulated artifacts. There is little risk for browsers and for connections that trust exclusively a private CA of your own. If you feel safe because you never bought an eMudhra certificate, you are mistaken, because what matters is not whose certificates you use but which roots your clients trust.
| CVE | Affected | Fix | Precondition | CVSS |
|---|---|---|---|---|
| eMudhra CA incident (no CVE) | Trust stores containing emSign ECC Root CA - G3 or AAA Certificate Services without a block on the eight keys | Trust store updates, OneCRL, CRLSet, revocation of the 16 certificates | Network position for a man-in-the-middle | none (own rating: critical) |
| CVE-2026-111943 (GO-2026-6809) | Go crypto/x509 before 1.26.10 and 1.27.0 up to before 1.27.3 | 1.26.10 / 1.27.3, recompilation required | as above | 6.8 per Amazon |
| CVE-2026-113470 (MFSA2026-111) | Firefox before 158.0.1, ESR before 153.5.1 and 115.43.1 | 158.0.1 (Android: 158.0.2) / ESR 153.5.1 / ESR 115.43.1 | as above | not published |
| Node.js (no separate CVE) | 22.x, 24.x, 26.x before the October release | 22.23.4 / 24.22.1 / 26.12.1 | as above | – |
No classic exploit is needed here, because anyone can compute the keys. No misuse against real targets has been confirmed so far. On 21 October, CERT-In found no evidence of malicious use in the public disclosures, Google saw no further certificates in the CT logs before its block, and eMudhra states that only EM DV TLS CA - G3C was in production and that none of the eight intermediates issued end-entity certificates. Against clients without CT checks, however, misuse cannot be ruled out in principle, because it does not have to leave a public trace. None of the CVEs mentioned is in the CISA KEV catalog, and EPSS put CVE-2026-111943 at 0.15 percent and CVE-2026-113470 at 0.20 percent on 21 October, which says little for a fix without a classic exploit path.
Trust store updates and where they arrive
There is no patch in the usual sense, because the error lies with the CA. What you update is the list of what your systems trust, and that list lives in more places than you might first think. The disclosure site, the vendors and Endor Labs agree on the following versions unless marked otherwise:
- Debian ca-certificates:
20250419~deb12u2(Bookworm),20250419+deb13u1(Trixie) - Alpine ca-certificates:
20261020-r0 - Wolfi/Chainguard ca-certificates:
20260909-r3, synced with NSS 3.131.1 according to the package definition in wolfi-dev/os - Amazon Linux 2023: no ALAS yet, issue #1147 is open. The image checked on 20 October contained
ca-certificates-2025.2.76-1.0.amzn2023.0.3without the fix. - Amazon Linux 2: no statement from Amazon on ca-certificates. For the Go flaw CVE-2026-111943, the Amazon Linux Security Center lists
golang,docker,runcandcri-tools, among others, as “Pending Fix” on AL2 and AL2023. - RHEL and UBI: Red Hat tracks the incident as RHSB-2026-012, lists no fixed versions yet and describes a workaround via the blocklist.
- Go: 1.27.3 and 1.26.10, no backport for 1.25 and older
- Node.js: 22.23.4, 24.22.1, 26.12.1 with updated roots and a key blocklist
- Python certifi: 2026.10.19
- Composer ca-bundle: 1.4.16 and 1.6.0
- Rust: rustls-webpki 0.103.16, webpki-roots 1.0.10
- Java: OpenJDK 27.0.1.0.2, 25.0.5.0.2, 21.0.13.0.2, 17.0.21.0.2, 11.0.33.0.2 and 8u511 according to the disclosure site. Endor Labs, on the other hand, does not list Java as fixed, so check your JDK distribution directly in
cacerts.
The biggest pitfall is the assumption that a host update is enough. The trust store is a file in the image, and a container sees the version copied in at build time. In Go images based on scratch or distroless, ca-certificates.crt is often copied in a build step, and an updated host changes nothing about that. According to the Go advisory, Go uses the system roots on most platforms. In a container those come from the image, so an updated trust store only takes effect once it has arrived there. The denylist for the eight keys, on the other hand, lives in the binary and only takes effect after recompiling with the new toolchain, which applies to your own services as much as to kubectl, operators and controllers.
Two further points deserve attention. Because the Sectigo path via AAA Certificate Services carries the same key, Red Hat's workaround and the macOS instructions on the disclosure site block that root as well. This can break legitimate connections to services whose chains still run via AAA, so it needs testing before rollout. Whether the fixed Debian packages remove AAA themselves is not clear from the sources; the fingerprint check in the image described in the detection section answers that for your case more quickly. And emSign Root CA - G1 remains trusted for now, while Chrome is still reviewing the remaining eMudhra CAs. With Node there is the added point that sessions resumed from before the update are not re-verified. According to the PR, that exposure only ends once the session timeout has expired.
Hardening beyond the update
Before looking at individual measures, some context helps. The attack does not happen in your container but on the path between your client and its target. No foreign code runs, no unusual syscall occurs, and the client does exactly what it was built to do. The only effective measures are therefore those that change which issuers a client trusts, how quickly that list can be changed and whether a client can notice a forgery at all.
Rebuild images regularly
Because the trust store lives in the image, regular rebuilds are the most important measure. An image rebuilt every week or with every new version of its base image gets the updated trust store a few days after the distribution update, without anyone having to think of the incident. For Go services the toolchain belongs in the same process, because the denylist from crypto/x509 only reaches the binary through a rebuild. The limit is obvious: a rebuild only helps if the distribution already ships the fix. On Amazon Linux 2023 you need your own block until then.
Manage trust bundles centrally in the cluster
trust-manager from the cert-manager project distributes a CA bundle as a ConfigMap or Secret into namespaces. The default package comes from Debian, and according to the documentation older tags still trust the compromised CA; you need at least trust-pkg-debian-trixie:20250419-deb13u1.0 or trust-pkg-debian-bookworm:20250419-deb12u2.0. Pin the tag and let Renovate update it, so the state of the bundle is a version in Git rather than a guess. The documentation describes no option for excluding individual certificates from the default package, so for a narrower selection you maintain your own bundle.
A central bundle only works if the runtime actually reads it. On Linux, Go and OpenSSL can be pointed to a file via SSL_CERT_FILE. Python with requests uses certifi unless you configure something else. Node ships its own store, and NODE_EXTRA_CA_CERTS only extends it instead of replacing it. If you want Node to use the system store, start it with --use-system-ca. These parallel stores are exactly what Endor Labs means by the trust stores you didn't know you had.
A private CA for internal connections
Connections between your own services, for example to the database, to Valkey or to internal APIs, do not need the public Web PKI bundle. If a client there trusts only a private CA that cert-manager runs as a CA issuer, a compromised public CA cannot forge these connections. Name Constraints on the private CA additionally limit which names it may issue for. This is a general principle and not a recommendation from the sources on the incident, but it removes a large part of the attack surface entirely. External APIs and package sources still need the public bundle.
Block the key instead of the certificate
Where the platform allows it, a block on the public key is more precise than removing a root, as the Sectigo path has shown. Go and Node now bring such a block themselves. For RHEL 7 and later and for UBI, Red Hat describes a workaround in RHSB-2026-012 using the update-ca-trust blocklist that blocks emSign ECC Root CA - G3 and the Comodo root AAA. For Amazon Linux 2023, which also uses update-ca-trust, the reporter of issue #1147 describes the same approach and verified it successfully against the test host, but only for the emSign root, which leaves the path via AAA open. Amazon has not commented on it officially. A block on the host does not reach your containers, so it belongs in the image build.
Certificate Transparency monitoring for your own domains
A CT monitor reports every logged certificate for your domains, no matter which CA issued it. Had someone issued and logged a certificate for one of your domains under one of the compromised keys, you would have noticed it quickly this way. You know the limit from the section above: against clients without CT checks, a forgery never needs to be logged. CT monitoring is an early warning system for misissuance, not a protection.
What does not help here
CAA records do not protect you, even though they are often understood that way. RFC 8659 specifies that only the issuing CA checks them and states explicitly: “Relying Parties MUST NOT use CAA records as part of certificate validation.” Whoever holds a CA key simply skips the check. Revocation does not help much either. eMudhra and Sectigo revoked the sixteen certificates, but most non-browser clients do not check revocation status by default, and according to Helme only one of two verification paths on Windows reported the chain as revoked. Mozilla itself notes in the Bugzilla entry that many endpoints have no effective way of obtaining revocation information.
Pinning only helps to a limited extent. Pinning the server key for a single connection protects you against foreign CAs, but you pay with considerable effort at every key rotation. A pin on a CA does not help when that very CA is affected. seccomp, dropped capabilities, readOnlyRootFilesystem and the Pod Security Standard “restricted” remain right, but they do nothing against an intercepted connection. An HSM at the CA did not prevent the incident either. And a CVE gate in the pipeline only fires where a package carries a CVE, such as the Go standard library. It does not reliably report an outdated ca-certificates in an image, because the CA incident itself has no CVE.
You will find more on egress, certificate management with cert-manager and image pipelines in the series TYPO3 on Kubernetes under IT-Grundschutz.
Detection and verification
The best way to check whether a client still trusts the keys is with that very client. The disclosure site runs test hosts whose certificates are signed with one of the compromised keys: dv-g3b-emudhra.we-have-a-problem.com chains via the emSign root, dv-g3b-aaa-emudhra.we-have-a-problem.com via AAA Certificate Services. Call both from within the image or pod. A certificate error means the client is protected, a successful connection means it still trusts. A test with curl only checks curl, though, and Go, Java and Node services in the same image may behave differently.
For bundles in an image, searching for “emSign” is not enough, because emSign Root CA - G1 remains trusted for now. Search specifically for emSign ECC Root CA - G3 with the SHA-256 fingerprint 86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B and for AAA Certificate Services with D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4. For Java, keytool -list lists the entries in cacerts, and for Go binaries go version -m shows which toolchain built them. In PHP projects it is worth checking the version of composer/ca-bundle, which Composer and many HTTP clients ship.
Whether you have been attacked is harder to answer, because a man-in-the-middle only leaves traces at the client. Check crt.sh for certificates under one of the eight intermediates for your domains, and use the SPKI hashes from the Go commit and the disclosure site as search criteria in TLS logs if your egress proxy records the issuers of remote endpoints. No IoCs have been published. For code signing, SOCRadar and other analyses advise looking for signatures with these intermediates since 30 April 2025 and re-signing affected artifacts with a new certificate and an RFC 3161 timestamp. If you have a concrete suspicion of an intercepted connection, rotate all credentials that passed through it.
Recommendation for operators
Act now if services in containers with trust stores from before 20 October 2026 open TLS connections to public endpoints, especially from networks you do not control yourself, or if CI runners load packages and images that way. Rebuild the images with updated ca-certificates, recompile Go services with 1.27.3 or 1.26.10, upgrade Node and put your own block in place on Amazon Linux 2023 hosts until Amazon ships an update.
Monitoring is enough if your clients are exclusively browsers or your services talk internally only via a private CA. The next regular image update will bring the new trust store anyway, and Chrome is expected to remove the root from its Root Store on 27 October.
Regardless of the individual case, what I say about every vulnerability applies: patches and updates belong in automation, and the trust store is a dependency just like any package. When Renovate or Dependabot propose new base images, the Go toolchain and the tags of the trust-manager package as merge requests on their own and the pipeline tests against staging, an incident like this is in every image after hours instead of weeks. A cooldown period for routine updates protects you against compromised releases, and you exempt security updates from it. Blocking AAA Certificate Services in particular can break legitimate connections, and that shows up in testing instead of in production. If you update by hand, you easily carry a trust store from summer into winter and then build a bulk update where everything breaks at once.
Frequently asked questions about the eMudhra compromise
I don't use eMudhra certificates. Am I affected?+
Possibly yes. What matters is not whose certificates you use, but which roots your clients trust. An old trust store accepts a forgery for any domain.
Is it enough to update ca-certificates on the host?+
No. Every container uses the trust store from its image. You have to rebuild the images and compile Go services with the new toolchain so that the key block ends up in the binary.
Why doesn't my CAA record help?+
Only the CA checks CAA, before it issues. According to RFC 8659, clients must not use CAA for validation. Whoever holds the CA key simply skips the check.
Do I now have to distrust all eMudhra certificates?+
No. The eight intermediates and the root emSign ECC Root CA - G3 are blocked. emSign Root CA - G1 remains trusted for now, but Chrome is still reviewing the remaining eMudhra CAs. Follow the announcements of the root programs.
Conclusion
The eMudhra case shows that a key can pass every formal check and still be worthless, and that an early warning sign such as a wrongly signed revocation list deserves a root cause analysis rather than a quick re-signing. For operators, the transferable lesson lies elsewhere. Browsers protected their users within hours because they enforce CT and distribute revocation lists centrally. Your services only have the trust store you gave them at their last build. Treat it like any other dependency, with a version, automatic updates and rebuilds, and give internal connections a CA of their own. Then the next incident of this kind is a merge request and not a project that takes weeks.
Sources
- Mozilla Bugzilla 2083449: eMudhra Intermediate CA Key Compromise
- Mozilla Bugzilla 1970728: invalid CRL signatures in 2025
- Mozilla Bugzilla 2084101: removal of emSign ECC Root CA - G3
- Mozilla dev-security-policy: discussion of the incident
- Mozilla dev-tech-crypto: NSS 3.131.1
- Google: Protecting Chrome and Android users
- Go Vulnerability Database: GO-2026-6809 (CVE-2026-111943)
- Go fix commit 7f86f957 (crypto/x509 denylist)
- Node.js PR #66871: reject chains containing blocked public keys
- Disclosure site with test hosts and fingerprints
- Scott Helme: Eight bits of trust
- eMudhra: Certificate Security Incident
- Red Hat RHSB-2026-012
- Amazon Linux 2023 issue #1147
- Endor Labs: The trust stores you didn't know you had
- cert-manager docs: trust-manager
- RFC 8659: DNS Certification Authority Authorization (CAA)
- Cloudflare: eMudhra certificate compromise
- SOCRadar: eMudhra CA Key Compromise
- The Register: eMudhra private keys compromised
I check which certificate authorities your containers, Go services and Kubernetes controllers really trust, and bring your trust stores up to date with automated rebuilds.
Trust store audits for images and runtimes, central trust bundles with trust-manager, private CAs with cert-manager for internal connections and rebuild pipelines with Renovate.
Platform operations instead of consulting on paper: I continuously check, patch and harden your infrastructure.
