Apache CXF wsdl通过SSL / TLS下载

我有一个运行的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 * features 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.(ServiceImpl.java:149) at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:98) at javax.xml.ws.Service.(Service.java:77) at com.mycompany.MyService.(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.(WSDLServiceFactory.java:100) at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:199) at org.apache.cxf.jaxws.ServiceImpl.(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.(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 

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

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

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

拜托,我已经有了答案,我打算把它放在这里。 所以,如果你没有一个好的答案,或者更好的答案,请不要发一个。

经过大量的网络研究没有成功,我决定是时候调试cxf API了。 这是开源的一点,对吧?

所以我发现cxf不直接下载wsdl。 它通过调用将其委托给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的Bus的扩展,你可以向它添加更多的ResourceResolver,DefaultResourceManager(实现ResourceManager)将循环遍历所有已注册的解析器,并将使用第一个解析非null值,你只需要添加一个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 resolve(String resourceName, Class 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; } } 
 tlsClientParameters.setUseHttpsURLConnectionDefaultSslSocketFactory(false); 

需要以上行来禁用默认的SslSocketFactory(它将忽略tlsClientParamters中配置的keyStore和trustStore)

我认为apache cxf的标准方法是在你的cxf.xml中设置一个引用你的jks密钥库的http conduit

   ...    ...   

更多信息: 配置SSL支持

javax.net.ssl.SSLHandshakeException:java.security.cert.CertificateException:找不到与localhost匹配的名称

发生以上exception当您使用您的名称创建自签名证书时,要解决此exception,您需要在以下步骤中添加"localhost"

 What is your first and last name? [Unknown]: localhost