
Checks Java and system trust stores for eMudhra CA certificates affected by CVE-2026-91403 by probing TLS test hosts, then reports vulnerable entries and keytool removal commands.
Checkers for Oracle Security Alert CVE-2026-91403.
CVE-2026-91403 is a remotely exploitable (CVSS 3.1 base 9.1, "Network / Low complexity / No privileges / No user interaction") issue in the JSSE component of Oracle Java SE, Oracle GraalVM for JDK and Oracle GraalVM Enterprise Edition.
Oracle's fix is to distrust the impacted intermediate CA certificates related to eMudhra. Any JVM or system trust store that still trusts those certificates will keep accepting TLS chains issued by them, which allows an attacker to impersonate affected hosts.
Affected products and versions (per the alert):
| Product | Versions |
|---|---|
| Oracle Java SE | 8u511, 8u511-perf, 11.0.33, 17.0.21, 21.0.13, 25.0.5, 27.0.1 |
| Oracle GraalVM for JDK | 17.0.21, 21.0.13 |
| Oracle GraalVM Enterprise Edition | 21.3.20 |
Oracle's guidance is to update to the latest Java version that distrusts these certificates, or to apply the mitigations described at https://ops.java/emudhra. This repo instead verifies whether a given trust store is still affected, and tells you which entries to remove if you have to patch it in place.
It answers one question: does this trust store still accept chains built on the leaked eMudhra CA
certificates? To do that it connects to public test hosts under emudhra.we-have-a-problem.com
(DV/OV/EV G3B/G3C via both the emSign and AAA root paths, plus non-CT variants) and reports whether
the TLS handshake succeeds (still trusted → vulnerable) or is rejected.
Exit codes used by all three tools:
| Code | Meaning |
|---|---|
0 | SAFE — every test host was rejected |
2 | VULNERABLE — at least one test host was accepted |
3 | UNCERTAIN — nothing was accepted, but some hosts could not be checked (egress/DNS/proxy) |
Each run also prints a SUMMARY=<verdict> trusted=<n> rejected=<n> unknown=<n> line for scripting.
| File | Purpose |
|---|---|
EmudhraCheck.java | The checker itself. Probes the test hosts with the JVM's own trust store (-Djavax.net.ssl.trustStore, or the bundled jssecacerts/cacerts), then prints a recap of which roots must be deleted. Java 8+ bytecode. |
check-swarm-images.sh | Runs EmudhraCheck against every Docker Swarm service image, using each image's own JVM. Run on a manager node (and on each worker). |
emudhra-check.sh | curl-based equivalent for non-Java containers/hosts. |
Check the current JVM (JDK 11+, no compile step needed):
java EmudhraCheck.java
After compiling to Java 8 bytecode:
javac --release 8 -d out EmudhraCheck.java
java -cp out EmudhraCheck
Custom hosts, or via the environment:
java -cp out EmudhraCheck https://host1/ https://host2/
EMUDHRA_URLS="https://host1/ https://host2/" java -cp out EmudhraCheck
Scan all Swarm service images (on a manager node):
bash check-swarm-images.sh
# mimic the service egress path and JVM options:
NETWORK=my-attachable-overlay \
EXTRA_JAVA_OPTS="-Djavax.net.ssl.trustStore=/etc/ssl/custom.jks -Dhttps.proxyHost=proxy" \
bash check-swarm-images.sh
Non-Java container:
sh emudhra-check.sh
Passing the same JVM options the service uses (trust store, proxy, …) is what makes the result reflect that service — and it always reflects the trust store of the JVM/image being tested, not the one running the scanner.
EmudhraCheck loads the trust store in use and matches its entries against Oracle's removal list
(emSign Root CA - G1, emSign Root CA - G2, emSign ECC Root CA - G3, AAA Certificate Services).
It then prints:
keytool commands, including a backup copy of the trust store.Preferred: update to a Java release that contains Oracle's fix (see the Patch Availability Document,
Oracle support document CPU426) and roll the service.
If you must patch the trust store in place (back up first, and remember aliases may contain spaces
or [jdk], so quote them):
keytool -delete -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit -alias "<alias>"
Then restart the JVM/service and re-run EmudhraCheck.java — it must report SUMMARY=SAFE.
check-swarm-images.sh only inspects images already present on the node (--pull never);
run it on every worker, and test images without java in PATH with emudhra-check.sh.UNCERTAIN is not safe: it means the test hosts were unreachable, so allow outbound access to
them or check from the network the service actually uses.we-have-a-problem.com; the checker only
performs a TLS handshake and does not send or download any payload.