I’ve been working on a RESTful API for use on computers and mobile devices. I have a JUnit test case that connects to the URLs and reads the responses. Given the nature of the data being transferred back and forth, I enabled SSL on the web server using a self-signed certificate I generated using Java’s keytool.
keytool -genkey -alias <hostname> -keyalg RSA
This is where my problems began. My test case is using HTTP, so I needed to refactor it to use the HTTPS protocol. Unbeknownst to me, accepting SSL connections from self-signed certificates is non-trivial, especially if you want to do it right. There is copious advice on the inter-web about how to accept any certificate. I’m not keen on that approach; therefore, I set out to find the correct way.
Without a decent Java security reference handy, I surfed the internet for answers. I found partial code snippets on stackoverflow and Example Depot. In a nutshell, I found that you need an instance of a SSLSocketFactory to set in a HttpsURLConnection. Here’s how:
// Load the keystore in the user's home directory File file = new File(System.getProperty("user.home") + File.separatorChar + ".keystore"); FileInputStream fis = null; KeyStore keyStore = null; fis = new FileInputStream(file); keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(fis, keystorePassword.toCharArray()); TrustManagerFactory tmf; tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, tmf.getTrustManagers(), null); //Get an instance of the socket factory SSLSocketFactory sslFactory = ctx.getSocketFactory(); HttpsURLConnection connection = null; url = new URL(newURLString); connection = (HttpsURLConnection)url.openConnection(); //set the socket factory in the connection connection.setSSLSocketFactory(sslFactory); //...
Tip: Remember the alias created in the keytool must be the hostname of the server. Otherwise, an exception will be thrown on the client:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching <alias> found