Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache CXF wsdl download via SSL/TLS

I have a cxf service running at

https://localhost:8443/services/MyService?wsdl

with client certificate required. The WSDL is not important here.

I am able to invoke the service when I remove the client certificate or https requirement.

The service and client classes were generated with cxf wsdl2java utility.

Here is MyService.class:

package com.mycompany;

import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;

/**
 * This class was generated by Apache CXF 2.7.3 2013-03-29T13:59:37.423-03:00 Generated source version: 2.7.3
 */
@WebServiceClient(name = "MyService", wsdlLocation = "myservice.wsdl", targetNamespace = "http://server/schemas/services")
public class MyService extends Service {

    public final static URL WSDL_LOCATION;

    public final static QName SERVICE = new QName("http://server/schemas/services", "MyService");
    public final static QName MyServicePort = new QName("http://server/schemas/services", "MyServicePort");
    static {
        URL url = MyService.class.getResource("myservice.wsdl");
        if (url == null) {
            Logger.getLogger(MyService.class.getName()).log(Level.INFO, "Can not initialize the default wsdl from {0}", "myservice.wsdl");
        }
        WSDL_LOCATION = url;
    }

    public MyService(URL wsdlLocation) {
        super(wsdlLocation, SERVICE);
    }

    public MyService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public MyService() {
        super(WSDL_LOCATION, SERVICE);
    }

    /**
     * 
     * @return returns EncaminharMensagemPortType
     */
    @WebEndpoint(name = "MyServicePort")
    public MyServicePortType getMyServicePort() {
        return super.getPort(MyServicePort, MyServicePortType.class);
    }

    /**
     * 
     * @param features
     *            A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the
     *            <code>features</code> parameter will have their default values.
     * @return returns EncaminharMensagemPortType
     */
    @WebEndpoint(name = "MyServicePort")
    public MyServicePortType getMyServicePort(WebServiceFeature... features) {
        return super.getPort(MyServicePort, MyServicePortType.class, features);
    }

}

Here is my client without client certificate requirement: (that work fine)

package com.mycompany;

import java.net.URL;

import javax.xml.namespace.QName;

import com.mycompany.IdHolder;
import com.mycompany.MyDataObject;

public class CxfClientSslTest {

