Private keys fell in a few tries, so browsers blocked 8 CAs
HOTSecurity18 min read

Private keys fell in a few tries, so browsers blocked 8 CAs

Eight intermediate CAs had keys below 256, and browsers blocked the key itself within hours. Tools and servers need manual updates; G3 root removal lands today.

Chrome put the problem CA keys on its block list 1 hour 40 minutes after receiving the report, while Node.js shipped its fix four days later, on the 21st. In other words, there was a window in which one side blocked the same forged certificate and the other accepted it. This article follows that gap through a single question: how did browsers block all 8 CAs at once, without a software update, instead of hunting down certificates one by one?

What got blocked are 8 intermediate certificate authorities that issued TLS certificates. The private keys of these 8, created by the certificate vendor eMudhra on April 30, 2025, were all numbers smaller than 256, so anyone holding a published certificate could recover a key by trying 0 through 255. This went on for almost 17 months, until a researcher found it on October 17. All times in this article are UTC.

If you only browse websites, there is nothing for you to do. Chrome, Firefox, Microsoft and Apple have already applied the block. What follows is for people who manage servers, developer tools or container images, and for people who got their own site’s certificate from eMudhra.

What got blocked was a key, not 8 certificates

The padlock in the address bar traces back to a root certificate authority, but a root’s private key is not taken out for everyday use, because if it is ever compromised everything collapses. The job of minting actual site certificates goes to intermediate CAs, which the root signs and delegates to. The layer in trouble this time is that middle tier, and anyone who knows its key can create a signature saying “I issued this” for a certificate on any domain.

There was one more wrinkle. In May 2025, Sectigo cross-signed all 8 of them. Cross-signing opens a second path that leads to the same intermediate CA from a different root, so the 8 certificates became 16. But the 16 certificates contain only 8 public keys.

Diagram: two certificates signed by Root G3 and by a Sectigo root contain the same weak key; the block list blocks that key, and Certificate Transparency logs filter out forged certificates Figure 1. The dotted area holds the certificates that contain the same public key. The green box is the block that catches a key in one stroke, and the yellow box is the second wall

The method Chrome used fit this structure. It put the hash of the public key (the SPKI), not the certificates, on its block list (CRLSet), so every certificate containing that key was blocked with a single line, however many there were and whichever root they sat under. According to Google, this approach needed no browser update or restart. Firefox’s block list (OneCRL) also added all 8.

In plain terms

A certificate is an ID card and a key is a stamp. If the stamp has been duplicated and you block ID numbers one at a time, a new ID stamped with the same stamp gets through again. Block the stamp itself and every ID it produced is void at once, however many there were.

Created
Key generation ceremony on April 30, 2025. 8 of the 24 intermediate CAs made in the same ceremony
Purpose
6 for web TLS (G3B and G3C of EM DV, OV and EV TLS CA) and 2 for code signing (G3B and G3C of EM OV CS CA, trusted only by Microsoft)
Key type
P-384 elliptic curve keys. eMudhra says RSA keys are not affected
Actual use
According to eMudhra, only EM DV TLS CA - G3C went into production, and it never issued an end-user certificate
Cross-signing
Sectigo, May 2025, 16 certificate entries
Discovery
October 17, 2026, about 17 months after creation

One integer, found by counting from zero

The discovery began at 16:27 on October 17, when a researcher was running checks against test data in the public CA database (CCADB) and noticed something wrong with a key. To prove the key could really be cracked, the researcher produced a test message signed with it and sent it to Apple, Chrome, Microsoft, Mozilla and eMudhra at 18:05.

A P-384 key should be one of about 10 to the 115th power possibilities. A public key is a base point multiplied by a secret integer, so if the key is a large random number, working backward from it is impossible even given the age of the universe. These 8 keys broke that premise. According to Mozilla’s analysis, the keys were integers smaller than 256, with a value only in the last of their 48 bytes. Multiplying the base point by 1, 2, 3 in turn and comparing the result with the public key finishes in at most 256 steps.

