How to Trust an SSL Certificate in Java

To trust an SSL site in Java, fetch the root CA certificate and install it with Java’s keytool utility.

If I had £1 for every time I’ve had to troubleshoot SSL issues in Java, I’d be a millionaire by now. But fortunately there’s a process to get Java to trust SSL certificates, which works 99% of the time… every time. So, here’s how to get a Java app to connect to your HTTPS-protected app.

You might be interested in this if you’re a DevOps person and you’ve been thrown a Java app that you need to get working. (On behalf of all Java developers, I apologise.)

Firstly, let’s look at why this problem happens, and how you might encounter it.

Why Java won’t connect to your HTTPS website

If your Java program won’t connect to an HTTPS (SSL/TLS) website, it’s probably because it doesn’t trust the remote server’s certificate.

If it’s broken, you might see errors like:

  • “sun.security.validator.ValidatorException: PKIX path building failed”

  • SSLHandshakeException: “unable to find valid certification path to requested target”

Fortunately, you can fix this issue without touching a single line of Java code!

You need to add the website’s certificate, or its root CA certificate, into Java’s list of trusted certificates.

In the rest of this post, we’ll find out what these terms mean, and how to do it.

What does "PKIX path building failed" mean?

PKIX is the name of a standard which uses public-key infrastructure and so-called X.509 certificates to verify the identity of a server. It’s the most common way for websites to identify themselves on the internet.

When Java throws this error, it means there was some problem while trying to verify the server’s certificate.

Java’s list of trusted certificates

Java comes bundled with a list of root certificates that it trusts by default. (This is similar to how your web browser works.)

A root certificate is usually used to sign other certificates.

This diagram shows how certificates build up a chain of trust. By trusting the root certificate at the top, you also implicitly trust the certificates further down in the chain:

How a certificate chain works

How a certificate chain works (PKIX, X.509 certificates)

How a certificate chain works by Tom Donohue is licensed under CC BY-SA 4.0

Java’s list of trusted certificates is stored in a file called a truststore.

Trusted public root certificates

Most public websites on the internet are signed by one of the certificates in the truststore.

But if Java makes a request to a remote server, and it presents a self-signed certificate, or it presents a certificate that’s signed by an untrusted certificate authority (CA), then Java will abort, and throw one of those errors above.

You might see this if you’re connecting to an internal site – such as some internal corporate app – which might not be signed by a known CA.

To solve this problem, you need to tell Java to trust the certificate.

The way to do that is usually to add the root CA’s certificate (or its own certificate, if it’s self-signed) into Java’s truststore.

How to trust a new certificate

If I want Java to to connect to an SSL host, I usually follow these steps:

  1. Get the root certificate for the remote website. (Or, if it’s a self-signed certificate, just grab that instead.)

  2. Add the root certificate(s) into the default Java truststore.

  3. Relaunch the Java application.

What is Java's default truststore?

Java’s list of trusted certificates is stored in its default truststore. This file is usually called cacerts.

You can also create your own truststore, and configure Java to use it instead.

If you can’t (or won’t) modify Java’s default truststore file, you can set up your own separate Java truststore and add certificates into it. But I tend to avoid doing that, unless it’s really necessary.

Usually, all Java apps on the same server need to trust the same set of certificates. So the most pain-free way to do that, is to add certificates into the default truststore, so that all apps can benefit.

Now we’ve seen a bit about Java’s approach to SSL and TLS! Let’s see how you get the certificate, and then install it.

How to fetch the certificate if you don’t have it

If a certificate file hasn’t been given to you, you can make an empty request to the web site, and save the certificates that it presents.

  • If the website is presenting a self-signed certificate, you can just use it.

  • If the website presents a certificate signed by someone else, you usually want to go “up the chain” and add the root certificate.

We’ll do this next.

Wait… Are you connecting to an internal app?

If you’re connecting to an internal website, like some internal corporate application, then it’s probably using a certificate signed by your company’s internal certificate authority (CA).

So, talk to your friendly security people first – they should be able to give you the right certificate to install, in PEM or CER format. (It’s sometimes called a “root CA” or perhaps “intermediate” certificate.) 🔐

Grab all the certificates with openssl (easy one-liner)

You can get the chain of certificates by making a real request.

OpenSSL is a really handy tool to do this. It’s a command line tool for creating secure connections to hosts (like web servers).

You can use the openssl command to open an SSL connection to a website, fetch the certificates (which is sometimes called an X.509 certificate). and write them to a file.

Use openssl to get all the certificates

To get all the certificates in an SSL request, you can use the openssl s_client command.

For this example, we’ll get the certificates for https://untrusted-root.badssl.com/. This example website presents a certificate which is signed by a root CA, but not a well-known one! So this example resembles the situation where you want to connect to a server, but its certificate is signed by your company’s (internal) root CA.

(I’ve used port 443 in the command below, because 443 is the default secure HTTP port. But, if your website or application is serving on a different port, then change ‘443’ to whatever port you’re connecting to)

Here’s a suggested one-liner that will save all of the certificates the server presents:

REMOTE_HOST=untrusted-root.badssl.com

echo \
    | openssl s_client -showcerts -partial_chain \
    -servername ${REMOTE_HOST} -connect ${REMOTE_HOST}:443 \
    | awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; out="cert"a".pem"; print >out}'

(Thanks to estani on Stack Overflow for this!)

