0

So, you have encountered the fabled PKIX Path Building issue. Does it look something like this?

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed

I bet it does. I sincerely hope you've found this SO question before spending hours frustratedly and futilely foraging the internet for ways to save yourself.

Let's go over at a high level what the problem is, and then dig into concrete solutions. (Credit Atlassian).

Disclaimer for those eager folks looking to mark this as a duplicate: there are many SO questions related to PKIX path building, but all of them beat around the core issue. From personal experience running into this issue (and spending many hours trying to resolve it) I know how hard it is to filter out the noise. I'm authoring this question in an effort to simply and elegantly provide a description of what causes the error, and how to most quickly resolve it. Feel free to edit to add meaningful detail where helpful.

M. Irvin
  • 143
  • 1
  • 13

1 Answers1

0

Java Certs

Whenever Java attempts to connect to another application over TLS (SSL) (e.g.: HTTPS, IMAPS, LDAPS), it will only be able to connect to that application if it can trust it. The way trust is handled in the Java world is that you have a keystore (typically $JAVA_HOME/lib/security/cacerts), also known as the truststore. This contains a list of all known Certificate Authority (CA) certificates. Java will only trust certificates that are signed by one of those CAs or certificates that already exist in that keystore.

This problem is therefore caused if the server presents to you (the client) a certificate that is self-signed (a CA did not sign it) or a certificate that does not exist within your Java truststore. Java does not trust the certificate and fails to connect to the application. This leaves you feeling sad, and staring at a PKIX path building failed error.

Solution

In general, the solution to this problem is to get the public cert of the host you're trying to make a request to, and add that cert to your tru ststore. Sounds easy, right? It should be.

1. Getting the cert

You can fetch the public cert of a host using the following: openssl s_client -connect <host>:<port>. This should output to the console the list of certificates and other nonsense for the given host and port. What you want to do is find and copy the lines from BEGIN CERTIFICATE to END CERTIFICATE. Alternatively, you can write this content directly to a file by extending your command a bit: openssl s_client -connect <host>:<port> < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > public.crt. The contents of public.crt should look like:

-----BEGIN CERTIFICATE----- 
< Certificate content as fetched by the command line.  Don't change this
 content, only remove what is before  and after the BEGIN CERTIFICATE and
 END CERTIFICATE.  That's what your Sed command is doing for you :-) >
-----END CERTIFICATE-----  

2. Importing the Cert into your truststore

You'll want to import your newfound public.crt into the truststore that is being used when the HTTP request is being made from wherever your java application is running. This can be done with something like <JAVA_HOME>/bin/keytool -import -alias <server_name> -keystore <path/to/truststore.jks> -file public.crt.

You can add the cert to the cacerts, usually somewhere like here <JAVA_HOME>/jre/lib/security/cacerts, or directly to the truststore.jks itself (this is what I've been doing recently).

Once this is done, you (the client) now trusts the certificate the server will present, and you'll be able to make your http request. If you're still getting the error, the most likely cause is that you're not adding the cert to the correct truststore. See this SO post for where the truststore usually lives.```

Loosely citing:

Atlassian

M. Irvin
  • 143
  • 1
  • 13