On a number line from 0 to 255, the values 4, 56, 66, 115, 155, 156, 230 and 237 of the 8 keys are marked; the long bar below is the range of a normal P-384 key, and the 8 keys sit in a thin line at its far left end Figure 2. Top: where the 8 values compiled by Kudelski Security sit. The width these values take up in the bar below is too small to see in reality, so it is exaggerated to 5px

What to see on the number line is that all 8 values fall inside the very end of the normal key space, a stretch no bigger than a point. The public table does not say which certificate has which value, but one value the researcher Flüpke posted on GitHub (4, for EM OV TLS CA - G3C) was checked by BleepingComputer against a revoked certificate.

There is a distinction to keep in mind when reading the numbers. “256 tries” is a computational upper bound that follows from the key being below 256, while “under 1 second” is a measurement: security researcher Scott Helme said he recovered a key on a laptop from public certificates alone. Mozilla independently recovered a key at 21:48, 1 hour 42 minutes after the report was received at 20:06, several other people did so on their own as well, and the report says some of them had help from AI agents. What was broken is not the P-384 algorithm but the process that generates keys. The signature scheme itself is unchanged.

How a weak key pair ended up on the device

Following eMudhra’s explanation (the Mozilla report), an approved procedural deviation had been slipped into the key ceremony to test the wrap and unwrap steps used when moving keys to another device. The script used for this test had a defect, so alongside the strong key pair that was intended, a weak second key pair was created inside the device. The labels were similar, so a person created the certificate issuance request with the weak one, the root signed it, and Sectigo added its cross-signature. 8 of the 24 took this path, and the company’s position is that the device itself behaved as designed. The keys described as unaffected are the RSA family and the A series, which was generated on a different device.

The company said this layer went undetected through two audits, and that it will re-verify every key ceremony it has run. It also acknowledged that “human errors occurred at several stages.”

The incident also left signs from before it happened. In June 2025, an issue was filed in Mozilla’s tracker saying the signature on a certificate revocation list (CRL) issued by eMudhra failed verification (Bug 1970728). It is said to have been resolved by re-signing with the weak key, because the list signed with the original key did not verify. In hindsight, the fact that signing with the weak pair fixed the problem was a clue. Mozilla and Chrome are asking eMudhra why it was resolved that way and why that history is missing from this report. The same list of questions asks whether the device was running in FIPS mode, whether RSA keys are really unaffected, and whether Sectigo, which did the cross-signing, was told the keys were wrong.

The second wall that kept forgeries from being used

A cracked key did not mean damage right away. To use a forged certificate, an attacker has to get into the path between the user and the server: public Wi-Fi, a compromised router or a corporate proxy are such places. From there, the attacker can lead the user to a fake server and make even the padlock look normal. The CDN company Cloudflare summed up the problem as being not about TLS encryption but about authentication, the question of “is the other side genuine?”

But browsers had one more wall that a key alone could not get past. Certificate Transparency (CT) is a system that makes issued certificates get recorded in a public ledger, and Chrome by default does not accept publicly trusted certificates that are not in the ledger. Google said that before the block, not a single certificate issued under the 6 problem TLS intermediate CAs had been logged in CT. A forged certificate cannot be put in the ledger, and if it is not, Chrome rejects it. Google added that it had also updated the CT logs to reject chains from these CAs outright.

Google also said in the same post that it had checked the keys in other root stores and found no such flaw.

Chrome in 1 hour 40 minutes, Node.js four days later

Put who blocked what on a date grid and the response speed splits into two groups. In the grid below, the darker cells are the days the block took effect, and the numbers are the time elapsed since the report at 18:05 on the 17th. Only times that were made public are shown.

A grid showing, for 12 actors, which day from October 17 to 27 they applied a block, revocation or fix Figure 3. Grid of dates when each block took effect. The browser block lists are in the 17th column, developer-tool fixes are in the 20th and 21st, and Chrome’s G3 root removal is scheduled in today’s (27th) column

