views:

183

answers:

2

Our system communicates with several web services providers. They are all invoked from a single Java client application. All the web services up until now have been over SSL, but none use client certificates. Well, a new partner is changing that.

Making the application use a certificate for the invocation is easy; setting javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword will do it. However, the problem is now how to make it so that it only uses the certificate when invoking that particular web service. I guess more generally speaking, we'd like to be able to choose the client certificate to be used, if any.

One quick solution could be setting the system properties, invoking the methods, and then unsetting them. The only problem with that is that we're dealing with a multi-threaded application, so now we would need to deal with synchronization or locks or what have you.

Each service client is supposed to be completely independent from each other, and they're individually packaged in separate JARs. Thus, one option that has occurred to me (although we haven't properly analyzed it) is to somehow isolate each JAR, maybe load each one under a different VM with different parameters. That's merely an idea that I don't know how to implement (or if it's even possible, for that matter.)

This post suggests that it is possible to select an individual certificate from a key store, but how to attach it to the request seems to be a different issue altogether.

We're using Java 1.5, Axis2, and client classes generated with either wsimport or wsdl2java.

+1  A: 

Java SSL clients will only send a certificate if requested by the server. A server can send an optional hint about what certificates it will accept; this will help a client choose a single certificate if it has multiple.

Normally, a new SSLContext is created with a specific client certificate, and Socket instances are created from a factory obtained from that context. Unfortunately, Axis2 doesn't appear to support the use of an SSLContext or a custom SocketFactory. It's client certificate settings are global.

erickson
Unfortunately we have no control over the servers. I was also hoping that those hints could be useful, but a test showed that at least with one server they weren't. WRT Axis2, that has also been my experience. If you happen to know of another Java tool that allows this sort of behavior, I'd greatly appreciate your sharing; we'll gladly explore it.
Carlos
@Carlos - Maybe I wasn't clear. You don't need any control over the server; if your partner is now requiring a client certificate, they will configure their server to request it. Your client will *not* send that certificate to any other servers, because they (presumably) are not requesting it. The only potential problem would be if additional services start requiring a client certificate, but don't accept the same CAs.
erickson
Got it, thanks. I guess the other question remains, though. If tomorrow we add another web service that also requires a client certificate, is there a way to choose which certificate in the key store to present to each service?
Carlos
It looks like Axis 2 uses Apache HTTP Client 3.x underneath, so it should be possible to configure an `SSLContext` this way (although I haven't tried).
Bruno
Thanks for your help!
Carlos
A: 

The configuration is done via an SSLContext, which is effectively a factory for the SSLSocketFactory (or SSLEngine). By default, this will be configured from the javax.net.ssl.* properties. In addition, when a server requests a certificate, it sends a TLS/SSL CertificateRequest message that contains a list of CA's distinguished names that it's willing to accept. Although this list is strictly speaking only indicative (i.e. servers could accept certs from issuers not in the list or could refuse valid certs from CAs in the list), it usually works this way.

By default, the certificate chooser in the X509KeyManager configured within the SSLContext (again you normally don't have to worry about it), will pick one of the certificates that has been issued by one in the list (or can be chained to an issuer there). That list is the issuers parameter in X509KeyManager.chooseClientAlias (the alias is the alias name for the cert you want to picked, as referred to within the keystore). If you have multiple candidates, you can also use the socket parameter, which will get you the peer's IP address if that helps making a choice.

If this helps, you may find using jSSLutils (and its wrapper) for the configuration of your SSLContext (these are mainly helper classes to build SSLContexts). (Note that this example is for choosing the server-side alias, but it can be adapted, the source code is available.)

Once you've done this, you should look for the documentation regarding the axis.socketSecureFactorysystem property in Axis (and SecureSocketFactory). If you look at the Axis source code, it shouldn't be too difficult to build a org.apache.axis.components.net.SunJSSESocketFactory that's initialized from the SSLContext of your choice (see this question).

Just realized you were talking about Axis2, where the SecureSocketFactory seems to have disappeared. You might be able to find a workaround using the default SSLContext, but this will affect your entire application (which isn't great). If you use a X509KeyManagerWrapper of jSSLutils, you might be able to use the default X509KeyManager and treat only certain hosts as an exception. (This is not an ideal situation, I'm not sure how to use a custom SSLContext/SSLSocketFactory in Axis 2.)

Alternatively, according to this Axis 2 document, it looks like Axis 2 uses Apache HTTP Client 3.x:

If you want to perform SSL client authentication (2-way SSL), you may use the Protocol.registerProtocol feature of HttpClient. You can overwrite the "https" protocol, or use a different protocol for your SSL client authentication communications if you don't want to mess with regular https. Find more information at http://jakarta.apache.org/commons/httpclient/sslguide.html

In this case, the SslContextedSecureProtocolSocketFactory should help you configure an SSLContext.

Bruno
Hum... and just realized you're using Java 1.5, so no `SSLContext.setDefault(...)` anyway. Why not use Java 6? Java 5 is no longer supported as far as I know (it would do no harm to have various security patches).
Bruno
(Setting the `SSLContext` via Apache HTTP Client 3.x should work with Java 1.5.)
Bruno
Thanks for your help!
Carlos
@Carlos, no problem. Did the change of SSLContext via Apache HTTP Client's Protocol.registerProtocol work with Axis 2 (just curious)?
Bruno
Actually, no change was needed, but it was good to have those general comments and input.
Carlos