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