The two cells at the top left are the fastest. Chrome issued its block list at 19:45, 1 hour 40 minutes after receiving the report at 18:05, and Mozilla finished applying the block to Firefox’s list at 22:59. Around 09:00 the next morning, eMudhra revoked the 8 certificates citing “CA key compromise,” and at 14:55 on the 19th Sectigo revoked the cross-signatures. Android received the same block through Google Play services around 22:00 on the 19th.

The developer tools sit one cell further right. Python’s certificate bundle certifi was updated at 02:24 on the 20th, Go’s fix came on the 20th and Node.js’s security release on the 21st. A browser’s block list arrives automatically, while a tool needs a new version to be released and then needs users to install it. That difference accounts for nearly all of the time gap.

More happened in between. Mozilla’s Dennis Jackson disclosed the incident on the developer policy forum on the 20th, and Mozilla removed emSign ECC Root CA - G3 from its trust list the same day (NSS 3.131.1, Firefox 158.0.1). Cloudflare finished the server side of a response that had been running since it learned of the issue around 17:00 on the 19th, at 19:20 on the 20th. A configuration in Firefox for Android that did not apply the block list was found and fixed in 158.0.2 on the 21st; browser auto-updates, too, left some setups out. The 2 code-signing CAs are certificates browsers do not use, so they were not subject to Chrome’s block, and Microsoft blocked them on its platform on the 20th.

curl accepted the same forged certificate

Browsers have two checks, the key block list and CT, while servers and developer tools usually have neither. The diagram below shows the sequence when the same forged chain reaches two kinds of clients.

Sequence diagram: a browser that receives the forged chain rejects it at two points, the block list and the CT record, while curl, Python and Node accept it as is because their bundle contains the root, and send data Figure 4. Two ways of facing the same forged chain before the fix. A tool trusts only the one certificate bundle inside itself

In Scott Helme’s tests, curl, Python and Node.js on Linux and macOS accepted a forged certificate made under EM DV TLS CA - G3B as valid. The /usr/bin/curl that ships with macOS reads a certificate bundle at /etc/ssl/cert.pem, and unless this file is updated it lets even a revoked chain through. This was one researcher’s test, and results can differ by environment.

Cloudflare gave two further pieces of guidance. Servers that customers run themselves, mutual TLS (mTLS) setups and container images need the customer to fix the trust list, and upgrading one SDK may not be enough, because an SDK often leaves certificate verification to another HTTP library, the runtime or OS settings. Passing a test in a browser or with curl does not guarantee the safety of a service running in the background, so you have to check through the TLS path that service actually uses.

Certificate bundles hide in more layers than you might think. Fix one layer, and if the layer beneath is stale, the problem leaks through that route.

  • OS bundle: /etc/ssl/cert.pem, the ca-certificates package. This is what the system curl reads.
  • Language runtime and package bundles: Python certifi, the root list inside Node.js, Rust webpki-roots.
  • Lists baked into executables: Go programs, single-file executables, snapshots. They stay as they are until rebuilt.
  • Container images: the bundle inside the base image. It takes effect only after you pull a new one and redeploy.
  • Embedded and VPN devices: lists burned into firmware. They take effect only after a vendor update.

Fixed versions per tool, and who is still waiting

The route differs by tool. Two cases that require a rebuild or restart come first.

  • Go: 1.26.10 and 1.27.3 fixed the standard library (crypto/x509) to reject chains that contain the key fingerprints of the 8 CAs (CVE-2026-111943). This applies even when you use a root list you specified yourself. Go does not check revocation information, so executables you have already built must be rebuilt with the new Go to be fixed, and there is no fix for 1.25 and earlier.
  • Node.js: the security releases of the 21st (22.23.4, 24.22.1, 26.12.1) reject chains containing those keys and raised the bundled roots to NSS 3.131.1. TLS sessions already established before the upgrade are not re-verified, so you need to restart the service, and there is no fix for the end-of-life 20 and earlier lines.
  • Python and Rust: certifi 2026.10.19 dropped the G3 root, and pip and uv followed with updates. On the Rust side, rustls-webpki 0.103.16 and webpki-roots 1.0.10 carry the same response.
  • Firefox: 158.0.1 (CVE-2026-113470) and ESR 153.5.1 are the versions that dropped the G3 root.

