Saturday 26 March 2011

Using Sun JSSE on IBM J9 JVM

A client asked to secure communications between quite old devices running IBM J9 JVM version 1.3. The geographically dispersed devices communicate with central servers via web services over HTTP protocol to sync data. What appeared to be a very simple task to switch to SSL protocol by replacing http with https in a config file, turned out to be quite a daunting exercise.
Though it is unlikely someone else encounters similar tasks, information below may also be helpful for learning intricacies around SSL certificates, keystores and even JSSE in general.

In order for an application running on a J2ME device under IBM J9 virtual machine to access a web service provided over secure HTTPS protocol, the procedure described in the document should be implemented.
Prior to JDK 1.4 version, the Java Secure Socket specification was not a part of standard JDK and therefore vendors were not required to implement it. It is quite likely that IBM did implement SSL in their J9 JVM version 1.3, however, it had not been possible to locate any usable user and developer documentation. On the contrary, Sun provided a well-documented reference implementation called JSSE. This document describes how to setup and configure the Sun JSSE library on a device running IBM J9 Java virtual machine.
The JSSE setup would be a straightforward exercise except that there is a problem with the keystore format. The JDK 1.3 specification, particularly java.securtiy.KeyStore class, does not specify persistence mechanism to store keys and certificates. Apparently, the JSSE library relies on a vendor implementation, in this case IBM, for providing persistence mechanism. Attempting to connect over HTTPS protocol using the standard cacerts keystore file results in the “java.net.SocketException: SSL implementation not available” exception. This is, however, not the root cause of the problem. As it turned out, the JSSE fails to initialize the truststore providing this error message:  
“default context init failed: java.security.PrivilegedActionException: java.io.IOException: Invalid keystore”

Running J9 with the –verbose flag reveals that JVM loads com/ibm/oti/security/provider/KeyStore class. In the contrast, the same test code, run under Sun JRE 1.3_15, loads the sun.security.provider.JavaKeyStore class from the rt.jar. Therefore, the conclusion is that IBM persistence implementation of keystore is not compatible with the Sun's. This document includes a description of how to convert the standard Sun cacerts file to the IBM J9 specific format that can be processed by the com.ibm.oti.security.provider.KeyStore class.
The keytool (included in both Sun and IBM JVM distributions) utility stores the keys and certificates in a so-called keystore. By default, both Sun and IBM implement the keystore as a file in JKS format. However, the IBM file format is not compatible with Sun JKS format. The following is the description on how to create a keystore file in IBM J9 format and install and configure Sun JSSE library to run in J9 VM. 

1. Export the root Certification Authority certificates from the standard cacerts file.
1.1. Open a console window and navigate to a working directory, e.g. c:\projects\certificates
1.2. Set path to Sun’s keytool with the command, for example in Windows:
set PATH=<JDK-installation>/jre/bin;%PATH%
1.3. Locate the cacerts file in jre\lib\security folder of a Sun JDK installation and copy it into the working directory.
1.4. List all available certificates with the command into a temporary file:
keytool -list -keystore cacerts > temp.txt
When prompted, enter the password ‘changeit’ (it is a default password that was set by Sun). Here is a sample what the file should contain:
Enter keystore password:changeit
Keystore type: jks
Keystore provider: SUN
Your keystore contains 33 entries:
verisignclass1g3ca, Thu Mar 25 14:27:59 EST 2004, trustedCertEntry, Certificate fingerprint (MD5): B1:47:BC:18:57:D1:18:A0:78:2D:EC:71:E8:2A:95:73
equifaxsecureebusinessca1, Fri Jul 18 14:43:22 EDT 2003, trustedCertEntry, Certificate fingerprint (MD5): 64:9C:EF:2E:44:FC:C6:8F:52:07:D0:51:73:8F:CB:3D
verisignclass2g2ca, Thu Mar 25 14:18:49 EST 2004, trustedCertEntry, Certificate fingerprint (MD5): 2D:BB:E5:25:D3:D1:65:82:3A:B7:0E:FA:E6:EB:E2:E1
verisignclass3g3ca, Thu Mar 25 14:31:09 EST 2004, trustedCertEntry,
...

