Programming languages

Java PKIX path building failed: causes and fixes

By · Last verified

Error messagejavax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

PKIX path building failed ... unable to find valid certification path to requested target means the JVM could not connect the server's certificate to any trusted root in its truststore. The usual culprits are a missing intermediate on the server, a proxy that re-signs HTTPS, or an internal CA missing from cacerts.

Quick fix

A one-certificate chain means fixing the server; a full chain to an unknown root means importing that root.

Decide whether the server or the JVM is wrong before importing anything:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
  • Only one certificate in the output and Verify return code: 21 (unable to verify the first certificate): the server chain is incomplete. Configure the full chain on the server (see missing intermediate certificate).
  • Full chain, but the top issuer is your company, a proxy vendor or an internal CA: the JVM does not trust that root. Import it into the truststore of the JVM that fails:
# JDK 9 and later
keytool -importcert -cacerts -alias company-root -file company-root.pem -storepass changeit -noprompt
# JDK 8
keytool -importcert -keystore "$JAVA_HOME/jre/lib/security/cacerts" -alias company-root -file company-root.pem -storepass changeit -noprompt

Restart the application afterwards; the truststore is read once at startup.

Causes

1. The server does not send its intermediate certificate

Java never downloads missing intermediates on its own, so a chain that works in Chrome can fail in Java.

  • Confirm: openssl s_client -showcerts shows only the leaf certificate, or the chain builder reports a missing intermediate.
  • Fix: serve leaf plus intermediates (fullchain.pem in Nginx, the full chain file in Apache, Tomcat or your load balancer).

2. A TLS-inspecting proxy or security gateway

Corporate proxies decrypt HTTPS and present certificates signed by a private root. Browsers trust it because IT pushed the root into the OS store; Java does not read the OS store.

  • Confirm: run with -Djavax.net.debug=ssl:handshake or check openssl s_client from the same machine; the issuer names the proxy product or your company.
  • Fix: import the proxy root into cacerts, or on Windows use -Djavax.net.ssl.trustStoreType=Windows-ROOT.

3. Internal CA or self-signed certificate

Internal APIs, Nexus or GitLab instances often use an internal CA.

  • Fix: import the internal root (not the leaf) once per JVM or, better, build a dedicated truststore and point the application at it:
keytool -importcert -keystore app-truststore.p12 -storetype PKCS12 -storepass changeit -alias company-root -file company-root.pem -noprompt
java -Djavax.net.ssl.trustStore=app-truststore.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar app.jar

javax.net.ssl.trustStore replaces cacerts, so a custom truststore must also contain the public roots your app needs.

4. An old JDK with an outdated cacerts

Old JDK builds lack newer roots, so certificates from a CA that switched to a newer root fail.

  • Fix: update to a current JDK release of your major version; each update ships a refreshed cacerts.

5. The app loads a different truststore than you think

Frameworks and servers can set their own truststore (Tomcat connectors, Spring configuration, Kafka or Elasticsearch clients).

  • Confirm: -Djavax.net.debug=ssl:trustmanager prints which truststore was loaded and which certificates it contains.

Fixes by environment

Add the root to the truststore the JVM loads; on most Linux distributions that means the system store.

Debian / Ubuntu OpenJDK: cacerts is generated from the system store by ca-certificates-java, so adding the root system-wide also updates Java:

sudo cp company-root.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

RHEL / Rocky / Fedora: copy the root to /etc/pki/ca-trust/source/anchors/ and run sudo update-ca-trust; the system OpenJDK reads /etc/pki/ca-trust/extracted/java/cacerts.

Docker images: bake the root into the image so every container starts with it:

COPY company-root.pem /tmp/company-root.pem
RUN keytool -importcert -cacerts -alias company-root -file /tmp/company-root.pem -storepass changeit -noprompt

Windows: use Windows-ROOT as above, or run keytool from an elevated prompt, because cacerts under Program Files is write-protected.

Build tools: Maven and Gradle show the same error when downloading dependencies; see Maven PKIX path building failed.

Don't do this

  • A trust-all X509TrustManager (empty checkServerTrusted) or a HostnameVerifier that returns true. Copied from forum answers, these turn off authentication completely, and they tend to survive into production.
  • Importing the leaf certificate instead of the root. It hides the real problem and breaks again at the next renewal.
  • Deleting or replacing cacerts with a file from another machine or an old JDK. You lose current roots and any later JDK update overwrites your change.

FAQ

Where is the Java cacerts file?

On JDK 9 and later it is $JAVA_HOME/lib/security/cacerts; on JDK 8 it is $JAVA_HOME/jre/lib/security/cacerts. Debian and Ubuntu link it to /etc/ssl/certs/java/cacerts, and RHEL-family systems to /etc/pki/ca-trust/extracted/java/cacerts. The default password is changeit.

I imported the certificate but still get PKIX path building failed. Why?

Usually the failing program runs on a different JVM than the one you edited: an IDE's bundled JDK, a JDK inside a Docker image, or a second Java on the PATH. Print System.getProperty("java.home") from the app, or check mvn -v / gradle -v, and import into that JVM's cacerts. Also check that nobody set -Djavax.net.ssl.trustStore, which replaces cacerts entirely.

Should I import the server's leaf certificate or the root?

Import the root CA (or your company's proxy root). A leaf certificate changes at every renewal, which at today's validity periods means every few months, so trusting the leaf breaks again soon. If the server is missing its intermediate, fix the server instead of importing anything.

Can Java use the Windows certificate store?

Yes. On Windows, start the JVM with -Djavax.net.ssl.trustStoreType=Windows-ROOT to trust the roots in the Windows store, which is convenient when IT already deploys a proxy root through Group Policy.

Sources

  1. keytool (Java SE 21 tool reference)
  2. Java Secure Socket Extension (JSSE) Reference Guide
  3. openssl-s_client (OpenSSL 3.0)