Workaround for the eMudhra CA Certificate Compromise
On October 20, 2026, Mozilla publicly disclosed that the private keys of eight eMudhra intermediate CA certificates were compromised. Azul Zing Builds of OpenJDK (Zing) trust the root certificates these intermediates chain to and don’t check revocation by default, so Zing would accept certificates made with those keys.
Azul Prime 26.08.101.0 and 26.02.501.0 contain the fix. See the Release Notes for details. We recommend that you upgrade as soon as possible.
If you cannot upgrade immediately, replace the block list file in every Zing installation with the updated one below, then restart the Java applications that use it. Browsers already block these certificates, so legitimate connections should not be affected.
1. Download the File
| Platform | Download |
|---|---|
Linux and containers |
emudhra-block-unix.certs |
Don’t use a file whose checksum doesn’t match. Don’t open or save it in a text editor: some editors add an invisible marker that makes Java ignore the whole list.
2. Find the File to Replace
The new file replaces the one with the same name in the installation’s lib/security directory.
You can find it in the following default locations:
| Installation | Directory |
|---|---|
DEB or RPM package |
|
TAR.GZ archive |
|
On a Zing 8 JDK, add jre/ before lib/security.
For other installs, run that installation’s java -XshowSettings:properties -version and use the java.home value followed by /lib/security. Appendix B: Find Every Zing Installation lists every Zing installation on a machine.
If the directory contains neither blocked.certs nor blacklisted.certs, contact Azul Support.
3. Replace the File
In every case, back up the original as <file>.bak-emudhra, copy the new file over it (a moved file can keep permissions Java can’t read), and restart every Java application that uses the installation.
The examples use Zing 17 and blocked.certs.
Linux
Run the script from Appendix A: Linux Script once per installation:
sudo sh zing-emudhra-workaround.sh /usr/lib/jvm/zing-jdk17/bin/java
Or by hand:
cd /usr/lib/jvm/zing-jdk17/lib/security
curl -fsSLo /tmp/blocked.certs https://docs.azul.com/assets/2026-10/emudhra-block-unix.certs
sha256sum /tmp/blocked.certs # must match step 1
sudo cp -p blocked.certs blocked.certs.bak-emudhra
sudo cp /tmp/blocked.certs blocked.certs && sudo chmod 644 blocked.certs
Containers
Patch the image or the deployment, not a running container. Find the file’s path in the image:
id=$(docker create <image> x)
docker export "$id" | tar -tvf - | grep -E 'lib/security/(blocked|blacklisted)\.certs'
docker rm -v "$id"
Then rebuild the image:
FROM <image>
COPY --chmod=644 blocked.certs /<path from the listing>
Or mount the file without rebuilding, for example in Kubernetes:
kubectl create configmap zing-blocked-certs --from-file=blocked.certs
# in the pod template
containers:
- name: <container>
volumeMounts:
- name: zing-blocked-certs
mountPath: /<path from the listing>
subPath: blocked.certs
readOnly: true
volumes:
- name: zing-blocked-certs
configMap:
name: zing-blocked-certs
With Docker, run chmod 644 on the host file and add -v /opt/blocked.certs:/<path from the listing>:ro to docker run, using :ro,z on SELinux hosts.
Other Environments
CI jobs that download Zing: run the script right after the JDK is set up, for example sh ./zing-emudhra-workaround.sh "$JAVA_HOME/bin/java".
Runtimes built with jlink: copy the file into the runtime’s lib/security after every build.
Patching the JDK doesn’t help, because jlink takes the file from jmods.
Applications that bundle Java: update lib/security in your own builds.
For vendor applications, ask the vendor.
CRaC: create new checkpoints after the change. Existing checkpoints keep the old list.
4. Verify
The installed file’s SHA-256 must match step 1, and the applications must have restarted since the change. The script checks the checksum itself.
sha256sum <directory>/blocked.certs
Container image (for a running container or a mounted file, use the container’s name instead of $id):
id=$(docker create <image> x)
docker cp -L "$id:/<path from the listing>" - | tar -xOf - | sha256sum
docker rm -v "$id"
5. Undo, and After the Fix
To undo, copy <file>.bak-emudhra back over the file and restart the applications.
The permanent fix is available in Azul Prime 26.08.101.0 and 26.02.501.0.
After upgrading, delete the backups and remove the COPY lines, mounts, and CI steps added for this workaround.
Left in place, they would hide future updates to the list.
A Zing update without the fix restores the original file. Apply the workaround again after such an update.
Appendix A: Linux Script
Save as zing-emudhra-workaround.sh.
It checks that the runtime is Zing, picks the right file, verifies the checksum, keeps a backup, and restores it if the update fails.
It’s safe to run twice.
#!/bin/sh
# Applies the eMudhra workaround to one Azul Zing installation.
# Usage: sudo sh zing-emudhra-workaround.sh /full/path/to/bin/java
set -eu
URL="https://docs.azul.com/assets/2026-10/emudhra-block-unix.certs"
EXPECTED="2596bb6dda83c66d96c5a252d7b2ae1f24cc1ae3f1890223b4f3591a8d52bf10"
JAVA_BIN="${1:-java}"
sha256() { sha256sum "$1" | cut -d' ' -f1; }
if ! "$JAVA_BIN" -version 2>&1 | grep -q 'Zing'; then
echo "ERROR: $JAVA_BIN is not an Azul Zing runtime." >&2; exit 1
fi
JAVA_HOME_DIR=$("$JAVA_BIN" -XshowSettings:properties -version 2>&1 | sed -n 's/^ *java\.home = //p')
if [ -z "$JAVA_HOME_DIR" ]; then
echo "ERROR: could not determine java.home for $JAVA_BIN." >&2; exit 1
fi
SEC_DIR="$JAVA_HOME_DIR/lib/security"
if [ -f "$SEC_DIR/blocked.certs" ]; then
FILE=blocked.certs
elif [ -f "$SEC_DIR/blacklisted.certs" ]; then
FILE=blacklisted.certs
else
echo "ERROR: no blocked.certs or blacklisted.certs in $SEC_DIR. Contact Azul Support." >&2; exit 1
fi
TARGET="$SEC_DIR/$FILE"
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
curl -fsSL -o "$TMP" "$URL"
if [ "$(sha256 "$TMP")" != "$EXPECTED" ]; then
echo "ERROR: checksum mismatch for the downloaded $FILE. Nothing was changed." >&2; exit 1
fi
[ -e "$TARGET.bak-emudhra" ] || cp -p "$TARGET" "$TARGET.bak-emudhra"
cp "$TMP" "$TARGET"
chmod 644 "$TARGET"
if [ "$(sha256 "$TARGET")" = "$EXPECTED" ]; then
echo "OK: updated $TARGET (original saved as $FILE.bak-emudhra)."
echo "Restart all Java applications that use $JAVA_HOME_DIR."
else
cp -p "$TARGET.bak-emudhra" "$TARGET"
echo "ERROR: $TARGET was not updated correctly; the original was restored." >&2; exit 1
fi
Appendix B: Find Every Zing Installation
This command lists Azul installations (Zing and Zulu) by reading their release files; it doesn’t run anything it finds.
The same block list file applies to both. For an UNKNOWN line, check whether that installation’s java -version mentions Zing or Zulu.
sudo sh -c 'find / \( -path /proc -o -path /sys -o -path /dev -o -path /run/containerd -o -path /var/lib/docker -o -path /var/lib/containerd -o -path /var/lib/containers \) -prune -o -type f \( -name blocked.certs -o -name blacklisted.certs \) -path "*/lib/security/*" -print 2>/dev/null | while IFS= read -r f; do h=${f%/lib/security/*}; if grep -qsE "Azul|Zing|Zulu" "$h/release" "$h/../release"; then echo "Azul: $f"; elif ! grep -qs "^IMPLEMENTOR=" "$h/release" "$h/../release"; then echo "UNKNOWN: $f (no vendor in release file; check by hand)"; fi; done'