Archive

Archive for March, 2013

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!

Categories: Java

Write XML files with specific encoding in SOA Suite

Outbound FTP adapters in Oracle SOA Suite normally use the default character encoding. Usually that means that all XML files are written in UTF-8. What do you do if you need to use something else, for example ISO-8859-1?

The logical solution would be to set an adapter property. In 11.1.1.6 there are three likely candidates: CharacterSet, Encoding and jca.message.encoding. Unfortunately none of them works. CharacterSet and Encoding only apply to attachments and are not even used by the adapter. The documentation states that they are meant for third-party applications that need to process the attachment. The jca.message.encoding is used, but only when the adapter reads files, not when it writes them.

What to do? The solution has been with us for a long time, the adapters in 10g worked in the same way. For native files the encoding can be specified in the schema with the nxsd:encoding attribute. It turns out that this works for normal XML files that have no need for native conversions too.

Simply add the following to the main XSD:


<xsd:schema 
  xmlns:nxsd="http://xmlns.oracle.com/pcbpel/nxsd"
  nxsd:version="XSD" nxsd:encoding="ISO-8859-1"
  ...>

Problem solved, the adapter will use ISO-8859-1. A bit dirty (in particular when the schema is owned by someone else), but it works.

Categories: SOA Suite