Monday 18 April 2011

Two-way / Mutual SSL authentication with Java and Tomcat

We have an unprotected client-server webservice (SOAP) setup.  As it's in a secure environment we were ok, but the client is to moved out of that environment into the big bad world and will now talk to the server through a firewall.  So we have a requirement to encrypt the messages to prevent eavesdropping and to implement authentication to ensure only authorised clients use the service.

We considered two options: 1) WS-Security SOAP or 2) SSL.  I Googled for people's opinions:
WS-Security versus SOA over SSL
WS-Security vs. SSL
Secure Web Services REST over HTTPS vs SOAP + WS-Security. Which is better?
On the performance of SSL vs. WS-Security
When to use WS-Security and SSL
Suffices to say this a hotly debated subject.  There is no blanket answer; it depends on your individual circumstances.  As I usually opt for simplicity and did not require any additional WS-Security functionality, 2-way SSL was the choice I made.

With 2-way SSL the client and the server exchange public certificates.  That way the traffic can be encrypted and they can both be certain to whom they are speaking.

To implement, ensure you have the java home bin directory on your path (that's where the keytool executable lives), and do the following (vary passwords/locations/periods as required) on the client:
# create keystore and generate client key pair
keytool -genkey -alias client -keyalg RSA -validity 3650 -keystore .\client.keystore -storepass abcd1234 - keypass abcd1234

# export client public certificate
keytool -export -alias client -keystore .\client.keystore -storepass abcd1234 -file .\client.cer
Similarly, on the server:
# create keystore and generate server key pair
keytool -genkey -alias server -keyalg RSA -validity 3650 -keystore .\server.keystore -storepass abcd1234 - keypass abcd1234

# export server public certificate
keytool -export -alias server -keystore .\server.keystore -storepass abcd1234 -file .\server.cer
Copy the client certificate to the server and vice-versa.  Then import the server certificate to the client's truststore:
# create truststore and import server certificate
keytool -import -alias server -keystore .\client.truststore -storepass abcd1234 -file .\server.cer
And on the server:
# create truststore and import client certificate
keytool -import -alias client -keystore .\server.truststore -storepass abcd1234 -file .\client.cer
Now we have the keystores and truststores set up on the respective machines we need to let the applications know about them.  For the Java client you need to set the following system properties:
javax.net.ssl.trustStore
javax.net.ssl.trustStorePassword

javax.net.ssl.keyStore
javax.net.ssl.keyStorePassword

javax.net.ssl.keyAlias (required if more than 1 key in the keystore)
You can either do this on the command line using the -D option, or you can do it programmatically using System.setProperty().

On the server side you need to configure tomcat.  Find the server.xml file in the conf directory and add a connector similar to this:
<Connector port="8433"
protocol="HTTP/1.1"
SSLEnabled="true"
maxThreads="150"
scheme="https"
secure="true"
clientAuth="true"
sslProtocol="TLS"
keystoreFile="<path>\server.keystore"
truststoreFile="<path>\server.truststore"
keystorePass="<password>"
truststorePass="<password>"
keyAlias="server"
/>

No comments:

Post a Comment