Programming languages
Java PKIX path building failed: causes and fixes
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested targetPKIX 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
issueris 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 -showcertsshows only the leaf certificate, or the chain builder reports a missing intermediate. - Fix: serve leaf plus intermediates (
fullchain.pemin 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:handshakeor checkopenssl s_clientfrom 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:trustmanagerprints 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(emptycheckServerTrusted) or aHostnameVerifierthat returnstrue. 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.