    public static void main(String[] args) {
        try {

            QName SERVICE_NAME = new QName("http://server/schemas/services", "MyService");
            URL wsdlURL = new URL("https://localhost:8443/services/MyService?wsdl");
            MyService ss = new MyService(wsdlURL, SERVICE_NAME);
            MyServicePortType port = ss.getMyServicePort();

            IdHolder mensagem = new IdHolder();
            mensagem.setId(1L);
            MyDataObject dataObject = port.getById(mensagem);

            System.out.println("Id: " + dataObject.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And here is my client send his certificate:

package com.mycompany;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.namespace.QName;

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;

public class CxfClientSslTest {

    public static void main(String[] args) {
        try {

            QName SERVICE_NAME = new QName("http://server/schemas/services", "MyService");
            URL wsdlURL = new URL("https://localhost:8443/services/MyService?wsdl");
            MyService ss = new MyService(wsdlURL, SERVICE_NAME);
            MyServicePortType port = ss.getMyServicePort();

            tslIt(port);

            IdHolder mensagem = new IdHolder();
            mensagem.setId(1L);
            MyDataObject dataObject = port.getById(mensagem);

            System.out.println("Id: " + dataObject.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void tslIt(MyServicePortType port) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException,
            UnrecoverableKeyException {
        Client client = ClientProxy.getClient(port);
        HTTPConduit http = (HTTPConduit) client.getConduit();

        TLSClientParameters tlsClientParameters = http.getTlsClientParameters();

        KeyStore keyStore = getKeyStore();
        KeyStore trustStore = getTrustStore();

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

        keyManagerFactory.init(keyStore, "123456".toCharArray());
        KeyManager[] keyMgrs = keyManagerFactory.getKeyManagers();
        tlsClientParameters.setKeyManagers(keyMgrs);

        trustManagerFactory.init(trustStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        tlsClientParameters.setTrustManagers(trustManagers);

        tlsClientParameters.setDisableCNCheck(true);
    }

    public static KeyStore getKeyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        URL keyStoreUrl = CxfClientSslTest.class.getResource("/certs/client.jks");
        File keystoreFile = new File(keyStoreUrl.getPath());
        if (!keystoreFile.exists()) {
            throw new RuntimeException("keystore doesn't exists: " + keystoreFile.getAbsolutePath());
        }

        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream keystoreInput = new FileInputStream(keystoreFile.getAbsolutePath());
        keystore.load(keystoreInput, "changeit".toCharArray());
        keystoreInput.close();
        return keystore;
    }

    public static KeyStore getTrustStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        URL trustStoreUrl = CxfClientSslTest.class.getResource("/certs/client-trust.jks");
        File trustStoreFile = new File(trustStoreUrl.getPath());
        if (!trustStoreFile.exists()) {
            throw new RuntimeException("truststore doesn't exists: " + trustStoreFile.getAbsolutePath());
        }

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream trustStoreInput = new FileInputStream(trustStoreFile.getAbsolutePath());
        trustStore.load(trustStoreInput, "changeit".toCharArray());
        trustStoreInput.close();
        return trustStore;
    }

}

The TLS configuration both from client and server were checked and are ok. But when I run the program I get this:

Information: Can not initialize the default wsdl from myservice.wsdl
javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
    at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:149)
    at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:98)
    at javax.xml.ws.Service.<init>(Service.java:77)
    at com.mycompany.MyService.<init>(MyService.java:36)
    at com.mycompany.CxfClientSslTest.main(CxfClientSslTest.java:32)
Caused by: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
    at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:100)
    at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:199)
    at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:147)
    ... 4 more
Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR: Problem parsing 'https://localhost:8443/services/MyService?wsdl'.: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found
    at com.ibm.wsdl.xml.WSDLReaderImpl.getDocument(Unknown Source)
    at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(Unknown Source)
    at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(Unknown Source)
    at org.apache.cxf.wsdl11.WSDLManagerImpl.loadDefinition(WSDLManagerImpl.java:262)
    at org.apache.cxf.wsdl11.WSDLManagerImpl.getDefinition(WSDLManagerImpl.java:205)
    at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:98)
    ... 6 more
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1868)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1337)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:154)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:868)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:804)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:998)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1294)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1321)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1305)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:523)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1296)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:653)
    at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:189)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:799)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:240)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:300)
    ... 12 more
Caused by: java.security.cert.CertificateException: No name matching localhost found
    at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:208)
    at sun.security.util.HostnameChecker.match(HostnameChecker.java:93)
    at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:347)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:203)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1319)
    ... 30 more

I can see that the problem is happening before my https configuration get done, when cxf tries to download the wsdl.

I made a research on how to make cxf use that https configuration to download the wsdl. It take me a lot of time, but I can't find the answer out there.

So my question is: how to make cxf use the https configuration to download the wsdl?

Please, I have the answer already and I intend to put it here. So, if you don't have a good answer, or better the answer, please don't post one.

like image 404
DiogoSantana Avatar asked Feb 16 '23 23:02

DiogoSantana


2 Answers

After a lot of research on the web without success, I decide it is time to debug the cxf API. That is one of the points of open source, right?

So I found out that cxf don't download directly the wsdl. It delegates that to wsdl4j through the call of

javax.wsdl.xml.WSDLReader.readWSDL(javax.wsdl.xml.WSDLLocator)

which calls

javax.wsdl.xml.WSDLLocator.getBaseInputSource()

which calls

org.apache.cxf.wsdl11.ResourceManagerWSDLLocator.getInputSource(String, String)

because ResourceManagerWSDLLocator were the WSDLLocator of the first method call.

ResourceManagerWSDLLocator.getInputSource first line is:

InputStream ins = bus.getExtension(ResourceManager.class).getResourceAsStream(importLocation);

Now as ResourceManager is a extension of xcf's Bus, and you can add more ResourceResolver to it and the DefaultResourceManager (implements ResourceManager) will loop through all registered resolvers and will use the first one resolving a non-null value, you just need add a ResourceResolver to the ResourceManager.

My final and working client application is:

package com.mycompany;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.namespace.QName;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.resource.ResourceManager;
import org.apache.cxf.resource.ResourceResolver;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

public class CxfClientSslTest {

