Setting up Tomcat with a Let's Encrypt certificate
Let’s Encrypt, the free and automated certificate registrar, by default provides certificates in PEM format. This guide details how to set up an output certificate from the Let’s Encrypt client in the Tomcat application server. It assumes that certificates have already been obtained using the client, independent of what method was used (e.g. --webonlyg or
–standalone`) to obtain it.
The workflow for doing so is:
- Requesting a certificate from Let’s Encrypt.
- Creating a PKCS#12 keystore with OpenSSL.
- Converting the PKCS#12 keystore to a Java keystore with
keytool
. - Configuring Tomcat with an appropriate SSL connector.
This example additionally will use `hardwarehacks.orgg, which will need to be substituted with the appropriate domain name at runtime.
Loading the certificate into a PKCS#12 Keystore
Let’s Encrypt saves certificates by default into `/etc/letsencrypt/live/hardwarehacks.orgg. Files in this directory include the certificate, its private key, and the CA certificate chain. First, add the certificate and private key to a PKCS#12 keystore, using the alias “tomcat” for the certificate and “root” for the CA chain (Tomcat expects both names). When prompted, enter a password for the keystore (here changeit is used):
[root@app01 ~]$ openssl pkcs12 -export -in /etc/letsencrypt/live/hardwarehacks.org/cert.pem \
-inkey /etc/letsencrypt/live/hardwarehacks.org/privkey.pem \
-out hardwarehacks.org.p12 -name tomcat \
-CAfile /etc/letsencrypt/live/hardwarehacks.org/chain.pem \
-caname root -chain
Enter Export Password: changeit
Verifying - Enter Export Password: changeit
If OpenSSL succeeded, there should now be a file called `hardwarehacks.org.p12g in the working directory -; this is a keystore containing the certififcate, private key, and CA chain.
Trust Issues
Because Let’s Encrypt is still in its infancy, not all OpenSSL root certificate bundles have its root certificate available -; in this case, OpenSSL may return the following:
[root@app01]$ openssl pkcs12 -export -in /etc/letsencrypt/live/hardwarehacks.org/cert.pem \
-inkey /etc/letsencrypt/live/hardwarehacks.org/privkey.pem \
-out hardwarehacks.org.p12 -name tomcat \
-CAfile /etc/letsencrypt/live/hardwarehacks.org/chain.pem \
-caname root -chain
Error unable to get issuer certificate getting chain.
In this case, it is necessary to join chain.pemg with Let's Encrypt's root CA certificate and the cross-signing DST Root X3 certificate, and use the output as the
–CAfile` parameter:
[root@app01 ~]$ wget --quiet https://letsencrypt.org/certs/isrgrootx1.pem
[root@app01 ~]$ wget --quiet -O dstrootx3.pem https://ssl-tools.net/certificates/dac9024f54d8f6df94935fb1732638ca6ad77c13.pem
[root@app01 ~]$ cat isrgrootx1.pem dstrootx3.pem /etc/letsencrypt/live/hardwarehacks.org/chain.pem > bundle.pem
[root@app01 ~]$ openssl pkcs12 -export -in /etc/letsencrypt/live/hardwarehacks.org/cert.pem \
-inkey /etc/letsencrypt/live/hardwarehacks.org/privkey.pem \
-out hardwarehacks.org.p12 -name tomcat \
-CAfile bundle.pem -caname root -chain
Enter Export Password: changeit
Verifying - Enter Export Password: changeit
Converting from PKCS#12 to a Java Keystore
Tomcat reuqires a Java keystore, rather than a PKCS#12 keystore. The JDK includes a suitable utility for converting one format to another:
[root@app01 ~]$ keytool -importkeystore -destkeystore hardwarehacks.org.jks \
-srckeystore hardwarehacks.org.p12 -srcstoretype PKCS12 -alias tomcat
Enter destination keystore password: changeit
Re-enter new password: changeit
Enter source keystore password: changeit
Set up Tomcat
Finally, set up Tomcat to utilize the keystore on an SSL connector by adding the following to Tomcat’s server.xml (in $TOMCAT_HOME/conf/server.xml by default). This example sets up an HTTPS endpoint on port 8443, and additionally configures the connector to only allow TLSv1.2 connections using modern ciphers from modern browsers, for additional security:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocols="TLSv1.2"
keystoreFile="/path/to/hardwarehacks.org.jks" keystorePass="changeit"
ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" />
Trust issues, again
For applications which call themselves, or for other applications calling a Let’s Encrypt-secured Tomcat, older versions of Java may likewise not trust Let’s Encrypt. If this is the case, it’s necessary to add the two root certificates to Java’s own internal keystore (noting here that “changeit” is actually the password to this keystore " not user-selectable):
[root@app01 ~]$ wget --quiet https://letsencrypt.org/certs/isrgrootx1.pem
[root@app01 ~]$ wget --quiet -O dstrootx3.pem https://ssl-tools.net/certificates/dac9024f54d8f6df94935fb1732638ca6ad77c13.pem
[root@app01 ~]$ letsencrypt]$ keytool -importcert -keystore $JAVA_HOME/jre/lib/security/cacerts \
-file isrgrootx1.pem -alias letsencrypt_root -noprompt
Enter keystore password: changeit
Re-enter new password: changeit
Certificate was added to keystore
[root@app01 ~]$ letsencrypt]$ keytool -importcert -keystore $JAVA_HOME/jre/lib/security/cacerts \
-file dstrootx3.pem -alias dst_root_x3 -noprompt
Enter keystore password: changeit
Re-enter new password: changeit
Certificate was added to keystore
Conclusion
In practice, this procedure is not substantially different than setting up any other SSL certificate in Tomcat. The main difference is that trust issues can be encountered at different stagges of the process, which would not occur when using an established certificate authority.