Over the last years, a lot has happened in SSL/TLS land. In 2011, the BEAST attack made it possible to decrypt session cookies. As a countermeasure, many people started preferring RC4 ciphers. Most vendors released security patches, lessening the need for server-side mitigations. Since RC4 is showing more and more weaknesses, this was probably a good thing. In this post we will explore the state of affairs regarding TLS when using Apache Tomcat, and we will try to find an optimal configuration.
Please note an updated Java 8 / Tomcat 7 configuration is at the end of this blog.
In short, for full flexibility and security you should use another web server, like nginx, to proxy requests to Tomcat. If that is too much work, use the Tomcat APR connector. If that is still too much work, or if you have other compelling reasons to stick to Tomcat’s BIO/NIO connectors, be aware that your configuration options will be limited, impacting security.
TLS concepts
Before we have a look at all the configuration details of the various connectors, we should discuss some of the concepts involved. We can do this by looking at a so-called cipher suite. When a client connects to a server to set up a secure connection, both parties will negotiate about which cipher suite to use. One of the cipher suites they could end up using is ECDHE-RSA-AES128-GCM-SHA256, let us break this down and discuss all the parts:
- The first part, ECDHE, specifies what key exchange algorithm should be used. During the key exchange, both parties agree on the shared secret that will be used to encrypt all the traffic. Sometimes this part is missing from the cipher suite string, in that case the authentication algorithm will be used for the key exchange. Since in this case the same key is used for both key exchange and authentication, a compromise of the server’s private key will compromise all traffic that was ever send or received from the server. For this reason, it is highly recommended to use ECDHE or DHE, which use Diffie-Hellman key and come up with a new key for every connection. We call this perfect forward secrecy (nowadays Google, GitHub and Twitter also provide this).
- Next up is the authentication algorithm, RSA in this case. This algorithm is used by the client to validate the server’s identity and makes sure you are indeed talking to your bank/Google/other party. If client-authentication is used, it will also be used by the server to validate the client. This is the part for which you usually buy a certificate from a trusted Certificate Authority. Other options, beside RSA are DSS and ECDSA, but you probably have a RSA certificate and thus need to use RSA as the authentication algorithm.
- The bulk cipher, AES128-GCM in the above example, is the main encryption algorithm and used to encrypt all the traffic, using the key that was agreed on using the key exchange algorithm. In this case, the AES algorithm with a 128 bit key is used, in Galois/Counter Mode. Couple of things to know:
- DES/Triple DES are considered deprecated and should not be used.
- RC4 is showing more and more weaknesses, and should only be used as a last resort (i.e., for Windows XP support).
- AES is the most recommended at the moment, but other algorithms do exist. 128 bit keys should be safe enough, larger keys will cost you more CPU cycles.
- The last part, SHA256, identifies the message digest in use, which verifies the authenticity of messages. Simple encryption is not enough: you have to be sure the messages have not been tampered with, for this reason all data is signed using the message digest algorithm. At the moment, SHA-512/384/256 are considered the most secure algorithms, SHA-1 should still be secure, and MD5 is considered broken and insecure.
This is by no means a complete description of TLS. If you want to know more, I suggest you start with Wikipedia and follow the links to the RFC’s and other blogs from there. Also, please be aware that the state of the art changes everyday, and algorithms that are considered to be safe today may be broken tomorrow.
TLS in Tomcat
Tomcat contains a couple of different HTTP(S) connectors out of the box: BIO, NIO and APR. BIO and NIO use the Java SSL/TLS implementation (JSSE), whereas APR uses the Apache Portable Runtime with OpenSSL. JSSE has a couple of shortcomings, making APR the preferred choice. Below we will discuss the configuration of both TLS implementations in more detail.
JSSE
Out of the box Tomcat uses the BIO connector, which does not require any extra libraries and provides TLS support through JSSE. Looking at the documentation we can see most options we are looking for already have sensible defaults or are absent. The most important thing to configure is the ciphers
attribute. But what should we put here? We can run a small program to determine the list of available ciphers, which shows us a large list of ciphers (for Java 1.7.0_51-b13 with JCE Unlimited Strength Jurisdiction Policy). Which ciphers to choose?
Keeping the Mozilla recommendations in mind, we see the following:
- If we are using an RSA certificate, like most us, we can forget about the ECDSA and DSS ciphers.
- We probably do not need Kerberos support, so we can leave the KRB ciphers out.
- DES/3DES are deprecated and should not be used.
- The DHE ciphers are nice, since they provide forward secrecy, but Java only uses a 768 bits DH key, which is deemed insecure. This will probably not be fixed until Java 8, so we cannot use these ciphers.
- The ECDHE ciphers are a nice alternative to the DHE ciphers, and use a 571 bits elliptic curve key, which provides more than enough security (unless you want to keep your secrets from the NSA).
- The RC4 ciphers should not be used, but we need them for Internet Explorer on Windows XP compatibility reasons.
We now end up with the following list:
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,
TLS_ECDHE_RSA_WITH_RC4_128_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA,SSL_RSA_WITH_RC4_128_SHA
The complete Tomcat server.xml Connector element:
Keep the following in mind:
- To use the 256 bit AES ciphers, it is necessary to install the JCE Unlimited Strength Jurisdiction Policy Files, which you can find at the bottom of this page. However, 128 bit encryption should be enough for daily use.
- It is currently not possible to let the server force the cipher order, so we are unable to force forward secrecy for some browsers. Strict cipher suite ordering will be added in Tomcat 8 / Java 8.
- This configuration enables client-initiated renegotiation, since there is no option to turn it off. Some people consider this a DoS risk, but the Tomcat developers disagree.
This is about as good as it gets with BIO/NIO (JSSE), feel free to leave a comment if you found a better configuration.
APR
Tomcat gives us the option to use the Apache Portable Runtime (APR) together with OpenSSL, instead of the BIO or NIO connector. Usually this improves performance, and in this case it also gives us some more SSL parameters to play with. For this, we either have to install a pre-compiled Tomcat Native library, or we should build our own. In order to have all the latest options it is important to use a recent OpenSSL and Tomcat Native library, so let us build our own:
In the above script, we patch the Tomcat Native library in order to get ECDHE support. The patch works, but I cannot guarantee anything in production environments. Check the Tomcat bugtracker for the latest info on ECDHE support.
After installation, you should something like this in your Tomcat logs:
Feb 04, 2014 9:29:34 PM org.apache.catalina.core.AprLifecycleListener init INFO: Loaded APR based Apache Tomcat Native library 1.1.29 using APR version 1.4.6. Feb 04, 2014 9:29:34 PM org.apache.catalina.core.AprLifecycleListener init INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true]. Feb 04, 2014 9:29:35 PM org.apache.catalina.core.AprLifecycleListener initializeSSL INFO: OpenSSL successfully initialized (OpenSSL 1.0.1e 11 Feb 2013)
Since this uses the OpenSSL library in the background, we can use the recommended list of ciphers from the Mozilla wiki entry. Furthermore we can force the cipher order and disable TLS compression to prevent the CRIME attack, resulting in the following Tomcat Connector element:
This configuration should provide a good security level and perfect forward secrecy with the browsers that support it. Some remarks:
- The Diffie-Hellman key exchange uses 1024 bits of parameters. This should not be a problem for most users, since ECDHE is used with almost all browsers (assuming you used our patch). At the moment it is not possible to use more bits for the DH parameters, but I have filed a bug about this problem, which you can watch.
- Session tickets are enabled by default, which may impact your forward secrecy since OpenSSL uses a static secret to encrypt the tickets, which only changes after a server restart. This should be good enough for most users, but when in doubt you should create a patch for Tomcat Native to disable session tickets (or set up a key rotation scheme like Twitter).
Proxying requests
The last option is to use a reverse proxy to forward requests to Tomcat. This has a couple of advantages:
- nginx and other web servers are usually more up-to-date when it comes to new (Open)SSL options, whereas Tomcat (Native) is often lagging behind, impacting your security level. For instance, both nginx and Apache httpd support OCSP stapling, custom session ticket keys, and custom DH parameters.
- Most reverse proxies can run at port 80 and 443 without any ‘special’ port-forwarding tricks, which is not possible with Tomcat.
- Some reverse proxies also offer the option of caching some of your static content, lowering the load on your application server.
Conclusion
The Tomcat BIO or NIO connectors do not provide the latest and greatest TLS cipher suites and leave a lot to be desired, at least until Java 8 comes out. APR is a lot better, but there are still some improvements possible, and ECDHE support still requires manual patching. If you are familiar with nginx or a similar reverse proxy server, we recommend to try these first. Also, always use a recent version of Tomcat, Java, OpenSSL and your reverse proxy, since new cipher suites, configuration options and other tweaks are added with each version.
Good luck configuring your TLS settings, please leave your comments or remarks below!
Update 27/5/14: As pointed out in the comments, OpenJDK does not (fully) support elliptic curve ciphers out of the box (see here and here). This will cause a limited set of ciphers to be available, and some clients will not be able to connect using the abovementioned configuration. I recommend to use the Oracle JDK build. Another option is to use the Bouncy Castle crypto implementation, by placing the JAR in $JAVA_HOME/jre/lib/ext
, and including it in the provider list at $JAVA_HOME/jre/lib/security/java.security
. See the docs for details (you can set it to be the default/first provider, should be fine).
Update 20/7/15: As this post still receives many views, and Java 8 provides better cipher support, here is a config that works well with BIO/NIO, Java 8 and Tomcat 7.0.60+:
Also include the following option in your Java/Catalina options to up the DHE size to 2048 bits:
-Djdk.tls.ephemeralDHKeySize=2048
This configuration supports perfect forward secrecy with all modern browsers.