Java with SSL explained

In a Nutshell

When it comes to SSL and Java things get sometimes complicated. In a very brief nutshell you have to take care of two things, a Keystore and a Truststore:

  • Keystore is a file that stores private entries, certificates with public keys, or just secret keys. It holds everything which identifies us.
  • Truststore is the opposite and contains certificates that identify others. You get a SSLHandshakeException when you try to communicate with a server that presents you a certificate from a Certificate Authority (CA) you don’t trust and can’t be found in the Truststore.

Java applications come with a bundled Truststore which is called cacerts. It is located in your $JAVA_HOME/jre/lib/security directory. It contains Certificate Authorities (CA) the JVM will trust automatically. When you set up your server with a signed certificate from an already trusted CA you don’t have to do anything on the client-side. When you have a self-signed certificate or you use a CA which is not part of the bundled Truststore it is on you to provide it.

You have basically two options when you have self-signed certificates:

  • Add it to the cacerts for the JVM globally
  • Create a Truststore file with your certificate and tell the Java process to use your Truststore file instead of the default one

:tipping_hand_woman: Use an own Truststore file that you control for a specific Java application. The cacerts Truststore is globally for all Java applications. It can give you unwanted surprises when it gets changed on upgrades or when you switch Java versions.

Manage Keystore and Truststore files

Java provides the keytool to manage certificate workflows and Keystores and Truststore files. Keystore and Truststore files are password-protected files. If you look for a tool with a graphical user interface, the KeyStore Explorer might be something for you.

:tipping_hand_woman: Most people run OpenNMS with Java 8 or Java 11. The command keytool works slightly differently. Check the docs for Java 8 and Java 11.

:biohazard: Until Java 8 they used the JKS format which is a Java-specific format and since Java 9+ the default format is PKCS12 which is a language-neutral format. When you migrate from 8 to 9+ you have to a) migrate your Keystore files to the default format or b) tell the JVM you use a different format than the default with the javax.net.ssl.trustStoreType property.

We have a Java server component and we want to provide SSL. There is no default Keystore and we have to provide it. During an SSL handshake, the server looks up the private key from the Keystore and presents the corresponding public key and certificate to the client. The client checks if the presented certificate or CA is in the Keystore. If not you get an SSLHandshakeException.

Java Options

Here are the most important Java options when you want to provide your own Keystore:

Property Description
javax.net.ssl.keyStore Path to the Keystore file
javax.net.ssl.keyStorePassword Password for the Keystore file
javax.net.ssl.keyStoreType Type Java 8 jks Java 9+ pkcs12

These are the most important Java options when you want to provide your own Truststore:

Property Description
javax.net.ssl.trustStore Path to the Truststore file
javax.net.ssl.trustStorePassword Password for the Truststore file
javax.net.ssl.trustStoreType Type Java 8 jks Java 9+ pkcs12

Running a Server with Let’s Encrypt Certificates

This example shows how to set up a server using a free certificate from Let’s Encrypt using certbot for an example host horizon.labmonkeys.de.

:tipping_hand_woman: We use OpenJDK 11 here in this example.

Step 1: Get a certificate

certbot certonly --standalone -d horizon.labmonkeys.de

Step 2: Extract the certificate and key pair in a temporary file in pkcs12 format

cd /etc/letsencrypt/live
openssl pkcs12 -export \
               -in horizon.labmonkeys.de/fullchain.pem \
               -inkey horizon.labmonkeys.de/privkey.pem \
               -out /tmp/horizon-temp.pkcs12 \
               -name horizon \
               -password pass:my-temp-secret-pass

Step 3: Import the key pair and certificate into a Java Keystore file

keytool -importkeystore \
        -srckeystore /tmp/horizon-temp.pkcs12 \
        -srcstorepass my-temp-secret-pass \
        -srcstoretype PKCS12 \
        -destkeystore /etc/my-ssl.jks \
        -deststorepass my-secret-store-pass \
        -destkeypass my-secret-store-pass \
        -alias horizon

# We can delete now the temporary pkcs12 file
rm /tmp/horizon-temp.pkcs12

Step 4: Use the Keystore server in your java application

java -Djavax.net.ssl.keyStore=/etc/my-ssl.jks \
     -Djavax.net.ssl.keyStorePassword=my-secret-store-pass \
     -jar something.jar ...

Further Readings


:woman_facepalming: You can fix me, I’m a wiki post.

1 Like