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

IDCVE-2026-111943
Componentcrypto/x509 (Certificate.Verify)
WeaknessCWE-295, improper certificate validation
Fixed inGo 1.26.10 and 1.27.3
SeveritySources 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:

  1. I checked the bundle first. openssl dgst -sha384 -verify <name>.pub.pem -signature <name>.sig message.txt printed Verified OK for all eight, and each public key matched the one in its certificate.
  2. I computed the SHA-256 of the DER SubjectPublicKeyInfo of each .crt.pem, the same input containsDistrustedSPKIHash uses.
  3. I took distrustedSPKIHashes from src/crypto/x509/verify.go at tag go1.27.3.

The result is an exact match: eight of eight, and nothing extra in Go’s list.

CertificateSHA-256 of SPKI
EM DV TLS CA - G3Bebed6475c2c2d80fcb199f3fe8dd6e6b3ce60b77eb15d4e42c3dec0c6f5b5105
EM DV TLS CA - G3C49dd353dc2b919a3770645117d0e60c16115e7d1bc259eefe734b00ea434fe5f
EM EV TLS CA - G3Be0254c7fcdc265e2f0f8a428dabb75ab4ae039b4872d62f5ee57f0877d5a8a41
EM EV TLS CA - G3C5c0660a2b164910d8ff589c18e14552e9e0ec237dcefe2cb736d41a4811f81a0
EM OV TLS CA - G3B43bffa19d6ef651451f6841e019931e96f6f2eaeff28ab4b6f1ce4576de2717b
EM OV TLS CA - G3C63ecefee431d49523bc1136c4e72d0206a477086caf606cd5cf025686c109cf8
EM OV CS CA - G3Bec4ffa1ad0ac88081314b7aaffef6a91f97cd57291bb5ba686c5415f5eb4f6b6
EM OV CS CA - G3C92c063d67e8f14db71d288e11266ffdaf69cbf115f7bab14f3bc2f68d25e0e30

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/x509 doesn’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

  1. Find the toolchain each binary was built with:

    go version -m ./your-binary | head -1
    

    Anything 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.

  2. Rebuild and redeploy with a fixed toolchain, and pin the Go version in CI.

  3. Don’t disable certificate verification (InsecureSkipVerify) to get around anything related to this change.

  4. 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
    

References