Visit Azul.com Support

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
SHA-256: 2596bb6dda83c66d96c5a252d7b2ae1f24cc1ae3f1890223b4f3591a8d52bf10

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

/usr/lib/jvm/zing-jdk<version>-<arch>/lib/security or /opt/zing/zing-jdk<version>-<arch>/lib/security, depending on the release and architecture

TAR.GZ archive

<uncompressed_prime_archive_directory>/lib/security

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'