How does this command work?

  1. First we use the echo command to send an empty request to openssl

  2. openssl connects to the host (e.g. untrusted-root.badssl.com) with our empty request ("") and outputs some connection information (including the all-important certificates!)

  3. The output from this command is piped to the text processing command awk, which extracts each certificate block (identified by the words ‘BEGIN’ and ‘END’) into a new file.

In this example, the files end in .pem, but you can call them whatever you like. PEM is just the convention people use for these types of files, because they are certificates in “PEM” format (which means a base64-encoded certificate).

How to look at the certificates 🔍

Once you’ve run the command above, you’ll probably get a few files. These are the certificates.

If there are many certificates in the chain, you’ll get several files here.

You can have a look at each certificate, with the openssl x509 command:

openssl x509 -in cert1.pem -noout -text
openssl x509 -in cert2.pem -noout -text

This will print lots of useful info, like:

  • The expiry (validity) of the certificate

  • The subject of the certificate (e.g. the owner, or the hostname)

  • The issuer of the certificate (basically who signed it)

Identifying the root certificate

We want to identify and trust the root certificate.

When you run the command above with the second PEM file (cert2.pem), it will show something like this:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            97:a0:fc:fa:d7:e5:28:fd
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Untrusted Root Certificate Authority
        Validity
            Not Before: Jul  7 06:31:35 2016 GMT
            Not After : Jul  2 06:31:35 2036 GMT
        Subject: C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Untrusted Root Certificate Authority
...

This is the root CA certificate, because:

  • It’s self-signed (the Issuer and Subject fields are the same)

  • It also has a long expiry time – this is usually a good clue that it’s a root certificate

If we trust this root certificate, we’ll also trust certificates signed by this issuer.

So now we’ve got the root certificate file, we need to tell Java to trust it.

This means adding it to Java’s truststore, which we’ll do next.

How to import the certificate into Java’s truststore

Once you’ve got the SSL certificate that you want to trust, you’ll want to add it into Java’s truststore.

Where’s the Java truststore?

I always add the certificates into Java’s default truststore. Unless there’s a really strong reason why different apps on your server need different rules on accepting certificates, just add everything to the default truststore.

It’s easier to maintain.

You can thank me later. 😉

The truststore is usually called cacerts, but its exact location will depend on your operating system:

Operating System Default truststore location
General $JAVA_HOME/lib/security/cacerts
Linux (RHEL and related distros, including the OpenJDK docker image) /etc/pki/java/cacerts
Linux (Ubuntu) /etc/ssl/certs/java/cacerts
Mac (with JRE installed only) $(/usr/libexec/java_home)/lib/security/cacerts
Mac (with JDK installed) $(/usr/libexec/java_home)/jre/lib/security/cacerts
Windows C:\Program Files (x86)\Java\jre<version>\lib\security\cacerts

† On RHEL, update your truststore using the update-ca-trust command.

‡ On Ubuntu, update your truststore using the update-ca-certificates command.

Add a certificate to the truststore

Once you’ve figured out the correct path to your truststore, add the certificate with Java’s keytool utility (it comes with Java).

(The default password for your truststore is changeit):

keytool -import -alias CHOOSE-AN-ALIAS \
    -file certificate.pem \
    -keystore /path/to/your/truststore

You should choose your own alias here. It gives the certificate a name that you can easily refer to.

Add a certificate to the truststore on RHEL or its variants

If you’re using RHEL, CentOS or Fedora, you can use the update-ca-trust command to update your Java truststore. This makes it much easier – just copy the certificate files into the correct location, run update-ca-trust, and it will do everything for you.

Test the certificate was installed

Now you can check to see whether the certificate is in the truststore.

Use keytool again:

keytool -list -keystore /etc/pki/java/cacerts

Now you should be able to restart your Java app and it will trust the new certificate.

Common mistakes

SSL isn’t easy! And there are a couple of common mistakes you should try to avoid:

  • Using self-signed certificates on the server.

    Self-signed certificates are the easiest way to set up HTTPS on a server.

    But self-signed certs are also a pain in the arse when they need to be renewed, because all of your clients have to be updated to trust the new, renewed certificate.

    It’s much better to use a certificate which has been properly signed by an issuer (like a CA). Your client apps will trust the CA’s certificate, which has a much longer expiry and you won’t need to update your client apps as often.

  • Not trusting the issuer’s certificate.

    If you connect to a server that’s presenting a certificate signed by someone else, then don’t solely trust the website’s certificate. Or else, you’ll have to update your client every time the server’s certificate is renewed (which can sometimes be every year)!

    Instead, trust the root CA certificate. When you trust that cert instead, you will also trust all future certificates they will sign.

  • Manually updating your cacerts file when you don’t need to.

    In case you missed my hint earlier on the page, you don’t need to do this on Red Hat Enterprise Linux (and its related Linuxes)!

    RHEL and similar distros come with a standard tool for updating certificates for lots of apps (not just Java) called update-ca-trust.

Love those certificates

It’s tempting to just tell Java (or your program) to ignore certificates, but it’s a decision you will probably regret later!

Even if you don’t regret it right now, then you’re probably making more hassle for someone else, after you.

So now you know how to use openssl and keytool to solve your Java certificate problems, you can faff around with SSL, to your heart’s content.

Join the discussion

Got some thoughts on what you've just read? Want to know what other people think? Or is there anything technically wrong with the article? (We'd love to know so that we can correct it!) Join the conversation and leave a comment.

Comments are moderated.