Securing SSL in Tomcat

You can find the original articles here:

Part One -  http://blog.techstacks.com/2008/09/securing-ssl-in-tomcat-part-one-sslv2.html

Part Two - http://blog.techstacks.com/2008/09/securing-ssl-in-tomcat-part-two.html

Part Three- http://blog.techstacks.com/2010/03/securing-ssl-in-tomcat--part-three---openssl-instead-of-jsse.html

Part One - SSLv2

Since the http connector in tomcat is very robust, stable, fast, etc., a lot of developers are opting to use tomcat as the front-end. I don't really recommend it because setting things up that the average developer does not set up can be a lot more difficult then setting it up on IIS and Apache. Controlling SSL behavior on tomcat is not all that well documented and can be extremely difficult to configure.

Ever since I started publishing trinkets and baubles on PCI compliance and remediation, I've seen a fair amount of referrals to this site where someone is looking to disable SSLv2 or weak ciphers on tomcat or jboss. The default http connector in tomcat contains a default attribute named "sslProtocol". On newer versions of tomcat the default setting for this attribute is "TLS"--even if you do not explicity define the attibute in the server.xml configuration. Interestingly, if you try to connect to your ssl-enabled tomcat site using SSLv2, you will fail. Try it out. You can validate it using cURL or the OpenSSL client. To validate in cURL, type the following at the command prompt:
./curl --verbose --sslv2 https://fqdn:[port]/
#[port] optional if 443 is standard for your site


You will see the following output:
* Unknown SSL protocol error in connection to :8443
* Closing connection #0
curl: (35) Unknown SSL protocol error in connection to :8443


If you want to give the ssl client in openssl a try, type the following at the command line:

./openssl s_client -ssl2 -connect :[port]
#[port] optional if 443 is the default


The connection attempt will fail with an error message similar to the following:

Loading 'screen' into random state - done
CONNECTED(00000108)
5312:error:1407F0E5:SSL routines:SSL2_WRITE:ssl handshake failure:.\ssl\s2_pkt.c
:428:


Swapping -ssl2 with -ssl3 or -tls1 will enable a successful connection.

On the server itself, start tomcat or jboss up with the -Djavax.net.debug=All java option and you will get some very interesting ssl trace output. Clients connecting to the host over sslv2 result in the following output written to the tomcat/jboss server logs:

http-8443-1, setSoTimeout(60000) called
[Raw read]: length = 5
0000: 80 2E 01 00 02 .....
http-8443-1, handling exception: javax.net.ssl.SSLException: Unsupported SSL v2.
0 ClientHello
http-8443-1, SEND TLSv1 ALERT: fatal, description = unexpected_message
http-8443-1, WRITE: TLSv1 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 01 00 02 02 0A .......
http-8443-1, called closeSocket()
http-8443-1, called close()
http-8443-1, called closeInternal(true)

From a PCI compliance standpoint, you should not be getting tagged with the site being vulnerable to SSLv2 but what you are probably getting tagged with are insecure ssl protocols and disabling them will be the subject of the second part of this series of posts. I've spent the past week testing this with tomcat 4.1.37, 5.5.27, and 6.0.18 with jdk1.4, jdk1.5, and jdk1.6 virtual machines and none of them allow an SSLv2 connection. If you are getting tagged with supporting SSLv2, you will probably need to update your JVM because newer JVMs don't even provide SSLv2 support.

Part Two - Disabling Weak Ciphers

The previous post dealt with SSLv2 behavior in tomcat and jboss. This post is concerned more with the items that we will probably get hit with on the next vulnerability report if we are running tomcat or jboss as a front-end web server. The tomcat documentation is pretty spartan on the topic of restricting certain SSL Ciphers, which is too bad because it means a lot of trial and error on our part. The changes I'm proposing here may not necessarily be ideal but they do work.

This post assumes that you are running a Sun JVM (1.4.2 or higher) and that you are using tomcat 4.1.32 or above AND that you are using a non-APR http connector. non-APR connectors utilize Sun's JSSE for encryption whereas APR connectors utilize OpenSSL.  

The directive that you add to your SSL connector is the "ciphers" attribute. The documentation for the ciphers attribute states that you can leave it out or blank for all ssl ciphers supported by JSSE or you can enter in a comma-separated list of ciphers that you want your server to support. I have provided this listing and verified with SSLDigger that connections that try to null, weak, and anonymous ciphers fail. Using the small application and SSL debugging switches from Build Secure Network Applications with SSL and the JSSE API, I see that the following ciphers are enabled on a tomcat 5.5 and a tomcat 6 instance running under jdk1.6:

SSL_RSA_WITH_RC4_128_MD5
SSL_RSA_WITH_RC4_128_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_DES_CBC_SHA
SSL_DHE_RSA_WITH_DES_CBC_SHA
SSL_DHE_DSS_WITH_DES_CBC_SHA
SSL_RSA_EXPORT_WITH_RC4_40_MD5
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

I have highlighted the weak ciphers. Edit server.xml and find the SSL Connector section. Copy the following attribute and value and paste it into your SSL Connector section:

ciphers="SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"

NOTE: Spaces between ciphers should be removed. They were only added into this post in order to wrap text within this post's body. Restart your tomcat server. This will configure your server to support all Medium and High-Grade, non-exportable encryption ciphers. Anonymous ciphers will not work. Null Ciphers will not work and connection attempts using weak encryption (export grade and/or 56-bit encryption or below) will fail as well.  

Note regarding tomcat 4.1:  Tomcat 4.1.32 and above support the 'ciphers' parameter--it is not available in versions prior to 4.1.32 so the suggestions in this article will not work unless you update your tomcat 4.1 container first.

Updated Added 03/27/10: Note regarding Tomcat with an APR Connector:  This configuration will not work with an apr (native) connector under tomcat 5.5.x or 6.0.x.  See Part Three of this series instead.

Part Three - OpenSSL instead of JSSE

Some implementations of tomcat differ from the version downloadable from tomcat.apache.org in that they utilize the APR connector.  When using SSL with the APR connector, the ciphers directive normally used in server.xml mentioned in Part Two of this series, will not work.  This is because APR uses a native connection and for SSL a native connection uses OpenSSL instead of JSSE.  

If you want to disable weak encryption ciphers and your http connector is using Http11AprProtocol vs. using  Http11Protocol or even the non-blocking Http11NioProtocol, use the SSLCipherSuite directive instead of using ciphers. Since you're using OpenSSL with an ARP tomcat implementation, the same string to disable SSL on a BigIP or with Apache works in tomcat. The following command will disable weak, null, and anonymous ciphers in a Tomcat apr connector implementation:

SSLCipherSuite="ALL:!ADH:!SSLv2:!EXPORT40:!EXP:!LOW"

Note: If you are using a non-apr/non-native http connector in tomcat (Http11Protocol or Http11NioProtocol) see Part Two instead. Part Two details how to disable weak encryption ciphers using a java connector, which uses JSSE for SSL support.