0

I am trying to invoke a WS over SSL, from a tomee 1.6 server, but I get a SSLHandshakeError. The problem is that the certificate is self signed, and is not recognized by my JVM. As it is only for test purpose, and not production, I have been asked to bypass the certificate control. I read a lot of stuff about how to proceed, and I have written that code :

a class NaiveSSLContext :

package fr.csf.ssl;

import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

/**
 * A factory class which creates an {@link SSLContext} that
 * naively accepts all certificates without verification.
 */
public class NaiveSSLContext
{
    private NaiveSSLContext()
    {}

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol) throws NoSuchAlgorithmException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol, Provider provider) throws NoSuchAlgorithmException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol, provider);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Get an SSLContext that implements the specified secure
     * socket protocol and naively accepts all certificates
     * without verification.
     */
    public static SSLContext getInstance( String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException
    {
        SSLContext sslCtx = SSLContext.getInstance( protocol, provider);
        init( sslCtx);
        return sslCtx;
    }

    /**
     * Set NaiveTrustManager to the given context.
     */
    private static void init( SSLContext context)
    {
        try
        {
            // Set NaiveTrustManager.
            context.init( null, new TrustManager[] { new NaiveTrustManager() }, new java.security.SecureRandom());
            System.out.println( "------------- Initialisation du NaiveSSLContext ---------------------");
        }
        catch( java.security.KeyManagementException e)
        {
            throw new RuntimeException( "Failed to initialize an SSLContext.", e);
        }
    }

    /**
     * A {@link TrustManager} which trusts all certificates naively.
     */
    private static class NaiveTrustManager implements X509TrustManager
    {
        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            System.out.println( "------------- NaiveTrustManager.getAcceptedIssuers() ---------------------");
            return null;
        }

        @Override
        public void checkClientTrusted( X509Certificate[] certs, String authType)
        {
            System.out.println( "------------- NaiveTrustManager.checkClientTrusted( " + certs.toString() + ", " + authType
                    + ") ---------------------");
        }

        @Override
        public void checkServerTrusted( X509Certificate[] certs, String authType)
        {
            System.out.println( "------------- NaiveTrustManager.checkServerTrusted( " + certs.toString() + ", " + authType
                    + ") ---------------------");
        }
    }
}

and another class NaiveSSLSocketFactory :

package fr.csf.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;

public class NaiveSSLSocketFactory extends javax.net.ssl.SSLSocketFactory
{
    private javax.net.ssl.SSLSocketFactory factory;

    public NaiveSSLSocketFactory() throws NoSuchAlgorithmException
    {
        javax.net.ssl.SSLContext sslCtx = NaiveSSLContext.getInstance( "SSL");
        factory = sslCtx.getSocketFactory();
    }

    private final String[] enabledProtocols = new String[]
    { "SSLv3", "TLSv1" };

