java - Apache CXF wsdl 通过 SSL/TLS 下载

标签 java ssl wsdl cxf client-certificates

我有一个 cxf 服务运行在

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

需要客户端证书。 WSDL 在这里并不重要。

当我删除客户端证书或 https 要求时,我能够调用该服务。

服务和客户端类是使用 cxf wsdl2java 实用程序生成的。

这是 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);
    }

}

这是我的客户,没有客户证书要求:(工作正常)

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();
        }
    }
}

这是我的客户发送他的证书:

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;
    }

}

来自客户端和服务器的 TLS 配置已经过检查,没有问题。但是当我运行程序时,我得到了这个:

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

当 cxf 尝试下载 wsdl 时,我可以看到问题发生在我的 https 配置完成之前。

我研究了如何让 cxf 使用那个 https 配置来下载 wsdl。这花了我很多时间,但我找不到答案。

所以我的问题是:如何让cxf使用https配置下载wsdl?

拜托,我已经有了答案,我打算把它放在这里。因此,如果您没有好的答案或更好的答案,请不要发布。

最佳答案

在网络上进行了大量研究但没有成功之后,我决定是时候调试 cxf API 了。这是开源的要点之一,对吧?

所以我发现cxf不直接下载wsdl。它通过调用将其委托(delegate)给 wsdl4j

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

调用

javax.wsdl.xml.WSDLLocator.getBaseInputSource()

调用

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

因为 ResourceManagerWSDLLocator 是第一个方法调用的 WSDLLocator。

ResourceManagerWSDLLocator.getInputSource 第一行是:

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

现在,由于 ResourceManager 是 xcf 总线的扩展,您可以向其添加更多 ResourceResolver,DefaultResourceManager(实现 ResourceManager)将遍历所有已注册的解析器,并将使用第一个解析非空值,您只需要将 ResourceResolver 添加到 ResourceManager。

我最终的工作客户端应用程序是:

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;
    }
}

关于java - Apache CXF wsdl 通过 SSL/TLS 下载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15755293/

相关文章:

java - 我该如何修复 Java 7 Files.write 错误?

wsdl - 将 Web 服务请求参数设为必填字段

java - 如何解析wsdl和xsd文件?

java - 将字符串分配给队列

java - maven和gradle可以和本地仓库共享同一个文件夹吗?

java - 如何在 Alfresco 中使用 Rest API 创建文档库

svn - 使用 curl 将文件从 subversion 目录复制到 unix 服务器中的某个位置时出现身份验证错误

ubuntu - 启动 jupyter notebook 时在 Ubuntu 上出现 SSLError : [SSL: EE_KEY_TOO_SMALL] ee key too small (_ssl. c:4022)

apache - .htaccess 使用 laravel 重定向到 ssl

java - JAX WS异步客户端: capture WS-Addressing 202 accepted