How to disable certificate validation in JAX-WS Client?
Asked Answered
W

4

22

How do you disable certificate validation in JAX-WS client using javax.xml.ws.Service?

I tried creating an all-trusting TrustManager in the SSLSocketFactory and tried to bind it with BindingProvider

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

Map<String, Object> ctxt = ((BindingProvider) wsport ).getRequestContext(); 
ctxt.put(JAXWSProperties.SSL_SOCKET_FACTORY, sc.getSocketFactory()); 

but I still getting Exception: unable to find valid certification path to requested target

But it works when I just use

HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 

Or is there a way to make javax.xml.ws.Service use the HttpsURLConnection that I created?

Waffle answered 18/9, 2012 at 8:57 Comment(3)
Context is important. Why do you want to deliberately introduce a major insecurity? If you don't want it secure, why use HTTPS/SSL at all?Piss
You'd be better off adding the untrusted certificate to the Java keystoreProtocol
Why can't people just assume that others know what they are doing when trying to bypass SSL verification?Soothsay
S
20

I found a solution here: http://schrepfler.blogspot.com.br/2009/06/relaxing-ssl-validation-for-jaxws.html

I'm using that solution calling the two static methods on a static block at the main class, like this:

static {
    SSLUtilities.trustAllHostnames();
    SSLUtilities.trustAllHttpsCertificates();
}

Hope this helps

EDIT: As David J. Liszewski pointed out, this breaks SSL/TLS for all connections from this JVM. So, keep that in mind.

Scifi answered 28/1, 2013 at 19:58 Comment(1)
Bravo. Break SSL/TLS for all connections from this JVM. :(Discovert
C
14

The truth can be found from Erik Wramner's blog here http://erikwramner.wordpress.com/2013/03/27/trust-self-signed-ssl-certificates-and-skip-host-name-verification-with-jax-ws

I include the full solution where Apache CXF is used to make SOAP web service requests to a self-signed SharePoint https service:

NaiveSSLHelper.java

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.transport.http.HTTPConduit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.BindingProvider;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

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("TLS");
      sslContext.init(new KeyManager[0], trustManagers,
        new SecureRandom());
      return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      return null;
    }
  }

  public static void makeCxfWebServiceClientTrustEveryone(HTTPConduit http) {
    TrustManager[] trustManagers = new TrustManager[]{
      new NaiveTrustManager()
    };
    TLSClientParameters tlsParams = new TLSClientParameters();
    tlsParams.setSecureSocketProtocol("TLS");
    tlsParams.setKeyManagers(new KeyManager[0]);
    tlsParams.setTrustManagers(trustManagers);
    tlsParams.setDisableCNCheck(true);
    http.setTlsClientParameters(tlsParams);
  }

  private interface SSLSocketFactoryHolder {
    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";
}

SoapTester.java

import crawler.common.sharepoint.stubs.sitedata.ArrayOfSList;
import crawler.common.sharepoint.stubs.sitedata.GetListCollectionResponse;
import crawler.common.sharepoint.stubs.sitedata.SList;
import crawler.common.sharepoint.stubs.sitedata.SiteData;
import crawler.common.sharepoint.stubs.sitedata.SiteDataSoap;
import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
import org.apache.cxf.transport.http.auth.HttpAuthHeader;
import org.apache.cxf.transport.http.auth.SpnegoAuthSupplier;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.ietf.jgss.GSSName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This example will invoke a web service on SharePoint 2013+ with optional kerberos auth.
 */
public class SoapTester {

  private static final Logger LOG = LoggerFactory.getLogger(SoapTester.class);