The table below checks each project’s releases against the community tracking table run by the researcher, as of the 26th. If a row lists two versions, pick the one that matches the line you use.

Environment Fixed version Notes
Go 1.26.10 · 1.27.3 Rebuild required to take effect, no fix for 1.25 and earlier
Node.js 22.23.4 · 24.22.1 · 26.12.1 Restart the service after upgrading
Python certifi 2026.10.19, pip 26.2.2, uv 0.13.5 Virtual environments made with CPython 3.9 or 3.10 need pip upgraded inside them
Debian ca-certificates 20250419~deb12u2 · 20250419+deb13u1
Ubuntu ca-certificates 20260816ubuntu0.22.04.1 or later 24.04 is 0.24.04.1
Alpine ca-certificates 20261020-r0
Developer tools Docker Engine 29.9.2, VS Code 1.143.1, Electron 44.9.1 · 43.8.1 · 42.11.16 Versions that updated their built-in bundles. Electron apps (Slack, Discord and others) take effect only once updated to a new Electron
Cloudflare WARP Android 2.5.7, iOS 1.12.7, desktop 2026.8.2115.0, cloudflared 2026.10.2 For containers, redeploy the image

In the same tracking table, all 8 language runtimes and all 12 root bundles are marked as applied, and most operating systems and applications are too. The ones still marked pending are RHEL 7, Bottlerocket, OpenHarmony, Godot, LibreOffice, certbot and containerd. If you use any of them, check the supplier’s notice.

Four read-only checks for your environment

The checks finish with commands that only read. The order is chain, bundle, version, test host.

  1. Look at the server's certificate chain. Confirm which CA's chain your own domain presents.
  2. Check whether a G3 root remains in the trust list. Look at the certificate bundles on your servers and development PCs.
  3. Check runtime and tool versions. If they are below the fixed versions in the table above, upgrade.
  4. Test directly against a test host. Confirm whether this client accepts a chain built with the weak key.

First, look at the issuers in the chain the server presents.

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -E '^ *([0-9]+ s|i):'

Each step of the chain prints one pair of subject (s:) and issuer (i:) lines. If any line shows an eMudhra name with G3 attached, such as EM … TLS CA - G3B, G3C or emSign ECC Root CA - G3, you need a plan to replace the certificate. By eMudhra’s account there are no site certificates signed directly by the 8, but certificates from other intermediate CAs chained to the G3 root may stop working in Chrome. Names in the emSign Root CA - G1 family are still trusted by Mozilla and are not a target of this check.

Second, count whether a G3 root remains in the trust list file. The file the default curl on macOS reads is /etc/ssl/cert.pem, and on Debian and Ubuntu it is /etc/ssl/certs/ca-certificates.crt.

openssl crl2pkcs7 -nocrl -certfile /etc/ssl/cert.pem | openssl pkcs7 -print_certs -noout | grep -c "emSign ECC Root CA - G3"

A result of 0 means it is already gone. If you get 2 (one pair of a subject line and an issuer line), the G3 root is still in this bundle, so update it.

Third, check tool versions. The commands below all print only versions.

go version                # if your tool is in Go: go version -m ./myprogram shows the Go version used to build it
node -v
python3 -c "import certifi; print(certifi.__version__)"

Fourth, send one request to the test host the researcher runs. This host presents a certificate that is not recorded in CT, so it shows whether a tool that does not check CT accepts the weak-key chain.

curl -sS -o /dev/null -w '%{http_code}\n' https://dv-g3b-noct-emudhra.we-have-a-problem.com/

