Java Secure Socket Extension (JSSE) is a Java standard encapsulating the low level programming interfaces to the Secure Socket Layer (SSL) protocol and its corresponding standard Transport Layer Security (TLS) protocol. IBM JSSE is the Java implementation of the JSSE framework. It supports SSL V2 and V3 and TLS V1. JSSE is architected such that it provides for two sets of interfaces: one is referred to as the Service Provider Interface (SPI) and the other one is an Application Programming Interface (API).
Those who provide implementation-specific function plugs use the provider interface (essentially, the plug into the interface). Application programmers generally deal only with the API. They can write portable code that depends only upon the methods that are exposed in the standard public APIs. The IBM JSSE implementation includes the IBM JSSE cryptographic providers as well.
It is important that developers adhere to the best programming practices in order to enable application portability. The JSSE API can be portable as long as the use of provider-specific information is not embedded or hardcoded in the application. IBM makes no claim of interoperability with other JSSE providers. No formal testing has been performed by the IBM development laboratory.
The intent of separating the API and SPI interfaces is to shield applications from the provider so that portability can be achieved. The functionality provided by each provider, however, may not necessarily match functionality provided by others. While the IBM JSSE includes IBM's providers, other vendors have their own providers. When using WebSphere Application Server, for example, we are focusing on IBM's own providers. Therefore, application code that assumes any particular provider is not portable. One common example of this is application code that explicitly loads com.sun.* classes. Since com.sun.* is not part of JSSE (or J2SE for that matter), such code is not portable. As you develop your code you should work hard to avoid provider-specific dependencies. Our examples here demonstrate that approach.
Programming to the JSSE is made simple in large part due to the high level abstractions that it provides around the standard socket programming interface. This makes it easy to establish network connections between two end-points that wish to communicate using transport level security over the TCP/IP protocol. The security services provided by JSSE consist of the transport-level message integrity and confidentiality (encryption), server authentication, and optional client authentication.
In this document, we address the configuration of the IBM JSSE, discuss the keystore and truststore aspects, and make recommendations for handling these important elements of the JSSE in the WebSphere Application Server environment. Subsequently, we present the JSSE programming model and show how simple it is when accessing resources available over HTTPS. Finally, we show how multiple keystores/truststores can be used simultaneously in a single application.
WebSphere Application Server V5 (and later) and WebSphere Studio V5 ship with ibmjsse.jar (which supports JSSE version 1.0.3) and its related certpath.jar. IBM JSSE is, therefore, automatically available for use by applications running in the WebSphere Application Server environment. To reiterate, WebSphere Application Server includes JSSE and fully supports it out of the box. No further install is required on your part; in fact, replacing JSSE or JCE implementations from other sources is forbidden. (You cannot override core runtime function. You can, of course, have additional providers, but don't replace what is already there). You do need to make sure that JSSE and its supporting JAR files, such as certpath.jar, are on your classpath during development (they are always on the classpath during run time). certpath.jar is a package that contains certificate path construction and validation functions needed by the JSSE runtime in order to establish certificate trust paths. You not need to program to the certpath.jar APIs to establish SSL/TLS communication channels, but JSSE will use it.
IBM JSSE provides the JSSE functionality for your WebSphere Application Server environment and deployed applications. This is statically set in the java.security configuration file in the JDK directory under your WAS install path. The following is the list of default providers listed out of the box in your java.security file on a Windows® system. Note that the names of the providers remain the same across platforms; the ordering, however, may be different. Regardless of platform, your out-of-the-box JSSE provider for WebSphere Application Serer is IBMJSSEProvider.
# # List of providers and their preference orders # security.provider.1=sun.security.provider.Sun security.provider.2=com.ibm.crypto.provider.IBMJCE security.provider.3=com.ibm.jsse.IBMJSSEProvider security.provider.4=com.ibm.security.cert.IBMCertPath security.provider.5=com.ibm.crypto.pkcs11.provider.IBMPKCS11
There is no need for you to change any of these. If you are curious, there are a number of providers here, most of which are not related to JSSE. The only JSSE-related providers are the IBMJSSEProvider, IBMJCE, and IBMCertPath. The latter two are used implicitly by the IBMJSSEProvider for certificate manipulation and encryption.
The SSL protocol is based on public-key cryptography where encryption keys come in pairs that are mathematically related, but one cannot be deduced from knowing the other. The pair consists of the private key and the public key (private, public). The private key remains under the protection of the entity to which it belongs. Note that the owner really owns the pair, but the public key, as its name suggests, can be publicly known, or at least is freely disseminated to the entities that communicate with the owner of the public/private key pair. The security services that public key cryptography enable are based on the fact that a message encrypted using the public key can only be decrypted by the corresponding private key, and vice versa. Thus, if one encrypts a message using the public key of an entity, then it is guaranteed that only the entity can decrypt the message. An immediate question that comes to mind is how to be assured that the public key being used is indeed bound to a legitimate entity and not to someone else. That is where Public Key Infrastructures (PKI) comes into play (see Resources).
Figure 1. The relationship between client and server truststore/keystore
Public Keys are distributed in containers called public key certificates (PKC). These are data constructs digitally signed by some signer (typically a Certificate Authority, but public keys can be self-signed). A digital signature stamps a proof of origin authenticity. One point to remember is that trust in computer security must be computationally verifiable. In order to verify that a public key certificate is legitimate, I need to verify the signature of the signer that issued it. This process is, in turn, based on the public key of the signer.
This is where the truststore comes into play. When using JSSE for SSL communication, one needs to maintain the set of trusted signer certificates in a local store, hence the name truststore. The truststore on the client side, for example, is used by the JSSE runtime in order to verify that a client attempting to connect to a server is indeed interacting with a server with a legitimate certificate (one that is issued by a trusted signer). Therefore, the signer of the server certificate must have a PKC stored in the truststore of the client. Figure 1 illustrates the relationship between client and server truststore/keystore.
By the same token, the server must have its private key locally protected and make it accessible to the JSSE runtime. That's where the keystore comes into play. A keystore has the same format as a truststore, it just contains different keys. In fact, the generic term "keystore" is typically used to mean "truststore or keystore."
In summary, truststores contain certificates for the signers that are trusted in the environment where the truststore is used. A keystore, on the other hand, maintains the private key of an entity, as well as its corresponding PKC. For example, when a server authenticates itself to the client, it needs to retrieve its private key from its keystore in order to engage in an SSL handshake with the client. So, one should really care about what signers to trust; that is, the content of the truststore being used. The IBM-provided default truststore includes several well known and widely trusted Certificate Authorites (CA). One good practice here is to periodically browse through your truststore and make your own judgment about the trustworthiness of its content -- and possibly remove some CAs.
Now that we know what a truststore is and what a keystore is, let's look at how they are defined in the WebSphere Application Server environment using the IBM JSSE.
To open JSSE connections, you must have appropriate certificates and private keys in a truststore and keystore. You will need to configure the JVM to use your truststore or keystore. However, if you are performing only server authentication (that is, the client validates the server's certificate, but not the reverse), you will need only a truststore.
When you create an SSL connection without specifying a truststore or a keystore explicitly, the JSSE runtime will use the "default" stores. Ordinarily in Java, this means the cacerts file is used as a default truststore (it is located in java/jre/lib/security under the WAS install directory).
However, if WebSphere Application Server security is enabled with WebSphere Application Server Network Deployment (ND), WebSphere Application Server will actually override the default JVM keystore and truststore settings by setting the JVM system properties javax.net.ssl.keystore and javax.net.ssl.truststore to point to the WebSphere Application Server server-level truststore and keystore files (which are
DummyServerKeyFile.jks by default). (See the IBM Support tech note in Resources for more information.) However, if global security is enabled, your code will not be using the JVM default keystore and truststore.
To make this behavior consistent, if you are not using WebSphere Application Server global security on WebSphere Application Server ND, set the JVM system properties (on the application server) for javax.net.ssl.keystore and javax.net.ssl.truststore to point to the WebSphere Application Server server-level truststore and keystore files. Otherwise, you will be confused later when you enable security. To configure the JVM System properties for truststores and keystores using the WebSphere Application Server admin console:
- From the admin console, navigate to Servers => Application Servers => server_name => Custom Properties => New.
- Enter the Name of the property, which is:
- Enter the value of the property, which is the fully qualified file name for your truststore.
- Add the description if you wish.
- Similarly, define an accompanying property that maintains the password needed to access your truststore by navigating to Servers => Application Servers => server_name => Custom Properties => New.
- The property name is:
javax.net.ssl.trustStorePasswordwith the value being the password. (Note here that the password remains in the cleartext.)
- The keystore can be defined in the same way using the property
javax.net.ssl.keyStoreand its corresponding
If the site you are contacting uses a certificate issued by a well known CA, it is likely that the IBM provided default certificates contain the information you need already. You can determine what certificates are included with WebSphere Application Server by using the iKeyman tool provided by IBM (automatically available for use after WebSphere Application Server is installed) to open the truststore file.
If the site you are accessing does not use a certificate from a CA that is included with WebSphere Application Server, you will need to obtain their signing certificate and add it to the truststore.
For most situations, this configuration is sufficient. In rare cases, you may need more than just a single default truststore/keystore. Later, you will see how to specify your truststores and keystores to the JSSE runtime programmatically, and how to use multiple truststores and keystores within your application.
You have seen that you can either rely on the default cacerts file, or you can define your own truststore. It may interest you to know how the JVM searches for truststores.
When a truststore is needed, it is searched for in the following order until a match is found:
- If a truststore is defined programmatically through the Java API (discussed above), then it is the one that is used as the truststore.
- If the javax.net.ssl.trustStore system property is defined, then the value of this property is used as the truststore's location.
- If file lib\security\jssecacerts is defined off the java\jre directory, then the jssecacerts file is used as the truststore. (WebSphere Application Server does not ship with jssecacerts. We mention it for completeness only, so you may ignore this case.)
- If file lib\security\cacerts is defined off the java\jre directory, then the cacerts file is used as the truststore.
If a particular trust store is found, then the search stops and the found file becomes your truststore. For instance, if one specifies a truststore by using its Java System property, if a valid signer is not found in the truststore, search will not continue on afterward.
WebSphere Application Server ships with a utility program called iKeyman which is automatically available for use after the WebSphere Application Server is installed. It is located in the bin directory underneath your WAS Install Root. (Refer to iKeyman documentation shipped with WAS for detailed information on how to use this utility, and Resources.)
In a nutshell, iKeyman manages keystores. Keystores come in one of several formats and iKeyman supports the most common formats, including the Java Key Store, known as JKS, which is the "normal" format used with Java applications (in fact, the cacerts truststore is in that format).
Next, we will see a simple example of using iKeyman to create a self-signed certificate. This is appropriate if the party you are communicating with will use the same certificate. If they already have their certificate, you will need to import the existing signing certificate, as mentioned earlier.
To start this process, execute the iKeyman tool by running
ikeyman.bat (on Windows platforms). The tool displays a graphical interface that looks like Figure 2.
Figure 2. iKeyman GUI
To generate a keystore that contains, for example, a self-signed certificate:
- Select Key Database File => New.
- Make sure that Key database type is JKS for Java Key Store.
- Enter the File Name and its Location (give
.jksto your file), then select OK.
- Fill in the password value (no major password checking rules apply here), then OK.
- Click on Create, then select New-Self-Signed Certificate.
- Enter the desired certificate information, then OK.
- At this point, your keystore contains your private key as well as your public key certificate. Highlight your keystore file name, then select View/Edit. You should see something like Figure 3.
Figure 3. Key information
You can use iKeyman to create a new self-signed certificate as we've just shown, or add a CA certificate to your own truststore. While we won't cover it here, if you wish to use iKeyman to add a new certificate to a keystore, remember that iKeyman can import certificates from a file in either binary DER format or in base64 ARM format. (DER stands for Distinguished Encoding Rules, a standard for data encoding, and ARM is the ASCII-armored-64 format.)
Next, we will show a simple example of how to use JSSE to connect over HTTPS to a remote server. In this example, we'll assume that the already existing truststore contains the needed certificate information. The steps below summarize the actions required to make it possible for WebSphere Application Server server side code to act as a HTTPS client to another remote server.
Through JVM configuration, add com.ibm.net.ssl.internal.www.protocol as the Java protocol handler. By default, no handler is set, so this step is required. It is best to define this as part of your JVM properties instead of hardcoding it in your application. Follow the same instructions for defining the keystore/truststore properties and add this property:
java.protocol.handler.pkgs, with this value:
com.ibm.net.ssl.internal.www.protocol. This property indicates that we are using the IBM HTTPS package for the JSSE framework.
Use the JVM custom properties to define your truststore and keystore as described earlier. Since by default the SSL protocol authenticates the server to the client, you may only need a truststore defined. Authenticating the client to the server is optional and is controlled from the server side settings (see Resources). The client application, however, needs to specify its keystore, as we have already described.
A client running within the WebSphere Application Server environment may choose to use the WebSphere Application Server server keystore. We do not recommend this, however, because in so doing, a client masquerades under the identity of the application server. If you are concerned about application code illegally accessing the WebSphere Application Server keystore, you can enable Java 2 security. Once enabled, WebSphere Application Server will not allow applications to access the keystore (or in fact any file) without explicitly being granted that permission.
The JSSE programming APIs for use with HTTPS are remarkably simple. Essentially, JSSE is used implicitly from code that opens up URLs that happen to point to HTTPS. The following trivial lines demonstrate creating a URL to an HTTPS endpoint and opening an HTTPS connection. Be sure to handle any applicable exceptions properly (refer to the Java networking programming reference).
String stringURL = "https://targetHost:9443/MyWebApps/servlet/myServlet"; URL url = new URL(stringURL); HttpsURLConnection urlc =(HttpsURLConnection)url.openConnection();
You can now follow the normal URL programming model for coding the rest of your application. For example, you can now begin reading from this URL connection.
These simple lines hide a lot of complexity. When the URL connection is used for the first time, the SSL handshake will automatically occur, authenticating the server to the client by way of the client receiving the server certificate and verifying it against the truststore. By the same token, client authentication may take place if an appropriate keystore is available and the server requests it. All this is done transparently for you by the JSSE runtime. Your exchange with the target server is now protected by the underlying SSL channel.
One important point to make about using SSL in this scenario is that the client is not verifying that the server it is talking to is in fact the intended party. Certificate authentication in this scenario really means "is the server providing a valid certificate?" The JSSE runtime will ensure that, but it doesn't guarantee that the server is the right server. Web browsers work around this issue by checking the server's certificate after validation to see if the Subject name in the certificate is the same as the DNS host name being accessed. For maximum security (if you are concerned about this very subtle -- and difficult to exploit -- security hole), you should perform validation similar to the Web browser, and determine the server's certificate using HttpsURLConnection.getServerCertificates().
If you are interested in accessing a Web services endpoint as a client using HTTPS, you can do something very similar to the previous example. This second example uses a Web services client proxy generated by IBM tooling to open an HTTPS connection. As in the previous example, we assume that the steps indicated earlier in this paper have already been followed.
String string URL = "https://targetHost:9443/MyWebApps/services/MyService"; ServiceProxy proxy = new ServiceProxy(); proxy.setEndPoint(stringURL); // proceed to invoke the proxy methods based on your application // ServiceProxy should be replaced by the name of your own application proxy ...
We have shown you how to open HTTPS connections that implicitly use JSSE. For many situations, that's all you need. Sometimes, though, it is necessary to code directly to the JSSE APIs when you are accessing something that uses SSL, but not HTTPS. Next, we show a little code sample that should show you the basics. As before, we assume you have configured the truststore and keystore environment properties.
Essentially, we get an SSL socket factory (default should be okay), create an SSL socket out of it, and begin reading writing to it:
SSLSocketFactory sslFactory=(SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket s=(SSLSocket)sslFactory.createSocket(targetHost,port); //substitute your targetHost and port // example OutputStream out=s.getOutputStream(); InputStream in=s.getInputStream();
A call to SSLSocket.startHandshake() is not needed unless you want to reset the parameters of the handshake.
Up to now, we have assumed in our examples that there is one truststore and keystore that is being used throughout the JVM. A client that is interacting with multiple services at the same time may find the need to use multiple keystores or truststores in order to establish SSL connections with each individual target application. The JSSE programming model enables the use of multiple such credential stores by way of maintaining a separate SSL context per individual target interaction. Each such context is encapsulated by a new SSLContext object. The following programming sequence shows how to define multiple keystores/truststore:
- Load your desired keystore into the runtime:
KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("yourKeyStore"), "yourPassword".toCharArray());
- Instantiate and then initialize a KeyManagerFactory object to encapsulate the underlying keystore:
KeyManagerFactory kmf = KeyManagerFactory.getInstance("IbmX509"); kmf.init(ks, "yourPassword".toCharArray());
- Load your desired truststore into the runtime (instantiated as a KeyStore object):
KeyStore ts = KeyStore.getInstance("JKS"); ts.load(new FileInputStream("yourTrustStore"), "yourPassword".toCharArray());
- Instantiate a TrustManagerFactory object and initialize it with your truststore:
TrustManager tm; TrustManagerFactory tmf = TrustManagerFactory.getInstance("IbmX509"); tmf.init(ts); tm = tmf.getTrustManagers();
- Get an instance of the SSLContext class:
sslContext = SSLContext.getInstance("SSL");
- Initialize this instance using the current trust manager (encapsulating the trusted CAs) and the key manager (encapsulating the client's credential):
sslContext.init(kmf.getKeyManagers(), tm, null);
- Use the current SSLContext object to get a SSL Socket Factory and instantiate an SSL socket out of it, as described in the previous section:
SSLSocketFactory factory = sslContext.getSocketFactory();
The main point here is the fact that the SSLContext class is not a singleton; that is, each getInstance call returns a different SSLContext object.
As we have seen, trust and key management for the HTTPS URL implementation is environment specific. This makes it easy to use SSL by simply configuring or specifying a few JVM properties. However, there is a disadvantage to this approach in that the specified properties will dictate the use of a single truststore/keystore. To overcome this situation, we use the JSSE interfaces to define an SSLContext for each separate HTTPS connection. The following code shows how you set up a custom keystore/trustore for your URL connection:
KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("yourKeyStore"), "yourPassword".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("IbmX509"); kmf.init(ks, " yourPassword ".toCharArray()); TrustManager tm; TrustManagerFactory tmf = TrustManagerFactory.getInstance("IbmX509"); tmf.init(ks); tm = tmf.getTrustManagers(); KeyStore ts = KeyStore.getInstance("JKS"); ts.load(new FileInputStream("yourTrustStore"), "yourPassword".toCharArray()); TrustManager tm; TrustManagerFactory tmf = TrustManagerFactory.getInstance("IbmX509"); tmf.init(ts); tm = tmf.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(kmf.getKeyManagers(), tm, null); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); URL url = new URL(stringURL); urlc = (HttpsURLConnection) url.openConnection(); urlc.setSSLSocketFactory(sslSocketFactory); // at this point communication with this target url // is set to use SSL with your custom truststore and keystore
While we won't show it here, if you wish to perform something similar with Web services clients, you will have to specify the keyrings to use via the Web services client deployment descriptors.
WebSphere Application Server fully supports JSSE as an SPI provider for the framework, and as a set of user APIs. As such, JSSE is readily avaialble for applications that need to establish SSL/TLS connections within the WebSphere Application Server environment. In this article, we have examined and presented recommended ways for using JSSE with an emphasis on how to maintain application portability by avoiding the hardcoding of JSSE implementation-specific constructs. Remember, of course, that the trust foundations for SSL/TLS connections rest foremost on the reliability and security of the public key certificates and their corresponding private keys. We also described how to use custom truststores and keystores in your application, as well as how to use multiple such constructs to establish connections with different entities.
- Introduction to the Public Key Infrastructure for the Internet, Messaoud Benantar, Prentice Hall, ISBN 0-13-060927-7, 2001
- SSL and TLS, Designing and Building Secure Systems, Eric Rescorla
Messaoud Benantar is a senior software engineer with IBM Software Services for WebSphere. He spent most of his career as a software developer working in IBM Large Systems Division, IBM Software Group/Tivoli, and IBM Global Services. His focus is on middleware and computing security.
Keys Botzum is a senior consultant with IBM Software Services for WebSphere. He has over 10 years of experience in large scale distributed system design, and additionally specializes in security. Mr. Botzum has worked with a variety of distributed technologies, including Sun RPC, DCE, CORBA, AFS, and DFS. Recently, he has been focusing on J2EE and related technologies. He holds a Masters degree in Computer Science from Stanford University, and a B.S. in Applied Mathematics/Computer Science from Carnegie Mellon University. Mr. Botzum has published numerous papers on WebSphere and WebSphere security. Additional articles and presentations by Keys Botzum can be found at http://www.keysbotzum.com, as well as on IBM developerWorks WebSphere. He is also an author of IBM WebSphere: Deployment and Advanced Configuration.