  public static void main(String[] args) {

    String endpointAddress = args[0];
    String keytabFilePath = args.length > 2 ? args[1] : null;
    String principalName = args.length > 2 ? args[2] : null;
    String servicePrincipalName = args.length > 3 ? args[3] : null;

    if (!endpointAddress.endsWith("/")) {
      endpointAddress += "/";
    }

    endpointAddress += "_vti_bin/SiteData.asmx";

    final String endpointAddressFinal = endpointAddress;

    Service service = Service.create(SiteData.SERVICE);
    SiteDataSoap soap = service.getPort(SiteDataSoap.class);
    NaiveSSLHelper.makeWebServiceClientTrustEveryone(soap);
    BindingProvider bindingProvider = (BindingProvider) soap;
    bindingProvider.getRequestContext().put(AsyncHTTPConduit.USE_ASYNC,
      Boolean.TRUE);
    bindingProvider.getRequestContext().put(
      BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);

    List<Handler> chain = bindingProvider.getBinding().getHandlerChain();
    chain.add(new SOAPHandler<SOAPMessageContext>() {
      @Override
      public boolean handleMessage(SOAPMessageContext context) {
        String endpointAddress = (String) context.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
        SOAPMessage msg = context.getMessage();
        Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
          msg.writeTo(out);
          String str = new String(out.toByteArray());
          LOG.info("Sharepoint xml [" + endpointAddress + "]" + (outbound ? " (Outbound)" : " (Inbound)") + ": " + str);
        } catch (Exception e) {
          LOG.error("Cannot get soap xml from message ", e);
        }
        if (outbound.booleanValue()) {
          try {
            context.getMessage().setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
        }
        return true;
      }

      @Override
      public boolean handleFault(SOAPMessageContext context) {
        return true;
      }

      @Override
      public void close(MessageContext context) {
      }

      @Override
      public Set<QName> getHeaders() {
        return null;
      }
    });
    bindingProvider.getBinding().setHandlerChain(chain);
    Client client = ClientProxy.getClient(bindingProvider);

    client.getEndpoint().put("org.apache.cxf.stax.maxChildElements", System.getProperty("org.apache.cxf.stax.maxChildElements") != null ? System.getProperty("org.apache.cxf.stax.maxChildElements") : "5000000");
    HTTPConduit http = (HTTPConduit) client.getConduit();
    NaiveSSLHelper.makeCxfWebServiceClientTrustEveryone(http);

    AuthorizationPolicy authorization = new AuthorizationPolicy();
    authorization.setAuthorizationType(HttpAuthHeader.AUTH_TYPE_NEGOTIATE);
    http.setAuthorization(authorization);

    SpnegoAuthSupplier authSupplier = new SpnegoAuthSupplier();
    if (servicePrincipalName != null) {
      authSupplier.setServicePrincipalName(servicePrincipalName);
      authSupplier.setServiceNameType(GSSName.NT_HOSTBASED_SERVICE);
    }

    Map<String, String> loginConfig = new HashMap<>();
    loginConfig.put("useKeyTab", "true");
    loginConfig.put("storeKey", "true");
    loginConfig.put("refreshKrb5Config", "true");
    loginConfig.put("keyTab", keytabFilePath);
    loginConfig.put("principal", principalName);
    loginConfig.put("useTicketCache", "true");
    loginConfig.put("debug", String.valueOf(true));
    authSupplier.setLoginConfig(new Configuration() {
      @Override
      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
        return new AppConfigurationEntry[] {
          new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
            AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
            loginConfig)};
      }
    });
    http.setAuthSupplier(authSupplier);

    HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
    httpClientPolicy.setAllowChunking(false);
    httpClientPolicy.setAutoRedirect(true);

    http.setClient(httpClientPolicy);

    Holder<ArrayOfSList> vLists = new Holder<>();
    Holder<Long> getListCollectionResult = new Holder<>();
    soap.getListCollectionAsync(getListCollectionResult, vLists, res -> {
      try {
        GetListCollectionResponse listCollectionResponse = res.get();
        ArrayOfSList arrayOfSList = listCollectionResponse.getVLists();
        LOG.info("Successfully got {} lists from {}", arrayOfSList.getSList().size(), endpointAddressFinal);
        for (SList slist : arrayOfSList.getSList()) {
          LOG.info("Successfully got list {}", slist.getTitle());
        }
        System.exit(0);
      } catch (Exception e) {
        LOG.error("List collection response", e);
      }
    });
  }
}

Here is another example with respect to JDK7 and glassfish. Please pay attention for Nikolay Smirnov's comment as well. I use jdk 7 and glassfish 3.1.2. In this environment the suggested solution works perfect if the server deal with a self signed cerfificate.

// import com.sun.xml.ws.developer.JAXWSProperties;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.ws.BindingProvider;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;

/**
 *
 * Usage examples (BindingProvider port):
 * NaiveSSLHelper.makeWebServiceClientTrustEveryone(port); // GlassFish
 * NaiveSSLHelper.makeCxfWebServiceClientTrustEveryone(port); // TomEE
 * 
 * Based on Erik Wramner's example frome here:
 * http://erikwramner.wordpress.com/2013/03/27/trust-self-signed-ssl-certificates-and-skip-host-name-verification-with-jax-ws/
 *
 * I have extended the functionality when Apache CXF is used.
 */