If the connection succeeds and a status code such as 200 comes back, this client accepts a chain made with the weak key, so it needs an update. If a certificate error occurs (exit code 60 for curl), it is rejecting the chain and is safe. Other errors, such as a connection failure, may be a network problem rather than a rejection, so check the exit code. This test only checks curl’s own trust list. You have to send the same request through the HTTP library your app uses to know whether the app itself is safe. If your corporate network policy blocks outside connections, skip this step and decide from the first three.

G3 root removal arrives today, and operators must swap

The most urgent group is operators who got their own site certificates from eMudhra. Google announced that the change removing emSign ECC Root CA - G3 from the Chrome root store reaches all devices on the morning of the 27th (today), and warned that even legitimate certificates chained to this root may stop working in Chrome. Chrome removed it to protect users of other root stores that do not receive the block list. What operators have to do is get a new TLS certificate from another CA hierarchy in the Chrome root store and install it.

eMudhra said it has about 30,000 certificates to move, 97% of which were issued through automatic renewal (ACME). Subscribers are being moved to the RSA-based G1 hierarchy, and it also offers replacement certificates issued by Sectigo free of charge. If you renew over ACME, you can change the CA you request from in the client settings. If a G3 name came up in the first check above, this is your case.

If you call external services from a CI server or container, calls that carry secrets out, such as webhooks, package repositories and update servers, are the riskiest path. Pull the base image fresh and rebuild, take releases of Go-built tools that were built with the new Go, and restart Node services after upgrading. In Cloudflare Containers, a way to hand HTTPS egress verification to Cloudflare has also been described.

If you download and install scripts from the terminal on a personal Mac, the browser is safe, but the terminal’s curl is a different matter. If your curl returns 200 in the fourth check, put off curl … | sh style installs anywhere the path is hard to trust, such as public Wi-Fi. As a stopgap before an update arrives, the researcher’s site describes removing the affected roots from the bundle by hand, but it has to be redone with every macOS update.

Even if you get a certificate error, do not turn off verification. Settings like --insecure, verify=False and NODE_TLS_REJECT_UNAUTHORIZED=0 make the error go away, but they also remove the very protection this incident targeted. If the error persists after you update the bundle, it is a sign that the server is still using a certificate chained to the G3 root, so you need to replace that server’s certificate, not change the client.

If the runtime is embedded in an executable on your server (single-file executables, snapshots), the bundle is frozen along with it, so check once which runtime version your build pipeline uses.

eMudhra withdraws, and the G1 root remains

After the incident came out, eMudhra issued a statement in an exchange filing on the 21st. It said the electronic signature services in its home market run on separate equipment and are unaffected, and that its infrastructure was not breached and no information leaked. According to reports, its share price hit the daily limit-down of 20% that day. On the 22nd it announced that it would withdraw from the global TLS certificate business, which it said makes up about 1% of total company revenue. In the Mozilla report the company fully admitted the error, while arguing that the impact was not broad because of the revocation on the 18th.

A few dates are set for what comes next. The root cause report is due within 14 days under CCADB rules, and a Bugzilla comment pointed out that the deadline for the answers to the 12 questions from Chrome and the questions Mozilla sent on the 20th is around the 28th by CCADB’s 7-day guideline. Those questions include the CRL signature problem, and in the Mozilla report someone separately raised that one CRL under emSign Root CA - G1 was signed with an OCSP key rather than the CA key. Mozilla says it is keeping trust in the G1 root for now, and Google says it is evaluating whether to keep eMudhra’s remaining CA certificates in the Chrome root store and will announce the result through the public CCADB list. For subscribers who moved to the G1 hierarchy, that decision lands directly on them.

A trust list is not a setting you install once and forget; it is a dependency that has to be updated regularly. What browsers finished in a few hours this time, tools and servers followed over days, and the G3 root removal on the 27th and Chrome’s decision after the answer deadline around the 28th will flow down the same road. Writing the update cycle of your own bundles and base images on a calendar is the cheapest preparation this incident offers.

#eMudhra#certificate authority#TLS#trust store#certificate revocation