Home > Java > Trust self-signed SSL certificates and skip host name verification with JAX-WS

Trust self-signed SSL certificates and skip host name verification with JAX-WS

Java takes security seriously, in spite of all the security holes that pop up every now and then. This is particularly true with SSL. Make a web service call with Java and the server certificate must be valid, trusted and for the right host. All very well, but it can be a pain in test and development, where few organizations buy real certificates. You can of course import the self-signed certificates or you can use global options, but both can be tricky in some environments and there are several drawbacks.

I prefer to disable the checks locally. This blog explains how to trust self-signed certs. In addition the host name verification must be disabled and both solutions must be integrated in the JAX-WS machinery.

After the lengthy introduction, here is some working code:


public class NaiveSSLHelper {
  public static void makeWebServiceClientTrustEveryone(
      Object webServicePort) {
    if (webServicePort instanceof BindingProvider) {
      BindingProvider bp = (BindingProvider) webServicePort;
      Map requestContext = bp.getRequestContext();
      requestContext.put(JAXWS_SSL_SOCKET_FACTORY,
          getTrustingSSLSocketFactory());
      requestContext.put(JAXWS_HOSTNAME_VERIFIER,
          new NaiveHostnameVerifier());
    } else {
      throw new IllegalArgumentException(
          "Web service port "
              + webServicePort.getClass().getName()
              + " does not implement "
              + BindingProvider.class.getName());
    }
  }

  public static SSLSocketFactory getTrustingSSLSocketFactory() {
    return SSLSocketFactoryHolder.INSTANCE;
  }

  private static SSLSocketFactory createSSLSocketFactory() {
    TrustManager[] trustManagers = new TrustManager[] {
      new NaiveTrustManager()
    };
    SSLContext sslContext;
    try {
      sslContext = SSLContext.getInstance("SSL");
      sslContext.init(new KeyManager[0], trustManagers,
          new SecureRandom());
      return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      return null;
    }
  }

  private static interface SSLSocketFactoryHolder {
    public static final SSLSocketFactory INSTANCE = createSSLSocketFactory();
  }

  private static class NaiveHostnameVerifier implements
      HostnameVerifier {
    @Override
    public boolean verify(String hostName,
        SSLSession session) {
      return true;
    }
  }

  private static class NaiveTrustManager implements
      X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] certs,
        String authType) throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] certs,
        String authType) throws CertificateException {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
      return new X509Certificate[0];
    }
  }

  private static final java.lang.String JAXWS_HOSTNAME_VERIFIER =
    "com.sun.xml.internal.ws.transport.https.client.hostname.verifier";
  private static final java.lang.String JAXWS_SSL_SOCKET_FACTORY =
    "com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory";
}

The JAX-WS properties are defined by an internal Sun class, which may be missing (been there). Hard-coding the names may not work with another web service stack, but at least it won’t blow up. The worst that can happen is that the normal SSL verification remains in place.

Call makeWebServiceClientTrustEveryone with the web service port before making a call, and voila! The security checks are gone. Please note that this is a bad idea in production, as it opens up for man in the middle attacks. Use it where it makes sense!

Advertisements
Categories: Java
  1. Nikolay Smirnov
    2014-04-29 at 09:26

    Erik, thanks a lot for your blog.
    The original version did not work for me (JDeveloper 12.1.2), but everything is ok after a minor modification:

    import com.sun.xml.ws.developer.JAXWSProperties;

    private static final String JAXWS_HOSTNAME_VERIFIER = JAXWSProperties.HOSTNAME_VERIFIER;
    // = “com.sun.xml.ws.transport.https.client.hostname.verifier”;
    private static final String JAXWS_SSL_SOCKET_FACTORY = JAXWSProperties.SSL_SOCKET_FACTORY;
    // = “com.sun.xml.ws.transport.https.client.SSLSocketFactory”

    • 2014-04-29 at 20:42

      Well, unfortunately this is a real mess. As you can see from the package (com.sun.xml.ws.developer) JAXWSProperties is an internal class; it is not part of the standard. That means that your code works as long as the JAX-WS implementation is from Sun (now Oracle). If you have another implementation you will get a ClassNotFound exception. That means your code will either work or blow up. My code will never blow up, but if the JAX-WS implementation uses another value than the one I copied from JAXWSProperties or if it doesn’t implement this at all the default SSL certificates will be used. Neither approach is perfect, but there you are.

      Hopefully the standard will address this in a future release!

  2. 2014-07-05 at 14:58

    Many-many thx to Erik and Nikolay. You saved my life. I did more than 16 hours troubleshooting until I have found your blog. Somebody at my company use self signed certificate for an important service and I could not connect before I have implemented your approach. God bless you in your all life and keep healty forever! :-)

  3. Marco
    2014-10-21 at 00:14

    ERik , i am having similar problems…. could you confirm all the classes you use in your example come from javax.ssl and NOT com.sun.net.ssl ?

    kind regards
    marco

    • 2014-10-21 at 08:31

      Well, kind of. I do not use any of Sun’s internal classes directly, but some parts of the code will only work with Sun’s implementation. That is because the JAXWS-constants are Sun-specific. It will not blow up on other implementations, but the options will be ignored. In order to make it work on other implementations you need to find the corresponding options, if any, and set them as well. There is no truly portable solution.

  4. 2016-08-18 at 18:32

    Some algorithms are no longer considered secure and they have been disabled in recent Java versions. It is still possible to use them, but they won’t work out of the box. See http://stackoverflow.com/questions/14149545/java-security-cert-certificateexception-certificates-does-not-conform-to-algori. Based on that post you may be able to get things working if you modify my code to extend X509ExtendedTrustManager. I haven’t tried it, so no guarantees. Alternatively you can enable the problematic algorithm for your JVM (also covered in the post).

    If you are using a self-signed certificate that you are in control of I would recommend you to generate a new one with modern algorithms. That is a real production-like solution. Unsafe algorithms are deprecated for a reason.

    If you can’t do that and the service is publicly available on the Internet I may be able to help if you can give me the URL?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: