In part 1, I explained how key-based SSL authentication works. In part 2, I intend to explain how to implement a certificate authorized SSL connection in Java.
If you haven't read part 1 yet, you might want to check it out. I'll still be here when you get back.
Dealing with Certificates
Before you can use keys for secure identification, obviously you need to get a key pair. You request one from a Certificate Authority, of course... if you've got enough money to pay for it.
At this point, you might be asking yourself why the CA is a Certificate Authority instead of a Key Authority. It's simple, really: the keys they hand you include not only the key itself, but some additional information -- how long the key is valid, who the key belongs to, which CA issued it, a checksum signed with the CA's own key to verify that the whole package of information is real, and so on. All together, this information is called a "certificate".
From this point on, I'll use the terms "key" and "certificate" interchangeably. If I need to talk about the actual key contained in a certificate, I'll explicitly tell you so.
Java comes with a handy keyword management tool, imaginatively named "keytool". It works from the command prompt (all you Windows users will be more likely to recognize this as the "Terminal Window" or "cmd.exe").
Keytool allows you to generate your own certificates. Most of the times that you need to program SSL sockets, the customer will hand you the certificates, but when you need to generate your own, here's how:
[code] keytool -genkeypair -keystore keystore.jks -alias keyname [/code]When you type this, you should substitute your own file for keystore.jks, and whatever you want to name your key for keyname. You'll be prompted for the password and other information.
Both the public and private key are stored in the keystore. Obviously, this is not what you want to hand out to other people. Here's how to extract just the certificate containing the public key:
[code] keytool -export -alias keyname -file keyname.crt [/code]Again, substitute something reasonable for keyname. The file "keyname.crt" will now contain your public key in a certificate (hence the ".crt" extension); you can give it to anyone, and they can use it to authenticate future information from you.
If you're lucky, you get a certificate repository from the customer. This is also known as a "truststore". It stores the public key certificates of the servers you can trust. Clever, no?
If you're not lucky, you just get a certificate. No sweat; you just need to create your own truststore containing the certificate:
[code] keytool -importcert -file keyname.crt -keystore truststore.jks [/code]Of course, you'll need to substitute appropriate files for keyname.crt and truststore.jks. The truststore will be created, if it does not already exist. You'll be prompted for passwords and such.
Of course, if you ever want to list what's in a keystore (handy for testing passwords), here's the command:
[code] keytool -list -v -keystore keystore.jks [/code]Dealing with SSL
Now that you're got a keystore with the server's public key certificate, you need to tell Java about it. This couldn't be easier! You just need to define two Java properties. Although you can use the "-D" switch on the command line, I'll do it here in Java:
[code] System.setProperty("javax.net.ssl.trustStore", truststorePath); System.setProperty("javax.net.ssl.trustStorePassword", truststorePass); [/code]Here I assume you've already read the truststore filename into truststorePath, and you've read the password into truststorePass. They could come from an XML configuration file, the command line, or wherever; the point is, your application has some way of figuring them out, and this is how you tell Java what they are.
Now you can feel free to create your SSL socket:
[code] Socket socket = SSLSocketFactory.getDefault().createSocket(server, port); [/code]Java will automatically authenticate the server using the certificates in your truststore.
If you want to create your own SSL server, you'll need to create your own key pair, as above. You give the exported public certificate to anyone who wants to connect to your server. You keep the original certificate yourself, and put it in the keystore (as opposed to the truststore). The properties are the same, except that they use "key" instead of "trust". Then you use the SSLServerSocketFactory instead of the SSLSocketFactory:
[code] System.setProperty("javax.net.ssl.keyStore", keystorePath); System.setProperty("javax.net.ssl.keyStorePassword", keystorePass); SSLServerSocket serverSocket = SSLServerSocketFactory.getDefault().createServerSocket(9999); SSLSocket workSocket = (SSLSocket) serverSocket.accept(); [/code]Conclusion
Of course, there are other ways to do this. Jetty has its own method of specifying keystores, and other HTTP frameworks probably do, too. However, just setting the Java properties mentioned here will take care of any certificate problems you have.
If there's enough interest, we can talk about creating your own trust managers -- this allows you to use your own criteria for whether to allow an SSL connection to continue. You can make them as strong or as weak as you like, even allowing any connection to succeed.
This article is long enough already. I hope it's been as helpful to you as it was to me!