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
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.
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
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.
Here are the most important Java options when you want to provide your own Keystore:
||Path to the Keystore file|
||Password for the Keystore file|
||Type Java 8
These are the most important Java options when you want to provide your own Truststore:
||Path to the Truststore file|
||Password for the Truststore file|
||Type Java 8
Running a Server with Let’s Encrypt Certificates
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 ...
- How to setup SSL with Jetty
- Troubleshoot Java with self-signed certificates
- Use embedded ActiveMQ with SSL
You can fix me, I’m a wiki post.