Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically set the SSLContext of a JAX-WS client?

I'm working on a server in a distributed application that has browser clients and also participates in server-to-server communication with a 3rd party. My server has a CA-signed certificate to let my clients connect using TLS (SSL) communication using HTTP/S and XMPP(secure). That's all working fine.

Now I need to securely connect to a 3rd party server using JAX-WS over HTTPS/SSL. In this communication, my server acts as client in the JAX-WS interation and I've a client certificate signed by the 3rd party.

I tried adding a new keystore through the standard system configuration (-Djavax.net.ssl.keyStore=xyz) but my other components are clearly affected by this. Although my other components are using dedicated parameters for their SSL configuration (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), it seems that they end up using the global SSLContext. (The configuration namespace my.xmpp. seemed to indicate separation, but it's not the case)

I also tried adding my client certificate into my original keystore, but -again- my other components don't seem to like it either.

I think that my only option left is to programmatically hook into the JAX-WS HTTPS configuration to setup the keystore and truststore for the client JAX-WS interaction.

Any ideas/pointers on how to do this? All information I find either uses the javax.net.ssl.keyStore method or is setting the global SSLContext that -I guess- will end up in the same confilc. The closest I got to something helpful was this old bug report that requests the feature I need: Add support for passing an SSLContext to the JAX-WS client runtime

Any takes?

like image 655
maasg Avatar asked Jun 12 '12 16:06

maasg


2 Answers

This one was a hard nut to crack, so for the record:

To solve this, it required a custom KeyManager and a SSLSocketFactory that uses this custom KeyManager to access the separated KeyStore. I found the base code for this KeyStore and SSLFactory on this excellent blog entry: how-to-dynamically-select-a-certificate-alias-when-invoking-web-services

Then, the specialized SSLSocketFactory needs to be inserted into the WebService context:

service = getWebServicePort(getWSDLLocation()); BindingProvider bindingProvider = (BindingProvider) service;  bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());  

Where the getCustomSocketFactory() returns a SSLSocketFactory created using the method mentioned above. This would only work for JAX-WS RI from the Sun-Oracle impl built into the JDK, given that the string indicating the SSLSocketFactory property is proprietary for this implementation.

At this stage, the JAX-WS service communication is secured through SSL, but if you are loading the WSDL from the same secure server () then you'll have a bootstrap problem, as the HTTPS request to gather the WSDL will not be using the same credentials than the Web Service. I worked around this problem by making the WSDL locally available (file:///...) and dynamically changing the web service endpoint: (a good discussion on why this is needed can be found in this forum)

bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation);  

Now the WebService gets bootstrapped and is able to communicate through SSL with the server counterpart using a named (alias) Client-Certificate and mutual authentication. ∎

like image 66
maasg Avatar answered Sep 29 '22 20:09

maasg


This is how I solved it based on this post with some minor tweaks. This solution does not require creation of any additional classes.

SSLContext sc = SSLContext.getInstance("SSLv3");  KeyManagerFactory kmf =     KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );  KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() ); ks.load(new FileInputStream( certPath ), certPasswd.toCharArray() );  kmf.init( ks, certPasswd.toCharArray() );  sc.init( kmf.getKeyManagers(), null, null );  ((BindingProvider) webservicePort).getRequestContext()     .put(         "com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",         sc.getSocketFactory() ); 
like image 44
Radek Avatar answered Sep 29 '22 19:09

Radek