    @Override
    public Socket createSocket( Socket s, String host, int port, boolean autoClose) throws IOException
    {
        Socket socket = factory.createSocket( s, host, port, autoClose);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( String host, int port) throws IOException, UnknownHostException
    {
        Socket socket = factory.createSocket( host, port);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( InetAddress host, int port) throws IOException
    {
        Socket socket = factory.createSocket( host, port);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException
    {
        Socket socket = factory.createSocket( host, port, localHost, localPort);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public Socket createSocket( InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
    {
        Socket socket = factory.createSocket( address, port, localAddress, localPort);
        ((javax.net.ssl.SSLSocket) socket).setEnabledProtocols( enabledProtocols);
        return socket;
    }

    @Override
    public String[] getDefaultCipherSuites()
    {
        String[] cipherSuites = factory.getDefaultCipherSuites();
        return cipherSuites;
    }

    @Override
    public String[] getSupportedCipherSuites()
    {
        String[] cipherSuites = factory.getSupportedCipherSuites();
        return cipherSuites;
    }
}

The problem is that I can't find out how to make the JVM use my Naive* classes instead of the default ones. I have tried different methods, but neither of them work :

First try :

javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory( new NaiveSSLSocketFactory());

My log traces in the checkClientTruted methods are never displayed. It seems that my NaiveSSLSocketFactory is never called.

2nd try :

java.security.Security.setProperty( "ssl.SocketFactory.provider", new NaiveSSLSocketFactory().getClass().getName());

I experienced a ClassNotFoundException due to a ClassLoader problem, but after this problem was fixed, the same problem remains.

I eventually found a blog where it was said that CXF client had to do a little more configuration stuff :

<http-conf:conduit name="*.http-conduit" >
    <http-conf:tlsClientParameters 
        useHttpsURLConnectionDefaultSslSocketFactory="true"     
    />
</http-conf:conduit>

As I use a Tomee1.6 server, my program is a CXF client. So that must be the solution. But where do I have to write this configuration properties ? I can't find any xml file in Tomee, related with CXF. There's only e cxf.properties file, which is nearly empty.

Jordi Castilla
  • 26,609
  • 8
  • 70
  • 109
jmb
  • 129
  • 11
  • Possible duplicate of [Java: Overriding function to disable SSL certificate check](http://stackoverflow.com/questions/19723415/java-overriding-function-to-disable-ssl-certificate-check) – Jordi Castilla Oct 30 '15 at 11:11
  • It's not a duplicate. I don't ask how to write the classes which bypass the certificate control. I already wrote them, and provided them in my post. I think they are correct because they look like all I found on the specialized web sites. I ask how I can tell my server (Tomeeplus v1.6) to use them. As it uses CXF, I beleive there is a config property to change, but I don't know which one nor where it is. – jmb Oct 30 '15 at 13:03

1 Answers1

1

First, Tomcat isn't involved with your consumption of a web service - in fact it's really not involved with with any outbound connections your application is making.

I know of two ways to achieve your desired results provided by CXF in a way that won't affect any other outbound SSL connections running on the same JVM:

  1. add the self-signed certificate to the CXF client's conduit trust store, or
  2. install a "do-nothing" trust manager to to the CXF client's TLS parameters

The first method is preferable as the second will trust any endpoint your client connects with.

To implement the first method, create a key store containing the certificate you wish to trust (and for good measure, include any intermediary certificates). Then add this trust store as outlined in CXF handbook section Configuring SSL Support. Your conduit configuration will look something like this:

<http:conduit name="{http://apache.org/hello_world}HelloWorld.http-conduit">

  <http:tlsClientParameters>
    <sec:trustManagers>
      <sec:keyStore type="JKS" password="password"
                  file="my/file/dir/Truststore.jks"/>
    </sec:trustManagers>
  </http:tlsClientParameters>
  <http:client AutoRedirect="true" Connection="Keep-Alive"/>
</http:conduit>

Note that the conduit name in the example above is obviously just an example. See the update to my answer here regarding another question as how to specify the conduit name. Also note that I did not include a cipher suite filter as I believe it will default to some set of values, which is potentially unsafe if you're using Java 6 or older .. but that's a whole other topic.

Also, you can eschew Spring configuration of CXF entirely and do all of the above programmatically using CXF client APIs.

I also highly suggest using a tool like KeyStore Explorer to extract certificate (and intermediaries) from the target endpoint and import them into your new trust store.

Finally, I would like to point out, in reference to your initial solution, the danger of using JVM-wide installation of things like SSL socket factories and trust managers as supported by the JDK API. There is a possibility of perilous consequences of doing so when running inside of a container supporting multiple applications: you can subvert the security profile of other applications. One of the benefits of using a framework like CXF is that it provides means to customize SSL/TLS configurations for each application client (or server) instance.

Community
  • 1
  • 1
David J. Liszewski
  • 10,959
  • 6
  • 44
  • 57
  • Thank you for your answer. But my problem is that my application is running under Tomee plus J2EE server, which encapsulates CXF. And I don't know how to configure CXF inside a Tomee server. The only configuration files I found is a tomee.xml file, and a cxf.properties file. – jmb Nov 02 '15 at 08:08