This blog post suggests a fix for an error such as this
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
which I experienced building an Apache CXF 2.7.3 client with an https URL.
Works for HTTP
This code builds a client object. "SysControlService" is a stub generated from my web service definition. I have some extra MTOM configuration for an attachment. This client does not use a Spring beans config file.Map<String, Object> properties = new HashMap<String, Object>();
properties.put("mtom-enabled", Boolean.TRUE);
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(SysControlService.class);
factory.setAddress(url);
factory.setProperties(properties);
client = (SysControlService) factory.create();
If attachments weren't needed, I could remove the code related to the "properties" object.
Broken with https://
When I ran this with an SSL (TLS) URL, I received an SSLHandshakeError. I was surprised, because I knew the certificate to be valid. After double-checking the configuration in SOAP UI, I then proceeded to write my own web service call using HttpURLConnection. I used the same method, payload, and URL as my generated stub. However, my hand-coded call didn't throw any SSL errors and it returned the correct response.This verified that my certificate was good and that the JVM recognized the certification path.
What I suspect is happening with the Apache CXF version is that it blanks-out the truststore and keystore settings by default. So, the empty truststore won't recognize any certificate path. This seems to be confirmed in the documentation through the support of the useHttpsURLConnectionDefaultSslSocketFactory property. I figured that setting this value to true (the default is false) would give Apache CXF the same behavior as I found with my hand-coded call.
Fix - Set TLSClientParameters
Specifying this value worked. Here is the re-written block of codeMap<String, Object> properties = new HashMap<String, Object>();
properties.put("mtom-enabled", Boolean.TRUE);
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(SysControlService.class);
factory.setAddress(url);
factory.setProperties(properties);
client = (SysControlService) factory.create();
Client cp = ClientProxy.getClient(client);
HTTPConduit httpConduit = (HTTPConduit)cp.getConduit();
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setUseHttpsURLConnectionDefaultSslSocketFactory(true);
httpConduit.setTlsClientParameters(tlsParams);
Since this particular app is deployed by JNLP, it's not practical to specify the truststore and keystore properties. This isn't needed anyway, because today's JVMs ship with a lot of certificate authorities. You won't need your own stores unless you're generating your own. I feel like the Apache CXF default should be the JVM default, but you can make it so with these few extra lines.
Thank you. Apache CXF 5 still not happy to call https without truststore.
ReplyDelete