public class NaiveSSLHelper {

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

    // In Glassfish (Metro) environment you can use this function (Erik Wramner's solution)
    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());
        }
    }

    // In TomEE (Apache CXF) environment you can use this function (my solution)
    public static void makeCxfWebServiceClientTrustEveryone(Object port) {
        TrustManager[] trustManagers = new TrustManager[]{
            new NaiveTrustManager()
        };
        Client c = ClientProxy.getClient(port);
        HTTPConduit httpConduit = (HTTPConduit) c.getConduit();
        TLSClientParameters tlsParams = new TLSClientParameters();
        tlsParams.setSecureSocketProtocol("SSL");
        tlsParams.setKeyManagers(new KeyManager[0]);
        tlsParams.setTrustManagers(trustManagers);
        tlsParams.setDisableCNCheck(true);
        httpConduit.setTlsClientParameters(tlsParams);
    }

    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(java.security.cert.X509Certificate[] certs,
                String authType) throws java.security.cert.CertificateException {
        }

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

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[0];
        }
    }
}
Corney answered 5/7, 2014 at 13:30 Comment(3)
Much better than Jose Renato's answer. The solution from the link is said not to work in all cases, but at least for me (with OpenJdk 1.8, and whatever JAX-WS implementation NetBeans 8.2 uses) it worked fine. Would you copy it here, to protect against potential future disappearance of the linked page?Keikokeil
I have added the solution itself based on @Keikokeil suggestion. I have also included the case when ApacheCXF is used.Corney
Thanks Nicholas for editing and cleaning my sentences :) Now it is easier to read :)Corney
A
4

Putting aside all security issues that come with it, if anyone still want to disable certificate validation in a JAX-WS Client. This is how i do.

NB: And this way, instead of breaking SSL/TLS for ALL connections, you disable certificate validation for THAT client only.

import java.security.cert.X509Certificate;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;

/** Custom JAX-WS client factory used to ignore certificate validation */
public class NotSecureClientFactory extends JaxWsProxyFactoryBean {

    @Override
    protected ClientProxy clientClientProxy(Client c) {
        // Create a client factory that does not validate certificate chains
        ClientProxy cp = super.clientClientProxy(c);
        HTTPConduit httpConduit = (HTTPConduit) cp.getClient().getConduit();
        httpConduit.setTlsClientParameters(tlsClientParameters());
        return cp;
    }

    public TLSClientParameters tlsClientParameters() {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

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

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

        TLSClientParameters tlsClientParameters = new TLSClientParameters();
        tlsClientParameters.setTrustManagers(trustAllCerts);
        return tlsClientParameters;
    }

}
Adjourn answered 8/4, 2020 at 7:44 Comment(3)
The hostname verifier still errors out with this solution. Miklos also has an answer for a specific client.Snowball
Sorry, no hostname verification error on my side. Could you elaborate please ?Adjourn
setting tlsClientParameters.setDisableCNCheck(true); makes it work. This solution is a lot simpler. Thanks. Also adding how to use this factory would be helpful. This is what I used: NotSecureClientFactory factory = new NotSecureClientFactory(); factory.setServiceClass(YourPort.class); factory.setAddress(address); YourPort port = (YourPort) factory.create();Snowball
L
2

Actually CXF comes w/ an InsecureTrustManager for testing purpose.

For a Spring Boot app, it would be as easy as the following to disable TLS validation in all your CXF clients. Needless to say, u should NEVER do this in a prod env.

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.https.InsecureTrustManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;

import javax.inject.Inject;
import javax.xml.ws.BindingProvider;
import java.util.Set;

@ConditionalOnProperty(value = "soap.validate-tls.client", havingValue = "true")
@Configuration
class DisableTlsCxfClientConfig {
    @Inject
    DisableTlsCxfClientConfig(Set<BindingProvider> soapClients) {
        var insecureTlsParam = new TLSClientParameters();
        insecureTlsParam.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers());
        insecureTlsParam.setDisableCNCheck(true);
        soapClients.stream()
                .map(ClientProxy::getClient)
                .map(Client::getConduit)
                .map(HTTPConduit.class::cast)
                .forEach(c -> c.setTlsClientParameters(insecureTlsParam));
    }
}

Shout out to @addiene, @jontro & @Miklos Krivan who provided all the pieces! Thank u very much!

Lilialiliaceous answered 25/2, 2022 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.