Blog
TLS trust stores beyond the browser after eMudhra
The eMudhra CA incident (private key scalar 4 on an intermediate) exposed how different TLS client classes maintain independent trust stores. Browsers blocked the compromised intermediate quickly. Go runtimes, containers, VPN clients, and embedded systems did not.
Trust & PKI
The eMudhra incident — an intermediate CA issued with a private key scalar value of 4 — resolved quickly for browser users. Browser vendors updated their CA stores, flagged the intermediate as distrusted, and users saw a certificate error on subsequent connections. That resolution path works because browsers have centralised, frequently-updated, vendor-controlled trust stores.
Non-browser TLS clients do not share that remediation path. They maintain independent trust stores updated through different mechanisms on different schedules. An intermediate CA that is distrusted by Chrome may remain trusted by a Go microservice, a Docker container, a VPN client, or an embedded network appliance for days, weeks, or indefinitely.
TLS client trust architecture
Loading diagram…
Trust store update paths by client class
| Client class | Trust source | Distrust update mechanism | Typical update lag | eMudhra fix status |
|---|---|---|---|---|
| Chrome / Firefox / Edge / Safari | Browser vendor CA program | Browser auto-update (silent, frequent) | Hours to days | Blocked immediately after distrust |
| macOS native apps | macOS Keychain | OS security update (push via Software Update) | Days to weeks | Depends on macOS security update release |
| Windows native apps (CAPI) | Windows Certificate Store | Windows Update (automatic for most users) | Days to weeks | Depends on Windows CU release |
| Linux system apps (OpenSSL) | ca-certificates package | apt upgrade or yum update | Days — if admins run updates | Depends on distro patch release |
| Go applications | crypto/x509 + OS roots | Go runtime update required + OS CA package | Days to weeks — only if rebuilt | CVE-2026-111943; requires Go 1.27.3 / 1.26.10 |
| Java apps (JRE cacerts) | JRE cacerts bundle | JRE update | Weeks — JRE update cycles | Independent of OS patch |
| Python (certifi) | Mozilla CA bundle via certifi | pip install --upgrade certifi | Days — if updated | certifi release with updated bundle |
| Docker containers | Base image CA bundle at build time | Rebuild required — no runtime update | Weeks to months — container rebuild cycles | Exposed until base image is rebuilt |
| VPN clients | Vendor-bundled trust store | Vendor firmware / client update | Weeks to months | Vendor-dependent |
| Network appliances | Appliance trust store | Vendor firmware update | Months to indefinite | Many vendors have no update path |
| Embedded devices | Hard-coded or compiled bundle | Firmware update — if one exists | Months to never | May be permanently exposed |
The container exposure problem
Docker base images capture the CA trust store at build time. A container built from debian:12-slim in August 2026 has the ca-certificates package state from August 2026. When the eMudhra distrust is published and Debian ships an updated ca-certificates package, containers already deployed do not receive that update automatically.
The remediation path for containerised workloads: 1. Rebuild base images with current OS packages 2. Rebuild application images on top of the updated base 3. Redeploy containers in all affected environments
For organisations running ephemeral containers on short-lived deployments, this is operationally simple. For organisations running long-lived containers (weeks or months without restart), the trust store may be stale for months before the rebuild cycle addresses it.
Identifying stale container trust stores:
# Check the ca-certificates package date inside a running container
docker exec <container_id> dpkg -l ca-certificates
# Or check the build date from the base image
docker inspect <image_id> | jq '.[0].Created'
# Check what the container trusts (OpenSSL-based)
docker exec <container_id> openssl s_client -connect example.com:443 -showcerts 2>&1 | \
grep -E "verify|Acceptable|issuer"Go runtime specifics (CVE-2026-111943)
Go's crypto/x509 package can use either the OS trust store (the default on Linux and macOS) or a compiled-in bundle (used when the OS store is unavailable or when custom trust is configured). The vulnerability tracked as CVE-2026-111943 covers cases where:
- Go applications run in environments where the eMudhra intermediate remains in the OS trust store (pending OS security update)
- Go applications with statically compiled trust bundles do not automatically pick up OS store changes
The fix requires both: 1. An OS-level CA update that marks the eMudhra intermediate as distrusted 2. Go runtime update to 1.27.3 or 1.26.10 (which includes its own trust store update)
Go applications that embed their own CA bundle via x509.NewCertPool() require a code-level update to remove the compromised intermediate.
Operator enumeration mid-engagement
On a foothold, enumerate which trust stores are in play:
# Go application trust (check if they use OS store or embedded)
strings /path/to/go-binary | grep -i "certificate\|trust\|ca-bundle\|pem"
# Container base image age
cat /etc/os-release
dpkg -l ca-certificates 2>/dev/null || rpm -q ca-certificates 2>/dev/null
# Java cacerts (check when JRE was last updated)
ls -la $JAVA_HOME/lib/security/cacerts
keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep -i emudhraTLS trust store hygiene is not a one-time remediation — it is a continuous operational requirement. The eMudhra incident makes visible what is always true: the browser's distrust is not universal.
Sources
- eMudhra weak private key disclosure — Talos Intelligence (2026-10-21)
- CA/Browser Forum Baseline Requirements — CA/B Forum (2024-01-01)
- The Private Key Was Four — Unmitigated Risk (2026-10-20)