Friday, December 17, 2010

HttpClient: Connecting to an SSL Server

In the last days I've been fighting trying to solve an issue connecting to an SSL Server with SSLv2 disabled. For some reason my client code was crashing trying to connect, but as soon as SSLv2 was enabled everything worked fine again.

The problem was that even when my server was not accepting SSLv2 connections, my client code was trying to do a handshake validation using the SSLv2 protocol. This was causing the problem.

Since it took me so much time to find the problem, I though would be nice to share here what did I discover.

The first step is to create the code to properly connect to an SSL server. You can find how to do it with Self-Signed certificates in my previous post "HttpClient: Use Self-Signed Certificates".

Ok, if you followed it without problems now we're going to attack the way our code validates the different protocols.

With the debug enabled, you'll see the whole SSL handshake process.
System.setProperty("javax.net.debug", "all");
In a normal scenario any of the SSL protocols should be accepted, but in some cases, as the one I found, the SSLv2 is disabled. That's because this protocol is considered insecure.

The way of dealing with these scenarios is creating your own SocketFactory.
public class TLSSocketFactory extends SSLSocketFactory {

 private final javax.net.ssl.SSLSocketFactory socketfactory;
 
 public TLSSocketFactory(SSLContext sslContext) {
  super(sslContext);
  
  this.socketfactory = sslContext.getSocketFactory();
 }

    public Socket createSocket() throws IOException {
     SSLSocket socket = (SSLSocket) super.createSocket();
     
     socket.setEnabledProtocols(new String[] {"SSLv3, TLSv1"});
     
     return socket;
    }
    
    public Socket createSocket(
            final Socket socket,
            final String host,
            final int port,
            final boolean autoClose
        ) throws IOException, UnknownHostException {
     
        SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
                socket,
                host,
                port,
                autoClose
          );
     
     sslSocket.setEnabledProtocols(new String[] {"SSLv3", "TLSv1"});
     
     getHostnameVerifier().verify(host, sslSocket);
     
     return sslSocket;
    }
}
As you can see the code is quite simple. It's simply wrapping the SSLSocketFactory. Where is the trick? The best way to control with protocols can be used for the handshake is configuring them directly in the socket object. So here we're simply overloading the createSocket methods, but inserting a minor tweak
sslSocket.setEnabledProtocols(new String[] {"SSLv3", "TLSv1"});
If for some reason you only want to allow connections using TLS, then remove the SSLv3 protocol from the list.

And that's all. As you can see the code is quite simple.

See you soon.

2 comments:

  1. I am agree with your post
    , but since I know your project, in my experience with Httpclient I suggest you to use
    http://hc.apache.org/httpclient-3.x/threading.html

    ReplyDelete