Java's TLS stack validates certificates using the PKIX algorithm against its own truststore, not against the operating system's. This exception means Java could not build a chain from the server's certificate to any root in that truststore:
javax.net.ssl.SSLHandshakeException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
The causes are the same as in other languages (missing intermediate, private CA, TLS-inspecting proxy, outdated roots), but the fix is Java specific because you change a keystore file or JVM flags.
Step 1: see what the server sends
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
| grep -E "^ *[0-9] s:|^ *i:|Verify return code"
- If one certificate is sent and the verdict is code 20 or 21, the server omits its intermediate. Java will not download it for you. Fix the server: incomplete certificate chain.
- If the issuer is a company or security-product name, a proxy is re-signing traffic. You need to trust that root.
- If the chain looks normal and public, the JDK on that machine probably has an old or damaged
cacerts.
Step 2: find which truststore this JVM uses
Without flags, Java uses lib/security/cacerts inside the JDK or JRE (in Java 8 it is jre/lib/security/cacerts). Different JDKs on one machine have different files, and a Docker image or an IDE may use another JDK from the one you tested.
java -XshowSettings:properties -version 2>&1 | grep -E "java.home|java.version"
ls "$JAVA_HOME/lib/security/cacerts"
keytool -list -cacerts | head # Java 9+ shortcut; default password is changeit
Check for overrides in JAVA_TOOL_OPTIONS, JAVA_OPTS, or application startup flags containing -Djavax.net.ssl.trustStore. A custom truststore that holds only your private CA, with no public roots, causes this error for every public site.
Step 3: get and import the missing certificate
Export the CA (or the proxy root) as PEM, then import it:
sudo keytool -importcert -trustcacerts -alias corp-root \
-file corp-root.pem \
-keystore "$JAVA_HOME/lib/security/cacerts" \
-storepass changeit
changeit is the conventional default password for the bundled cacerts; if your distribution changed it, use that one. Import the root or intermediate CA, not the server's leaf certificate, so renewals keep working.
Distribution JDKs may overwrite cacerts on upgrade. A safer approach for applications is a separate truststore:
keytool -importcert -alias corp-root -file corp-root.pem \
-keystore /etc/myapp/truststore.p12 -storetype PKCS12 -storepass changeit
java -Djavax.net.ssl.trustStore=/etc/myapp/truststore.p12 \
-Djavax.net.ssl.trustStorePassword=changeit \
-Djavax.net.ssl.trustStoreType=PKCS12 -jar app.jar
Remember that setting trustStore replaces the default store for that JVM. To keep public roots, copy the default cacerts first and add to the copy.
Containers and build tools
- Dockerfile: copy the PEM into the image and run
keytool -importcert ... -cacertsduring the build, or mount a truststore and pass the system properties. - Maven or Gradle failing to download: they run on a JVM too, so the build machine's
cacertsneeds the proxy root. Look atMAVEN_OPTSandGRADLE_OPTS. - Debian and Ubuntu: the
ca-certificates-javapackage syncs the system store into the JVM store when you runsudo update-ca-certificates, so installing a PEM under/usr/local/share/ca-certificates/with a.crtextension may be enough.
Debug the handshake
Turn on JSSE logging to see the certificates presented:
java -Djavax.net.debug=ssl:handshake:verbose -jar app.jar 2>&1 | grep -iE "certificate|issuer|alert"
The output is large; limit it to a short reproduction. You will see each certificate subject and issuer, which tells you where the chain breaks.
Related Java errors
No subject alternative names matching IP address ... foundorNo subject alternative DNS name matching x found: hostname mismatch, covered in certificate name mismatch.Received fatal alert: handshake_failure: protocol or cipher disagreement. See sslv3 alert handshake failure.NotAfterin the debug output: an expired certificate in the chain.
Verify
Rerun the failing call and confirm it completes. List the imported entry with keytool -list -keystore ... -alias corp-root, and check the server from outside with the SSL Chain Debugger so you know whether other clients will hit the same problem.
अगले कदम
- Compare with Go x509 unknown authority.
- Read SSL/TLS certificate errors explained.
- Check expiry across hosts with the Bulk SSL Checker.
- Check supported protocol versions with the TLS वर्शन चेकर.
सामान्य प्रश्न
Why does Java not use the operating system's certificates?
Historically the JDK ships and manages its own cacerts file. Some distributions link it to the system store, but you should not assume it.
What is the password for cacerts?
The default for the bundled file is changeit. Treat it as a file-integrity password, not a secret, and check your distribution if it fails.
Should I import the server certificate or the CA?
Import the CA (root or intermediate). A leaf certificate expires with each renewal, so importing it needs repeating.
It works in my browser. Why not Java?
Browsers fetch missing intermediates and use the OS store. Java does neither, so a server with an incomplete chain breaks Java alone.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
यह पेज सामान्य जानकारी है, जिसे हमारी संपादकीय टीम ने लिखा है और दस्तावेज़ीकरण बदलने पर अपडेट किया जाता है। सर्वर और प्रदाता अलग-अलग होते हैं, इसलिए बदलाव पहले सुरक्षित तरीके से जाँचें। देखें: संपादकीय नीति और अस्वीकरण.