On 19–20 October 2026 the Go team released Go 1.27.3 and 1.26.10. They contain one security fix, CVE-2026-111943 (Go issue 82345): crypto/x509 now rejects chains that contain a small set of intermediate certificates from the CA eMudhra (emSign), because their private keys are compromised. The advisory doesn’t say which certificates, so I looked.
If you ship Go binaries, updating Go on your machine is not enough. Every binary has to be rebuilt with a fixed toolchain.
Summary
| ID | CVE-2026-111943 |
| Component | crypto/x509 (Certificate.Verify) |
| Weakness | CWE-295, improper certificate validation |
| Fixed in | Go 1.26.10 and 1.27.3 |
| Severity | Sources disagree: 6.8 (Medium) from Red Hat, 8.1 (High) in Oracle’s preliminary score |
What happened
According to the reports I found, a researcher noticed on 17 October that eight intermediate certificates under eMudhra’s emSign were generated with a flawed key-generation process. Mozilla later concluded the flaw is easy to exploit. They are the DV, EV, OV (TLS) and OV code-signing CAs, each in the G3B and G3C generations.
Whoever can recompute one of these private keys can issue a certificate for any domain, and a client that trusts the chain will accept it.
What the fix does
Verify now drops any chain that contains one of the blocked intermediates, so it is never returned to the caller. The filter is a hardcoded list of eight SHA-256 hashes, computed over each certificate’s SubjectPublicKeyInfo (SPKI) instead of the whole certificate.
That is the right choice. One key can appear in several certificates, for example an intermediate cross-signed by two roots. The certificate hash differs between them but the SPKI hash is the same, so one entry covers every encoding.
Is the list complete?
A hardcoded denylist is only as good as its completeness, so I compared it with the researcher’s proof bundle (proof.zip) on 26 October 2026:
- I checked the bundle first.
openssl dgst -sha384 -verify <name>.pub.pem -signature <name>.sig message.txtprintedVerified OKfor all eight, and each public key matched the one in its certificate. - I computed the SHA-256 of the DER SubjectPublicKeyInfo of each
.crt.pem, the same inputcontainsDistrustedSPKIHashuses. - I took
distrustedSPKIHashesfromsrc/crypto/x509/verify.goat taggo1.27.3.
The result is an exact match: eight of eight, and nothing extra in Go’s list.
| Certificate | SHA-256 of SPKI |
|---|---|
| EM DV TLS CA - G3B | ebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105 |
| EM DV TLS CA - G3C | 49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f |
| EM EV TLS CA - G3B | e0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41 |
| EM EV TLS CA - G3C | 5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0 |
| EM OV TLS CA - G3B | 43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b |
| EM OV TLS CA - G3C | 63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8 |
| EM OV CS CA - G3B | ec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6 |
| EM OV CS CA - G3C | 92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30 |
In the 1.27.3 source the filter runs on every path: verify.go for the pure-Go verifier, and root_darwin.go and root_windows.go for the platform verifiers. A forged leaf from any of the eight is rejected whichever root it chains to.
This holds against the researcher’s bundle, which covers DV, EV, OV-TLS and OV-CS in G3B and G3C. If the faulty process also produced a ninth intermediate that nobody reported, this list would not catch it, and only eMudhra or the root programs can rule that out. I did not test a forged chain against a real Go binary.
The operational gap
Browsers handled this with revocation pushes (CRLSet, OneCRL, CRLite). Go has no such channel, because the list lives in the standard library source. So:
- A binary built before the fix trusts all eight intermediates forever. No revocation data will ever reach it.
- Go’s
crypto/x509doesn’t enforce Certificate Transparency, so a forged certificate wouldn’t be exposed by CT logs either. - Installing a newer Go changes nothing for binaries that already exist. Distributions may also backport the fix under their own version strings.
What to do
Find the toolchain each binary was built with:
go version -m ./your-binary | head -1Anything built with Go older than 1.26.10 (on the 1.26 line) or 1.27.3 (on 1.27) needs a rebuild, and so do container images made from those binaries.
Rebuild and redeploy with a fixed toolchain, and pin the Go version in CI.
Don’t disable certificate verification (
InsecureSkipVerify) to get around anything related to this change.Check the list yourself if you want to be sure. In a fixed toolchain:
grep -A12 distrustedSPKIHashes "$(go env GOROOT)/src/crypto/x509/verify.go"And the hash of a certificate you hold:
openssl x509 -in cert.pem -noout -pubkey \ | openssl pkey -pubin -outform DER \ | openssl dgst -sha256