1.5. Each certificate in cacerts file has to be exported into an individual certificate file. To do this, you need to run the following command for each alias (aliases are shown in bold in the sample above. If you want, you can edit the file to leave only aliases in it – this is all information that you need from this temporary file):
keytool -export -keystore cacerts -file <alias>.cer -alias <alias>
where <alias> is an alias in the file, e.g. verisignclass1g3ca. On each run you will have to enter the password – ‘changeit’.
2. Import the root certificates into IBM-specific file.
For this procedure you need an IBM keytool utility that generates keystore file in J9 1.3 specific format. One place where it can be found is a WebSphere Studio Device Developer package.
2.1. There are several keytool utilities in the package. To find the right one you can try this: search the WSDD installation directory for keytool.exe files. Open a console window and navigate to the directory that contains a keytool.exe. Because the file formats are incompatible, the IBM keytool is not able to understand the Sun format (and vice versa). To test it, try to list certificates from the Sun keystore file by running the command:
keytool –list –keystore c:\projectes\certificates\cacert

At the prompt, enter the password changeit
If it fails with a message similar to this: keytool error: java.io.IOException: Invalid keystore, this is the utility that you need.
In the default installation of a trial version of WSDD 5.6, the keytool utility is located in the following directory:
C:\Program Files\IBM\DeviceDeveloper5.6\wsdd5.0\ive-2.1\bin 

When running the IBM keytool utility, make sure that the environment does not have JAVA_HOME variable, otherwise the utility will not be able to locate the runtime library and will fail with a message similar to this:
Fatal error: Unable to find and initialize required class java/lang/Object
2.2. Import each certificate file that was created in the step 1 to an IBM-specific keystore by running the following command:

keytool -import -alias <alias> -file c:\projects\certificates\<alias>.cer -keystore c:\projects\certificates\cacerts.j9 

Enter the same password for each certificate.
3. HTTPS Support
The JSSE implementation contains a URL handler for the HTTPS protocol. In order to use this handler, the handler's implementation package name has to be added to the list of packages which are searched by the java URL class. This is configured via the "java.protocol.handler.pkgs" system property. See the java.net.URL class documentation for details.
System properties can be set via the command line or at runtime through the java.lang.System class.
For example, you can set this property on the command line via:

java -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol

When accessing HTTPS servers through a web proxy, you must set the "https.proxyHost" and "https.proxyPort" system properties to the correct host name and port number of the web proxy. For example, to set this property on the command line to access HTTPS servers through the proxy host "webproxy" running at port 8080 you would use:
java -Dhttps.proxyHost=webproxy -Dhttps.proxyPort=8080
To configure the handler at runtime, the following code has to be added to the application start-up module:
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
4. Configuring the TrustManager keystore location. 
The created in the step 2 cacerts.j9 file is a keystore file in an IBM J9 specific format. This file has to be available to the JVM running a program that uses SSL. The location of the file and the password for the keystore can be specified through appropriate system properties dynamically, e.g.
java -Djavax.net.ssl.trustStore=cacerts.j9 -Djavax.net.ssl.trustStorePassword=<password>


or they can be statically coded into a program like this:


System.setProperty("javax.net.ssl.trustStore", "cacerts.j9");
System.setProperty("javax.net.ssl.trustStorePassword", “<password>”);
5. Sun Java Secure Socket Extension package
Download Sun Java Secure Socket Extension package – JSSE from http://java.sun.com/products/jsse/index.jsp
The JSSE version 1.0.3_03 is the latest (and the last) implementation available for Java 1.2 and 1.3 (Update: it has reached EOL now). Install the package on a development computer. You can find 3 jar files in the JSSE lib directory: jcert.jar, jnet.jar, jsse.jar. Copy the files to a lib directory on a clock device and include them into the classpath.
6. Register the SunJSSE provider.
To dynamically register the provider, include the following line into the application startup code:
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); 

Detailed instructions can be found in INSTALL.txt file of the JSSE installation.

7. To test the installation 
Configure the device to access a server via HTTPS protocol. If the server uses a certificate signed by a Certificate Authority (CA), verify that the CA was added to the cacerts.j9 file (Step 2). If using a self-signed certificate, add the certificate to the cacerts.j9 following the procedure described in step 2. 
Copy the cacerts.j9 file to a device. Run the device application launching it from a starter class that adds the security provider (step 6). Verify log files contain no errors when accessing the web services over HTTPS.

Useful links that can be used for understanding the subject and troubleshooting: