In a break from our Haskell programming…
I used to be a staunch defender of Java as a language. I still think it’s relatively good (though mostly for the tool support), but there are things about it that make me want to scream.
Imagine, for example, you’d like to make an SSL authenticated fetch from a webserver. You have a client certificate to authenticate your client, and a server certificate to authenticate the server, and you’ve generated them both from your own CA. Shouldn’t be that hard, right? Wrong
Everything SSL has to be configured via the key stores, so you need to import your private certificate and the server’s public certificate in to your key store in order to make anything go.
There are two stores - the Trust Store and the Key Store. The Trust store contains the certificates you trust (CAs, etc.). The Key Store contains certificates for which you have the private key and against which you’ll encrypt challenges to verify your identity. All you have to do is populate them…
Step 1: Don’t use GCJ
There are a lot of great things to be said for the Open Source outlook on life. GCJ isn’t one of them. It works quite like Java, except when you try and run anything. Unfortunately it works sufficiently like Java that you don’t necessarily know you’re using it, and it’s installed as the default on a lot of Debian machines.
keytool error: java.lang.IllegalStateException: masked envelope
That was the first cryptic clue that I was using GCJ. Other clues are random GC messages on the console. Here’s a quick way to tell if you’re infected:
$ ls -l /etc/alternatives/ | grep -c java-gcj
24
The number you’re looking for is ‘0′ on a correctly configured system. Specifically you want to see:
$ chase `which keytool`
/usr/lib/jvm/java-1.5.0-sun-1.5.0.15/jre/bin/keytool
$ chase `which java`
/usr/lib/jvm/java-1.5.0-sun-1.5.0.15/jre/bin/java
If you’re not getting that, reconfigure the alternative:
# update-alternatives --config java
There are 7 alternatives which provide `java'.
Selection Alternative
-----------------------------------------------
1 /etc/alternatives/kaffe-system/bin/java
2 /usr/bin/gij-wrapper-4.0
* 3 /usr/lib/jvm/java-1.5.0-sun/jre/bin/java
4 /usr/bin/gij-4.1
5 /usr/bin/gij-4.3
+ 6 /usr/lib/jvm/java-gcj/jre/bin/java
7 /usr/bin/gij-4.2
Step 2: Import the CA
Now we’re running the right JVM, it should be a simple matter of:
$ #Create a trust store with a CA Cert in it (teststore.jks doesn't yet exist)
$ keytool -import -v -trustcacerts -alias myalias -file cacert.pem -keystore teststore.jks
Enter keystore password:
keytool error: java.lang.NullPointerException
Oh. There may be a way to use blank passwords on keystores, but keytool ain’t it. Let’s try again with a password:
$ keytool -import -v -trustcacerts -alias myalias -file cacert.pem -keystore teststore.jks
Enter keystore password: password
Smashing. That’ll mean we can at least connect to the remote host. But the SSL handshake will still fail when the host sees our lack of client certificate.
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Step 3: Import the Client certificate
My client certificate comes as two files - a certificate PEM file (the public part) and a key PEM file (the private part). Naïvely, I tried just installing the PEM part:
$ keytool -import -v -alias myalias2 -file signup1-cert.pem -keystore teststore.jks
Enter keystore password: password
keytool error: java.lang.Exception: Input not an X.509 certificate
*sigh*. That’ll happen. Fortunately, we can convert from PEM to ‘DER’, which is something that keytool understands, using ‘openssl’:
$ openssl x509 -in signup1-cert.pem -inform PEM -out signup1-cert.der -outform DER
$ keytool -import -v -alias myalias2 -file signup1-cert.der -keystore teststore.jks
Enter keystore password: password
Certificate was added to keystore
[Storing teststore.jks]
It’s stored, but unfortunately a) it doesn’t work and b) keytool thinks this is a ‘trustedCertEntry’ rather than a ‘keyEntry’:
$ keytool -v -list -keystore teststore.jks
...
Entry type: trustedCertEntry
...
Now, we can use ‘openssl’ to convert our certificate and key into a PKCS#12 combined key file:
$ openssl pkcs12 -export -in signup1-cert.pem -inkey signup1-key.pem -out signup1.p12
Even better, according to the documentation, PKCS#12 format files are valid key stores… unless you try and use them:
default context init failed: java.io.IOException: Invalid keystore format
Right. Let’s try that as a combined PEM format file then:
$ openssl pkcs12 -in mykey.p12 -out keystore.pem -nodes
$ keytool -import -v -alias clientcert -file keystore.pem -keystore keystore.jks
Enter keystore password: password
keytool error: java.lang.Exception: Input not an X.509 certificate
True. (Incidentally, that’s No DES, not ‘nodes’) But we can convert PEM files to DER files
$ openssl x509 -in keystore.pem -inform PEM -out keystore.der -outform DER
$ keytool -import -v -alias clientcert -file keystore.der -keystore keystore.jks
Enter keystore password: password
Step 4: Using the keystore in your program
You can configure the keystore at runtime as follows:
System.setProperty("javax.net.ssl.keyStore", context.getRealPath(KEYSTORE));
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", context.getRealPath(TRUSTSTORE));
System.setProperty("javax.net.debug", "ssl");
HttpClient httpClient = new HttpClient();
GetMethod httpGet = new GetMethod("https://something.com");
httpClient.executeMethod(httpGet);
return new String(httpGet.getResponseBody());
javax.net.debug=ssl truly is a magic rune. I don’t know if you can get a list of such runes, but commit that one to memory. The debug output is pretty handy, if I little hard to follow.
Step 5: Become frustrated
What I didn’t mention, and perhaps should have mentioned above, is that I still hadn’t managed to import my key as a keyEntry, so this code still didn’t work. I downloaded the source code to the JDK and tried single-stepping KeyTool, but that also didn’t help. [Aside: People who create compressed archives without a top-level folder should be shot].
Step 6: Use KeyMan
KeyMan can be downloaded from IBM Alphaworks at time of writing:
http://www.alphaworks.ibm.com/tech/keyman/download
It ‘just works’. It lets you import your certificate and create a valid key store. Thanks IBM! (Source code plz).