Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Saturday, December 13, 2014

Fixing SSL Handshake Error in Apache CXF

Without SSL (TLS), you can programmatically create an Apache CXF web services client using a generated stub and immediately make a call after specifying the URL and the target class.  You need extra configuration if the generated stub's communication needs to be encrypted.

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 code

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

1 comment:

  1. Thank you. Apache CXF 5 still not happy to call https without truststore.

    ReplyDelete