    public static void main(String[] args) {
        try {

            Bus bus = BusFactory.getThreadDefaultBus();
            ResourceManager extension = bus.getExtension(ResourceManager.class);

            extension.addResourceResolver(new ResourceResolver() {
                @Override
                public <T> T resolve(String resourceName, Class<T> resourceType) {
                    System.out.println("resourceName: " + resourceName + " - resourceType: " + resourceType);
                    return null;
                }

                @Override
                public InputStream getAsStream(String name) {
                    try {
                        if (!name.startsWith("https")) {
                            return null;
                        }
                        SSLSocketFactory sslSocketFactory = SslUtil.getSslSocketFactory();
                        SchemeRegistry schemeRegistry = new SchemeRegistry();
                        schemeRegistry.register(new Scheme("https", 8443, sslSocketFactory));

                        final HttpParams httpParams = new BasicHttpParams();
                        DefaultHttpClient httpClient = new DefaultHttpClient(new BasicClientConnectionManager(schemeRegistry), httpParams);

                        HttpGet get = new HttpGet(name);
                        HttpResponse response = httpClient.execute(get);
                        return response.getEntity().getContent();
                    } catch (Exception e) {
                        return null;
                    }
                }
            });

            QName SERVICE_NAME = new QName("http://server/schemas/services", "MyService");
            URL wsdlURL = new URL("https://localhost:8443/services/MyService?wsdl");
            MyService ss = new MyService(wsdlURL, SERVICE_NAME);
            MyServicePortType port = ss.getMyServicePort();

            tslIt(port);

            IdHolder mensagem = new IdHolder();
            mensagem.setId(1L);
            MyDataObject dataObject = port.getById(mensagem);

            System.out.println("Id: " + dataObject.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void tslIt(MyServicePortType port) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException,
            UnrecoverableKeyException {
        Client client = ClientProxy.getClient(port);
        HTTPConduit http = (HTTPConduit) client.getConduit();

        TLSClientParameters tlsClientParameters = http.getTlsClientParameters();

        KeyStore keyStore = getKeyStore();
        KeyStore trustStore = getTrustStore();

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

        keyManagerFactory.init(keyStore, "123456".toCharArray());
        KeyManager[] keyMgrs = keyManagerFactory.getKeyManagers();
        tlsClientParameters.setKeyManagers(keyMgrs);

        trustManagerFactory.init(trustStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        tlsClientParameters.setTrustManagers(trustManagers);

        tlsClientParameters.setDisableCNCheck(true);
    }

    public static KeyStore getKeyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        URL keyStoreUrl = CxfClientSslTest.class.getResource("/certs/client.jks");
        File keystoreFile = new File(keyStoreUrl.getPath());
        if (!keystoreFile.exists()) {
            throw new RuntimeException("keystore doesn't exists: " + keystoreFile.getAbsolutePath());
        }

        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream keystoreInput = new FileInputStream(keystoreFile.getAbsolutePath());
        keystore.load(keystoreInput, "changeit".toCharArray());
        keystoreInput.close();
        return keystore;
    }

    public static KeyStore getTrustStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
        URL trustStoreUrl = CxfClientSslTest.class.getResource("/certs/client-trust.jks");
        File trustStoreFile = new File(trustStoreUrl.getPath());
        if (!trustStoreFile.exists()) {
            throw new RuntimeException("truststore doesn't exists: " + trustStoreFile.getAbsolutePath());
        }

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream trustStoreInput = new FileInputStream(trustStoreFile.getAbsolutePath());
        trustStore.load(trustStoreInput, "changeit".toCharArray());
        trustStoreInput.close();
        return trustStore;
    }
}
like image 52
DiogoSantana Avatar answered Feb 24 '23 05:02

DiogoSantana


I think the standard way in apache cxf is to set-up an http conduit in your cxf.xml refering to your jks keystore:

<http:conduit id="{Namespace}PortName.http-conduit"> 
<http:tlsClientParameters>
...
<sec:trustManagers>
  <sec:keyStore type="JKS"
                password="StorePass"
                file="certs/truststore.jks"/>
</sec:trustManagers>
 ...
</http:tlsClientParameters>
</http:conduit>

More information here: Configuring SSL support

like image 31
forgacan Avatar answered Feb 24 '23 05:02

forgacan