The UrBlog

John's Ramblings about Software Development

Using a HostnameVerifier With Spring Web Services

I was working with a web service from a site that uses SSL with a certificate that was self-signed. When attempting to make the calls, I received the error:

    javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching {web address} found

The workaround for this issue is to provide a HostnameVerifer that skips the host name verification process. There is an example of how to do this here.

I ran into this issue after I had a nice clean codebase using Spring’s WebServiceTemplate and RestTemplate. It took some digging, but I was able to find the spots where I had to install the verifier. We start with a NullHostnameVerifier that returns true for everything:

public class NullHostnameVerifier implements HostnameVerifier {
   public boolean verify(String hostname, SSLSession session) {
      return true;
   }
}

The org.springframework.ws.client.core.WebServiceTempate extends org.springframework.ws.client.support.WebServiceAccesor which uses a org.springframework.ws.client.support.WebServiceMessageSender. The WebServiceMessage sender for HTTPS is org.springframework.ws.transport.http.HttpsUrlConnectionMessageSender which is found in the spring-ws-support-1.5.9.jar. We need to create one of these and set the HostnameVerifier into it and pass it along to the WebServicesTemplate:

public void setWebServicesTempalate(WebServicesTemplate template) {
   HostnameVerifier verifier = new NullHostnameVerifier();
   HttpsUrlConnectionMessageSender sender = new HttpsUrlConnectionMessageSender();
   sender.setHostnameVerifier(verifier);
   template.setMessageSender(sender);
   this.template = template;
}

The org.springframework.web.client.RestTemplate is setup differently. It uses a org.springframework.http.client.ClientHttpRequestFactory to handle the connections. Stepping thru the code, I found the RestTemplate using org.springframework.http.client.SimpleClientHttpRequestFactory which has a protected prepareConnection(…) method I could override and catch the HttpsURLConnection which I could set the verifier in. First we need our own ClientHttpRequestFactory:

 
public class MySimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

   private final HostnameVerifier verifier;

   public MySimpleClientHttpRequestFactory(HostnameVerifier verifier) {
      this.verifier = verifier;
   }

   @Override
   protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
      if (connection instanceof HttpsURLConnection) {
         ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
      }
      super.prepareConnection(connection, httpMethod);
   }

}

Now we can use that as the request factory in our RestTemplate:

public void setRestTemplate(RestTemplate template) {
   HostnameVerifier verifier = new NullHostnameVerifier();
   MySimpleClientHttpRequestFactory factory = new MySimpleClientHttpRequestFactory(verifier);
   template.setRequestFactory(factory);
   this.template = template;
}

Once the NullHostnameVerifer is in place in the WebServiceTemplate and the RestTemplate, we will no longer see the error

Comments