Import a certificate into the Java truststore (cacerts)

When a Java application talks to a server whose CA it does not know, you get “PKIX path building failed”. The fix is to add the CA to the truststore. There is a quick way and a lasting one.

Where is cacerts?

The default password is changeit. Which file a running application really uses is shown by -Djavax.net.debug=ssl:trustmanager.

Which certificate to import?

Import your company’s root CA (or the issuing intermediate CA), not the server certificate. A server certificate becomes invalid at every change; the CA lasts for years. Get the CA from IT or from the server’s chain. The SSL check shows all certificates and downloads them as PEM.

The keytool command

# Java 9 and later
keytool -importcert -cacerts -alias company-root -file company-root.crt -storepass changeit -noprompt

# Java 8
keytool -importcert -keystore "$JAVA_HOME/jre/lib/security/cacerts" \
  -alias company-root -file company-root.crt -storepass changeit -noprompt

# check and remove again
keytool -list -cacerts -alias company-root -storepass changeit
keytool -delete -cacerts -alias company-root -storepass changeit

The file may be PEM or DER. On Linux and macOS you usually need sudo, because cacerts belongs to the JDK. Restart the application afterwards; Java only reads the truststore at startup.

The lasting way on Linux

A JDK update replaces cacerts and your change is gone. The packages of Debian, Ubuntu, RHEL and Fedora therefore generate cacerts from the system certificate store. Add the CA there and it applies to Java, curl, Python and everything else at once:

# Debian, Ubuntu (extension .crt, PEM format)
sudo cp company-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# RHEL, Rocky, Alma, Fedora
sudo cp company-root.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

That works for the distribution’s Java packages. A manually unpacked JDK (e.g. under /opt) brings its own cacerts. Either change that one with keytool, or point to the system store with -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts (Debian) or /etc/pki/ca-trust/extracted/java/cacerts (RHEL).

In Docker images

The official eclipse-temurin images can pick up CAs at startup. Put the certificates into /certificates and set USE_SYSTEM_CA_CERTS=1:

docker run -e USE_SYSTEM_CA_CERTS=1 -v ./ca:/certificates:ro my-app

# or baked into the image
FROM eclipse-temurin:21-jre
COPY company-root.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

With other base images, use the keytool command from above in the Dockerfile.

A separate truststore instead of cacerts

If you do not want to touch the JDK, give the application its own truststore. Important: it replaces cacerts, so it contains only what you put in. The easiest way is to copy cacerts and add the CA:

cp "$JAVA_HOME/lib/security/cacerts" truststore.p12
keytool -importcert -keystore truststore.p12 -storepass changeit -alias company-root -file company-root.crt -noprompt
java -Djavax.net.ssl.trustStore=truststore.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar

A truststore with only specific CAs can be built in the Java keystore helper under “PEM & CA reply”: paste the CA certificates, save as truststore, done.

FAQ

Do I have to change the changeit password?

Not necessarily for cacerts. It only protects against modification, not reading, and the contents are public certificates. What matters is that only administrators can write the file.

“Certificate was added to keystore”, but nothing changed?

Usually a different cacerts was changed than the one the application uses, e.g. with several JDKs installed. Check the file with -Djavax.net.debug=ssl:trustmanager.

Does this apply to Tomcat, Jenkins or Maven too?

Yes, they all use the truststore of the Java runtime they are started with. For Maven that is the JVM from JAVA_HOME or MAVEN_OPTS.

Can I use the company CA from the Windows store?

Yes, with -Djavax.net.ssl.trustStoreType=Windows-ROOT Java reads the Windows certificate store. No import needed.