Wednesday, February 29, 2012

Working With Java SSL keys

I've seen a number of posts around the net where people were struggling with getting their own private SSL keys to work, or trying to use a keystore that wasn't sitting on the filesystem somewhere. Having spent way too much time beating my head against this I figured I'd throw what I learned out there for posterity.

First things first.  "javax.net.ssl.SSLHandshakeException: no cipher suites in common" is a lie! This message has driven me close to insanity way too many times. Let me translate it for you. "I can't find a valid key or keystore." This generally  comes up when you follow most of the examples on the net that look like this:
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
That first 'null'  is the problem. You must pass in an array of keyManagers. Here is a complete example that will let you work with your own keystore regardless of where you got it.


 SSLContext sslContext = SSLContext.getInstance("SSL");

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
        trustManagerFactory.init(keyStore);    
        
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, "password".toCharArray());
        
        sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
 You will also get the same error if you do not have a trusted certificate on the client side that matches the certificate used by the server. The aliases must match, as that's what The trust managers use to find the proper key/cert to check against.

If you don't want to write your own thing because your keystore is sitting on the file system someplace safe. You should just use the default SSLSocketFactory. Which is used by setting some system properties.
System.setProperty("javax.net.ssl.keyStore", "keystore path");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
SSLSocketFactory sslSocketFactory = ((SSLSocketFactory)SSLSocketFactory.getDefault());
 I hope this helps.