In October 2026, the private keys of eight eMudhra intermediate CA certificates
were found to be trivially recoverable (P-384 keys generated with private scalars
below 256). They were revoked cACompromise on 2026-10-18 and distrusted by the
Mozilla, Chrome, Microsoft and Apple root programs — see
Mozilla Bug 2083449.
Because a leaked private key cannot be un-leaked, anyone can mint a validly-signed certificate for any domain (6 TLS keys) or any publisher (2 code-signing keys) that chains to these intermediates. Such a forgery is accepted by any client that still trusts the emSign G3 roots and has not received the distrust push — and a forged leaf need carry no revocation pointer, so a client that only checks the leaf's revocation will not catch it. This repo helps you detect and deny that path.
This repository deliberately contains no private keys — only the public certificates, their fingerprints, and tooling to distrust them.
| CA | Type | SHA-256 | Serial |
|---|---|---|---|
| EM DV TLS CA - G3B | TLS | 42c4accd…ba1493 |
A4AEE678F23362B4C0E460B772F214 |
| EM DV TLS CA - G3C | TLS | 0164d7ce…934d19 |
588B2E3C2BB8CDA13E1A1209254321 |
| EM EV TLS CA - G3B | TLS | e7568fe6…333838 |
C7C41EFE92D825F05EAE841A4A82C1 |
| EM EV TLS CA - G3C | TLS | 432ec25f…0261f3 |
8009928A64107601AF05FA2F19F109 |
| EM OV TLS CA - G3B | TLS | 98b89662…83bc25b |
6CADA367FB1C100088120EA202D568 |
| EM OV TLS CA - G3C | TLS | cb6e1516…bf9c7c3f |
296926C227A3A1526392B19482A504 |
| EM OV CS CA - G3B | Code signing | 9e39de28…b067b4 |
7818933F8F85B4619A2B35FBA83D0A |
| EM OV CS CA - G3C | Code signing | 4b2460a4…630b1 |
544B7F922C31AE220A4A922916F6E6 |
Roots: emSign Root TLS CA - G3 (7dd78d5f…2746ed8d), emSign Root CS CA - G3
(f2edaaff…aba34a5). Full machine-readable list in indicators.csv,
PEMs in certs/.
Requires Python 3.10+ and openssl on PATH for distrust fingerprint checks.
pip install -e .
emsign-g3 scan
emsign-g3 export-indicators --verifyOverride data paths when needed: EMSIGN_G3_INDICATORS_CSV, EMSIGN_G3_CERTS_DIR.
Legacy entry points (python3 tools/…) remain as thin shims and work without install.
emsign-g3(CLI) —scan,distrust,export-indicatorssubcommands (see below).tools/emsign_trust_scanner.py— shim foremsign-g3 scan.tools/emsign_distrust.py— shim foremsign-g3 distrust.tools/emsign-g3-distrust.rules— Suricata/Snort rules that drop TLS whose leaf is issued by any of the eight (matchestls.cert_issuer). Deploy inline or behind a decrypting proxy. Verified to fire on Suricata 8.tools/suricata_livefire_test.sh— replays a recorded real-world handshake (tests/fixtures/) through the rules and checks they fire. No root, no network.tools/record_handshake_fixture.py— how that fixture was recorded: a TLS 1.2 handshake with a public researcher demo host, captured without root.src/emsign_g3/— installable package; loadsindicators.csvviaemsign_g3.indicators.tools/export_siem_indicators.py— shim foremsign-g3 export-indicators.
-
Ship
tools/emsign-g3-distrust.rulesto your Suricata sensor (inline IPS or tap IDS). -
For passive IDS, change rule actions from
droptoalertin the rules file. -
Validate offline before deploy:
tools/suricata_livefire_test.sh
Expect
PASSand sid92083401on the recorded DV G3B fixture. Suricata must parse TLS (tls_events> 0 in the script output). -
Correlate production alerts on
signaturecontainingBug2083449or sid92083401–92083408(seeindicators.csvsuricata_sidcolumn).
-
Inventory (read-only):
emsign-g3 scan emsign-g3 scan --json # MDM / fleet collection -
Plan explicit distrust of the eight intermediates (dry-run first):
emsign-g3 distrust sudo emsign-g3 distrust --apply
PEMs load from
certs/by default; fingerprints are checked againstindicators.csv. -
Roll back with
emsign-g3 distrust --undo(add--applyto execute).
-
Canonical table:
indicators.csv— eight compromised intermediates, three related trust anchors, SHA-256 fingerprints, serials, Suricata SIDs, andtls.cert_issuermatch strings. -
Bulk export:
emsign-g3 export-indicators emsign-g3 export-indicators --ndjson emsign-g3 export-indicators --verify # PEMs vs CSV emsign-g3 export-indicators --format sigma --output-dir rules/sigma/ emsign-g3 export-indicators --format elastic-kql --output-dir rules/elastic/ emsign-g3 export-indicators --check-committed-rulesCommitted copies live under
rules/sigma/andrules/elastic/(regenerate withtools/regenerate_detection_rules.sh).Use host fields (
sha256_fingerprint) against scanner/MDM output; use network fields (suricata.sid,tls_cert_issuer_match) against Suricataeve.json.Sigma and Elastic KQL exports are starter detections mapped to Suricata EVE (
tls.issuerdn,alert.signature_id). Tune field names before production alerting.
Tagged releases (v*) build wheels, attach indicator bundles and rule tarballs, and
publish to PyPI when trusted publishing is configured. See RELEASE.md.
pip install -e ".[dev]"
pytest
ruff check src tests
emsign-g3 export-indicators --verify
emsign-g3 export-indicators --check-committed-rules
tools/suricata_livefire_test.sh # optional; requires SuricataKeep indicators.csv in sync with
src/emsign_g3/data/indicators.csv (enforced by tests).
Trust-store distrust reaches browsers via OneCRL / CRLSet and OS CA updates. The long
tail — non-browser TLS clients, embedded devices, Java/Node/certifi bundles, code
signing — updates on its own schedule. Use the scanner to find exposed clients and the
distruster/IDS rules to deny the keys until every client has dropped the roots.