Fork me on GitHub

How to SSL with Http Client and Spring WS

Posted on August 25, 2011 in Development, Java

2

In the previous post I wrote about some SSL common issues in Java. One of them, the most popular in my opinion, points out to a flexible way to make ssl connections using Jakarta Commons Http Client 3.1 without any static call. This is a good choice if your client has to communicate with two or more different hosts using different keystores or truststores.

Basically, you have to use a specific HostConfiguration and a given ProtocolSocketFactory implementation within your HttpClient object. I found out that the HostConfiguration has to contain the name of the host you want to communicate with, while the HttpMethod object must contain a relative path, otherwise the HostConfiguration will be overridden at runtime with the default one.

	URL keystoreUrl = new File("keystore.jks").toURI().toURL();
	URL truststoreUrl = new File("truststore.jks").toURI().toURL();
	AuthSSLProtocolSocketFactory protocolSocketFactory = new AuthSSLProtocolSocketFactory(keystoreUrl, "password", truststoreUrl, "changeit");
	Protocol myProtocol = new Protocol("https", protocolSocketFactory, 443);
	HttpClient httpclient = new HttpClient();
	httpclient.getHostConfiguration().setHost("www.whatever.com", 443, myProtocol);
	GetMethod httpget = new GetMethod("/path");
	try {
		httpclient.executeMethod(httpget);
	} finally {
		httpget.releaseConnection();
	}

And what about doing the same to make a web service call through Spring Web Services?
First of all, the default WebServiceMessageSender implementation, called CommonsHttpMessageSender, doesn’t accept any relative path because of the following method:

    public boolean supports(URI uri) {
        return uri.getScheme().equals(HttpTransportConstants.HTTP_URI_SCHEME) ||
                uri.getScheme().equals(HttpTransportConstants.HTTPS_URI_SCHEME);
    }

Therefore, we need a custom WebServiceMessageSender implementation to accept relative paths, something like this:

public class CustomHttpMessageSender extends CommonsHttpMessageSender {
	public CustomHttpMessageSender(HttpClient httpClient) {
		super(httpClient);
	}

	@Override
	public boolean supports(URI uri) {
		HostConfiguration hostConfig = getHttpClient().getHostConfiguration();
		if (hostConfig.getProtocol() != null && hostConfig.getHost() != null){
			return true;
		}
		return super.supports(uri);
	}
}

Briefly, my supports method result depends on whether there’s a specific HostConfiguration within the HttpClient object or not. If the HostConfiguration is not null and contains the host (and the protocol) to communicate with, there’s no need to require only absolute paths as destination url.

Finally, this is the code to make a web service call using Spring Web Services using a specific HostConfiguration, thanks to our custom WebServiceMessageSender implementation which allows to accept relative paths:

	URL destinationURL = new URL("http://www.whatever.com/path");
	URL keystoreUrl = new File("keystore.jks").toURI().toURL();
	URL truststoreUrl = new File("truststore.jks").toURI().toURL();
	Protocol customProtocol = new Protocol(destinationURL.getProtocol(), new AuthSSLProtocolSocketFactory(keystoreUrl, "password", truststoreUrl, "changeit"), destinationURL.getPort());
	HttpClient httpclient = new HttpClient();
	httpclient.getHostConfiguration().setHost(destinationURL.getHost(), destinationURL.getPort(), customProtocol);
	WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
	webServiceTemplate.setMessageSender(new CustomHttpMessageSender(httpClient));
	StringResult result = new StringResult();
	Source source = new StreamSource(new StringReader(message));
	webServiceTemplate.sendSourceAndReceiveToResult(destinationURL.getPath(), source, result);

Now we can communicate through web service with more than one hosts using differents keystores and truststores. Note that the AuthSSLProtocolSocketFactory class used in this example is provided as a reference material within the Jakarta Commons Http Client, so it may be inappropriate for use without additional customization.

Related Posts:

(2) comments

Hi Luca, Really nice tutorial……………thanks :)
Thanks for the detailed explanation. It worked for me after I added the following line of code. Protocol.registerProtocol(“https”, customProtocol); Also, AuthSSLProtocolSocketFactory in commons-ssl.jar has a known issue – ‘java.security.KeyStoreException: No private keys found in keystore!’. You may have to either use not-yet-commons-ssl.jar (which is certified by apache but not yet released through them) OR write a custom class which extends AuthSSLProtocolSocketFactory. More details on this error can be found here – http://httpcomponents.10934.n7.nabble.com/No-private-keys-found-in-keystore-td14905.html

Write a comment

Notify me of followup comments via e-mail. You can also subscribe without commenting.