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.
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
which calls
which calls
because ResourceManagerWSDLLocator were the WSDLLocator of the first method call.
ResourceManagerWSDLLocator.getInputSource first line is:
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:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching localhost found
Above exception happens When you create your self-signed certificate with your name, To resolve this exception, you need to add
"localhost"
on the following stepI think the standard way in apache cxf is to set-up an
http conduit
in your cxf.xml refering to your jks keystore:More information here: Configuring SSL support
Above line is required to disable default SslSocketFactory (which will ignore the keyStore and trustStore configured in tlsClientParamters)