Here I'll present the steps of a recent exercise I completed, around configuring SSL on the client side, encapsulating the configuration for transparency. For some background on SSL and the topics discussed in this article, here are some resources:
SSL Protocol Overview
SSL Certificates HOWTO
Transport Layer Security
The Security Now podcast series also has some excellent deep-dives into SSL and many other security-related topics.
The context for this article is a client-server communication where the server presents its certificate but the client doesn't recognize the certificate authority (CA). That might be because the certificate is self-signed, or is signed by a CA that is not in the client's truststore. If the certificate is self-signed, the client needs to import that certificate (e.g. using keytool) into its truststore; else, the client needs to add the CA that signed the certificate to its truststore. In either event, we need to make sure the client is in fact aware of the truststore location at runtime. As it turned out in my case, the CA was in fact already in the truststore; I just needed to point the runtime process to that location. I won't discuss the details around importing certificates, etc.; that type of thing is well covered in the links I provide here.
What I will do is mention some of the exceptions one can encounter when going down this road. If, when addressing a server that uses SSL, you see this:
Remote host closed connection during handshake
...make sure you're using the secure port (e.g. 443, perhaps 8443, etc.). If using the correct port, but you see this:
unable to find valid certification path to requested target
...it may be that the server's certificate is either self-signed, or is signed by a CA that the client doesn't know about - and, as we already know, this was my particular situation. To remedy this, we can start up the JVM process with a system property that specifies the correct location:
-Djavax.net.ssl.trustStore=<filename>
Now, if the given filename is not correct, we'll get this:
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
Specifying it with the correct filename should result in success. Now, if we rely on the command line system property approach, this will need to be repeated for all clients (each and every test case, installation scripts, production code, etc.). Alternately, we can encapsulate this configuration into a class that gets instantiated once at startup, and during construction sets the system property under the covers. We can use Spring to wire this up:
<!--
Assumes the keystore artifact exists somewhere in the runtime classpath
-->
<bean id="context" class="com.mybiz.security.Context">
<property name="keystoreLocation" value="classpath:mycerts.truststore"/>
</bean>
Here's the Context class:
public class Context {
public void setKeystoreLocation(Resource keystoreResource) throws IOException {
File keystoreFile = keystoreResource.getFile();
System.setProperty("javax.net.ssl.trustStore", keystoreFile.getAbsolutePath());
}
}
The Spring snippet would have to be replicated for each test case, installation script, production class, and etc. - but the details of its functionality are now encapsulated. All the client needs to do is ensure the truststore file is in the runtime classpath and include the Spring snippet. If there are any other Spring beans that depend on the system property being set before they are constructed (e.g. if they need to access the SSL resource), we can use the depends-on attribute to facilitate this:
<bean class="com.mybiz.DefaultDAO" depends-on="context"/>
No comments:
Post a Comment