Entity view (Content)

Unsecure SSL

By aabed
Jul. 20, 2016

SSL Protocol is supported in both Java and Mule and there is nothing special to be done to make it work. You may run into some trouble with self signed certificates if they are not trusted in the CA on the client side JVM (which in our case the Mule server). Below is a hack to bypass this security check. I highly recommend not using this hack in Production servers. It is just to to make it easier testing code in QA servers without going into the trouble justifying to the Server Admin why you need to install the self signed certificate in the Mule QA servers.
Bypassing the host name check depends on the way Java connect through SSL and verify the authenticity of the certificate. In Java you can define your own TrustManager class and define your own authentication mechanism in it. So a script like the one below will allow a Java program to use any certificate (even unsigned one) without importing them into Java keystore.

TrustManager[] trustAllCerts = new TrustManager[] {
       new X509TrustManager() {
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
          }

          public void checkClientTrusted(X509Certificate[] certs, String authType) {  }

          public void checkServerTrusted(X509Certificate[] certs, String authType) {  }

       }
    };

SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

The good thing is that Mule is build using Java, so the SSL connectivity is using JSSE and we can use the same solution to bypass the check if server is trusted or not (which includes the check for server host name or address).

The bad part is that the SSLContext class is buried inside the Mule HTTP Connector end-point code (specifically in the TlsConfiguration class) and Mule do not provide any mechanism to to override it on even call its init() method as shown in the script above.

So the solution was to add a Mule notification listener class that trap mule application events, specifically the CONTEXT_STARTED one which indicates that the Mule app started then we search the mule registry for the https connector and override its trust manager by providing a custom trust manager factory class using the method shown in the event below:

https://www.mulesoft.org/docs/site/current3/apidocs/org/mule/transport/http/HttpsConnector.html#setTrustManagerFactory

And this will do the trick. The Https connector will not be checking the authenticity of the certificate hence ignore that the host names do not match.

This hack should be used with extreme caution because disabling the certificate authenticity check will allow any server to send the same certificate and bypass the SSL check but we depend on the fact that the communication is done through VPN tunnel anyway hence it is less risky. A safer solution ma be is to add some code to the checkServerTrusted method in our custom TrustManager class to actually check the server but do not mind using the host name or ip address (less restrictive).

I did notice that Mulesoft will have similar approach baked inside Mule XML files as shown in the commit below. I think this will make it to future releases and then we do not need to provide our custom trust manager and we only need to set insecure="true" in the https connector trust-store tag.

https://github.com/mulesoft/mule/commit/181312496300184d073fca25fd4d87e13118366d

Below the Mule XML and Java code to implement this hack:

<https:connectorname="httpConnector" .../>
<spring:beans>
     <spring:beanname="insecureCerts"class="some.package.InsecureCertificates" id="Bean"scope="singleton">
          <spring:constructor-argvalue="httpConnector"/>
     </spring:bean>
 </spring:beans>
 <notifications>
      <notificationevent="CONTEXT"/>
      <notification-listenerref="insecureCerts"/>
</notifications>

public class InsecureCertificates implements
        MuleContextNotificationListener {

    static Logger log = Logger.getLogger(InsecureCertificates.class.getName());

    static class InsecureTrustManagerFactory extends TrustManagerFactory {

        protected InsecureTrustManagerFactory(TrustManagerFactory parent) {
            super(new TrustManagerFactorySpi() {

                @Override
                protected void engineInit(KeyStore ks) {
                }

                @Override
                protected void engineInit(ManagerFactoryParameters spec) {
                }

                @Override
                protected TrustManager[] engineGetTrustManagers() {
                    return new TrustManager[] { new X509TrustManager() {

                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            log.info("TrustManager: Get accepted issuers called");
                            return null;
                        }

                        @Override
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            log.info("TrustManager: Check client certificate called");
                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            log.info("TrustManager: Check server certificate called");
                        }
                    } };
                }
            }, (parent == null) ? null : parent.getProvider(),
               (parent == null) ? null : parent.getAlgorithm());
        }
    }

    private String connectorName;

    public InsecureCertificates(String connectorName) {
        this.connectorName = connectorName;
    }

    @Override
    public void onNotification(MuleContextNotification notification) {
        if (notification.getAction() == MuleContextNotification.CONTEXT_STARTED) {
            try {
                log.info("Installing insecure trust manager...");
                HttpsConnector connector = (HttpsConnector) notification.getMuleContext()
                        .getRegistry().lookupConnector(connectorName);
                connector.setTrustManagerFactory(new InsecureTrustManagerFactory(
                        connector.getTrustManagerFactory()));
                log.info("Insecure trust manager installation complete!");
            } catch (Exception e) {
                log.error("Error installing insecure trust manager: " + e);
                e.printStackTrace();
            }
        }
    }
}
Post Tags: