Java API for SSL

Java API for SSL or JSSE was available as a separate download prior to J2SE v1.4 but is now part of the standard platform. With this API, you can write SSL-enabled client and server programs. As the structure of these programs is similar to those of plain TCP-based programs and the SSL API is closely related to the underlying socket-based networking API, let us first go over a brief overview of TCP-based client-server programs and the Java socket API.

A typical TCP-based networking program plays the role of either a client, the one who initiates the connection, or a server, the one who accepts connection requests, or both, acting as a server for some interactions and a client for others. As the interaction style is different for client and server, it is convenient to talk about the actions of a particular role. In a server role, the program opens a server socket on a particular TCP port, identified by a number between 0 and 65536, and listens for incoming connection requests. In a client role, the program attempts to establish a TCP connection by specifying the machine name or IP address and the port number of the destination server socket, and optionally, the local TCP port number. On a successful connection request processing, a new socket, bound to an unused TCP port, is created for the server program. The client also gets a socket, bound to either a specified port or an unused port chosen by the socket library.

Once the connection has been established and both client and server programs have their sockets, the connection essentially behaves like a two-way pipe. Data written by one end is available to the other end for reading and vice-versa. At the server program, the same thread that accepted the connection may engage in data exchange (an iterative server) or spawn a separate thread for data exchange (a concurrent server), allowing the main thread to listen for more connections. The exchange pattern and the syntax and semantics of the data are usually governed by a higher, application-level protocol.

Now, let us talk about using the Java APIs to perform these functions. These API classes and interfaces reside in the packages java.net and javax.net, and support many different sequences of class instantiations and method invocations to establish a connection. We limit our discussion to one such specific sequence of steps. These steps use the factory classes ServerSocketFactory and SocketFactory of the package javax.net. Concrete instances of default factory classes are obtained by calling the static method getDefault(). A server program calls the method createServerSocket() on ServerSocketFactory to create a ServerSocket bound to a specific port and waits for incoming connection requests by calling accept() on a ServerSocket instance. On getting a request and successfully establishing the connection, accept() returns a java.net.Socket object ready for read and write. A client program initiates a connection by calling SocketFactory.createSocket() passing the machine name or IP address and the port number of the destination socket. Successful execution of this call returns a java.net.Socket object. A Socket has two I/O streams associated with it: an InputStream for reading and an OutputStream for writing. You can get these by calling methods getInputStream() and getOutputStream(), respectively.

You must have noticed the underlying framework in these API classes. The framework essentially supports the notion of server sockets to accept connections and normal sockets for data exchange. The default sockets support TCP communication, but there can be sockets for other kinds of communication and appropriate factories create these sockets, presenting a uniform and consistent interface to programmers. Java API for SSL fits nicely into this framework with derived factory classes SSLServerSocketFactory and SSLSocketFactory and socket classes SSLServerSocket and SSLSocket, all under the javax.net.ssl package.

Enough theory. Let us now look at the source code of a simple client-server program that uses SSL to communicate. The source files for this program can be found under srcjsbookch6ex1 subdirectory of JSTK installation. Let us begin with the server program EchoServer.java. This program creates a SSL server socket on port 2950, waiting to accept connection requests on this port. Once a connection is established, it reads incoming data from the socket and echoes back the same data to the same socket.

Listing 6-1. Program to accept SSL connections and read/write data-bytes
// File: srcjsbookch6ex1EchoServer.java
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLServerSocket;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {
  public static void main(String[] args) throws Exception {
    ServerSocketFactory ssf = SSLServerSocketFactory.getDefault();
    ServerSocket ss = ssf.createServerSocket(2950);

    // Placeholder for additional code.

    while (true){
      System.out.print("Waiting for connection... ");
      System.out.flush();
      Socket socket = ss.accept();
      System.out.println(" ... connection accepted.");
      SocketUtil.printSocketInfo(socket, " <-- ");

      java.io.InputStream is = socket.getInputStream();
      java.io.OutputStream os = socket.getOutputStream();
      int nread = 0;
      byte[] buf = new byte[1024];

      while ((nread = is.read(buf)) != -1){
        System.out.println("Read " + nread + " bytes.");
        os.write(buf, 0, nread);
        System.out.println("Wrote " + nread + " bytes.");
      } // inner while
    } // while (true)
  } // main()
}

Except for the static method SocketUtil.printSocketInfo(), which prints information about the socket passed as an argument, this code is fairly straightforward and hardly needs any explanation. Also, notice the comment indicating a placeholder for additional code. We get back to both these points in a short while.

The corresponding client side program, EchoClient.java, is given below. After establishing an SSL connection to the server, it prompts the user for a message, sends the message to the server, reads back the response, and displays it on the screen.

Listing 6-2. Program to initiate SSL connections and write/read data-bytes
// File: srcjsbookch6ex1EchoClient.java
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoClient {
  public static void main(String[] args) throws Exception {
    String hostname = "localhost";
    if (args.length > 0)
      hostname = args[0];
    SocketFactory sf = SSLSocketFactory.getDefault();
    Socket socket = sf.createSocket(hostname, 2950);
    System.out.println("Connection established.");
    SocketUtil.printSocketInfo(socket, " --> ");

    java.io.InputStream is = socket.getInputStream();
    java.io.OutputStream os = socket.getOutputStream();
    byte[] buf = new byte[1024];
    java.io.BufferedReader br = new java.io.BufferedReader(
        new java.io.InputStreamReader(System.in));

    while (true){
      System.out.print("Enter Message (Type "quit" to exit): ");
      System.out.flush();
      String inp = br.readLine();
      if (inp.equalsIgnoreCase("quit"))
        break;
      os.write(inp.getBytes());
      int n = is.read(buf);
      System.out.println("Server Returned: " + new String(buf, 0, n));
    }
    socket.close();
    System.out.println("Connection closed.");
  }
}

Those of you familiar with socket programming will notice that this is indeed quite similar to the plain TCP-based socket programs. But wait! What about the certificates for authentication and verification? Where do they come from? Well, by default the Java library examines a number of java system properties to get these values:

  • javax.net.ssl.keyStore: The keystore file having the private key and the corresponding certificate or certificate chain required for authentication. It needs to be set at the server program. It is also required for the client program if client authentication is enabled. Note that the EchoServer code presented above does not enable client authentication.

  • javax.net.ssl.keyStoreType: The type of the keystore specified by system property javax.net.ssl.keyStore. Possible values are: JKS, JCEKS and PKCS12. Default value is JKS.

  • javax.net.ssl.keyStorePassword: The password of the keystore specified by the system property javax.net.ssl.keyStore. This is required, as the server needs to load the private key. Recall that a keystore allows each entry to be password protected, potentially with different passwords. However, the default behavior relies on the fact that a random entry in the keystore is picked and this entry has the same password as that of the keystore.

  • javax.net.ssl.trustStore: The truststore file, which is of the same format as a keystore file, having certificate entries for trusted subjects and issuers. This is required at the client program for verifying the certificate presented by the server. It is also required at the server to verify the client's certificate if client authentication is enabled. By default, keystore jssecacerts, if present in the jre-homelibsecurity directory, is taken as the truststore. This keystore doesn't ship with J2SE v1.4 SDK. In its absence, the cacerts file of the same directory is used.

  • javax.net.ssl.trustStoreType: The type of the truststore specified by system property javax.net.ssl.trustStore.

  • javax.net.ssl.trustStorePassword: The password of the truststore specified through system property javax.net.ssl.trustStore. Use to check the integrity of the truststore, if specified. This password may be omitted.

You can pass these properties to the JVM at the command line using syntax "java –Dprop=value classname" or programmatically within the code by invoking method System.setProperty(prop, value). We describe this mechanism more concretely in a subsequent section.

The above description and code fragments may give the impression that SSL communication with Java is essentially the same as TCP communication, at least for a programmer. Reality is more complex:

  • The above code fragments rely on JVM-wide system properties for specifying the certificate, private key and the set of trusted issuers. As we see later, this mechanism has its limitations and is not adequate for many scenarios.

  • By default, the server doesn't negotiate or insist on client authentication. To check with the client if it can furnish a certificate or to insist on a certificate, separate calls, setWant-ClientAuthentication(true) or setNeedClientAuthentication-(true), are needed on a SSLServerSocket instance, taking us away from using the base ServerSocket class. Further examination of the SSL-specific properties associated with an SSLSocket class would take us even further away from the generic model.

  • Java New I/O library, under the package java.nio, with support for asynchronous I/O and direct buffers to minimize in-memory copy, supports TCP sockets, but not SSL sockets.

Let us augment the EchoServer.java file so that it can negotiate or insist on client authentication based on a command line argument. To do so, we would replace the placeholder comment in the Listing 7-1 with the following code fragment:

if (args.length > 0){
  SSLServerSocket sss = (SSLServerSocket)ss;
  if ("-needClientAuth".equalsIgnoreCase(args[0])){
    sss.setNeedClientAuth(true);
  } else if ("-wantClientAuth".equalsIgnoreCase(args[0])){
    sss.setWantClientAuth(true);
  }
}

You can see that this requires casting the ServerSocket object into SSLServerSocket. Retrieving SSL-specific connection information from the socket also requires us to work with an SSLSocket object, as we can see from the code in SocketUtil.java, shown in Listing 6-3.

Listing 6-3. Displaying SSL socket information
// File: srcjsbookch6ex1SocketUtil.java
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.net.Socket;
import java.net.InetSocketAddress;

public class SocketUtil {
  public static void printSocketInfo(Socket socket, String dir) {
    try {
      InetSocketAddress localAddr, remoteAddr;
      LocalAddr = (InetSocketAddress)socket.getLocalSocketAddress();
      remoteAddr = (InetSocketAddress)socket.getRemoteSocketAddress();

      System.out.println("  Connection   : " +
          localAddr.getHostName() + ":" + localAddr.getPort() + dir +
          remoteAddr.getHostName() + ":" + remoteAddr.getPort());

      SSLSession sess = ((SSLSocket)socket).getSession();
      System.out.println("  Protocol     : " + sess.getProtocol());
      System.out.println("  Cipher Suite : " + sess.getCipherSuite());
      Certificate[] localCerts = sess.getLocalCertificates();
      if (localCerts != null && localCerts.length > 0)
        printCertDNs(localCerts, "  Local Cert");

      Certificate[] remoteCerts = null;
      try {
        remoteCerts = sess.getPeerCertificates();
        printCertDNs(remoteCerts, "  Remote Cert");
      } catch (SSLPeerUnverifiedException exc){
        System.out.println("  Remote Certs: Unverified");
      }
    } catch (Exception exc){
      System.err.println("Could not print Socket Information: " + exc);
    }
  }

  private static void printCertDNs(Certificate[] certs, String label){
    for (int i = 0; i < certs.length; i++){
      System.out.println(label + "[" + i + "]: " +
          ((X509Certificate)certs[i]).getSubjectDN());
    }
  }
}

The above listing illustrates how to access SSL-specific information, such as protocol, cipher suite, local and remote certificates from an active SSLSocket object. As we discover in the next section, the information displayed by the method printSocketInfo() is helpful to us in analyzing the behavior of the EchoServer and EchoClient programs.

Running Programs EchoServer and EchoClient

In this section, we go through the steps to compile and run the EchoServer and EchoClient programs. For simplicity, we run them on the same machine. However, you can certainly try them on two separate machines. The purpose is to get familiar with the operational issues and set the stage for more advanced development and experimentation.

Compiling the programs is simple. Just go to the source directory and issue this command:

C:ch6ex1>javac *.java
						

To run the programs, we need to set up appropriate keystore and truststore files. For this, we try a number of configurations:

  1. Server authentication with a self-signed certificate. EchoClient trusts EchoServer's certificate.

  2. Mutual authentication with self-signed certificates. EchoClient trusts EchoServer's certificate and EchoServer trusts EchoClient's certificate.

  3. EchoServer authentication with a CA signed certificate. EchoClient trusts CA's certificate.

  4. Mutual authentication with CA signed certificates. Both EchoClient and EchoServer trust CA's certificate.

These configurations are illustrated in Figure 6-1.

Figure 6-1. Configurations for running EchoServer and EchoClient.


With respect to each of these configurations, we are interested in two activities: setting up keystore and truststore files, and specifying system properties to run the server and the client. The following subsections explain the commands used to carry out these activities. The script files for these commands can be found in the same directory as the source files for EchoServer and EchoClient programs.

Server Authentication with Self-Signed Certificate

For this configuration, let us first create server keystore server.ks with a private key and self-signed certificate; export the certificate to a temporary file temp$.cer; and then import it from the temporary file to the client's truststore client.ts. Execution of these commands is shown below.

C:ch6ex1>keytool -genkey -storepass changeit -storetype JCEKS 
							-keypass changeit -keystore server.ks -keyalg RSA 
							–dname  "CN=Server, OU=X, O=Y, L=Z, S=XY, C=YZ"

C:ch6ex1>keytool -export -file temp$.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore server.ks
Certificate stored in file <temp$.cer>

C:ch6ex1>keytool -import -file temp$.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore client.ts -noprompt
Certificate was added to keystore

We are now ready to run the programs. Let us first run EchoServer, specifying the keystore information through system properties and let it wait for a connection.

C:ch6ex1>java -Djavax.net.ssl.keyStore=server.ks 
							-Djavax.net.ssl.keyStoreType=JCEKS 
							-Djavax.net.ssl.keyStorePassword=changeit EchoServer
Waiting for connection...

Now let us run the client program EchoClient, specifying the truststore information through system properties, in a different command window.

C:ch6ex1>java -Djavax.net.ssl.trustStore=client.ts 
							-Djavax.net.ssl.trustStoreType=JCEKS EchoClient
Connection established.
  Connection   : 127.0.0.1:1296 --> localhost:2950
  Protocol     : TLSv1
  Cipher Suite : SSL_RSA_WITH_RC4_128_MD5
  Remote Certs: [0]CN=Server, OU=X, O=Y, L=Z, ST=XY, C=YZ
Enter Message (Type "quit" to exit): Hello, World!
Server Returned: Hello, World!
Enter Message (Type "quit" to exit): quit
Connection closed.

The server program displays the following output:

Waiting for connection...  ... connection accepted.
  Connection   : 127.0.0.1:2950 <-- 127.0.0.1:1296
  Protocol     : TLSv1
  Cipher Suite : SSL_RSA_WITH_RC4_128_MD5
  Local Certs : [0]CN=Server, OU=X, O=Y, L=Z, ST=XY, C=YZ
  Remote Certs: Unverified
Read 13 bytes.
Wrote 13 bytes.
Waiting for connection...

Note that the client program gets a remote certificate, the one supplied by the server, but the server program gets no certificate. This is expected, as the server has not asked for client authentication. Also, the cipher suite selected for the communication is SSL_RSA_WITH_RC4_128_MD5, the strongest with RSA encryption among all enabled cipher suites.

As an exercise, run these programs with a DSA certificate and see what cipher suite is used.

Mutual Authentication with Self-Signed Certificate

This configuration requires a separate keystore, client.ks, for the client program and a separate truststore, server.ts, for the server program in addition to what we had in the previous section. Let us create these keystore and truststore files with commands similar to the ones we used in the previous section:

C:ch6ex1>keytool -genkey -storepass changeit -storetype JCEKS 
							-keypass changeit -keystore client.ks -keyalg RSA 
							-dname "CN=Client, OU=X, O=Y, L=Z, S=XY, C=YZ"

C:ch6ex1>keytool -export -file temp$.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore client.ks
Certificate stored in file <temp$.cer>

C:ch6ex1>keytool -import -file temp$.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore server.ts -noprompt
Certificate was added to keystore

For running the server for mutual authentication, we need to specify not only the keystore information but also the truststore information, so that the client certificate can be verified, and the command line flag to either require or negotiate client certificate. The following command, by specifying the command line option "-wantClientAuth", requires client authentication:

C:ch6ex1>java -Djavax.net.ssl.keyStore=server.ks 
							-Djavax.net.ssl.keyStoreType=JCEKS 
							-Djavax.net.ssl.keyStorePassword=changeit 
							-Djavax.net.ssl.trustStore=server.ts 
							-Djavax.net.ssl.trustStoreType=JCEKS EchoServer -wantClientAuth
Waiting for connection...

The command to run the client now needs to specify the keystore information as well, in addition to truststore information:

C:ch6ex1>java -Djavax.net.ssl.trustStore=client.ts 
							-Djavax.net.ssl.trustStoreType=JCEKS 
							-Djavax.net.ssl.keyStore=client.ks 
							-Djavax.net.ssl.keyStoreType=JCEKS 
							-Djavax.net.ssl.keyStorePassword=changeit EchoClient
Connection established.
  Connection   : 127.0.0.1:1297 --> localhost:2950
  Protocol     : TLSv1
  Cipher Suite : SSL_RSA_WITH_RC4_128_MD5
  Local Certs : [0]CN=Client, OU=X, O=Y, L=Z, ST=XY, C=YZ
  Remote Certs: [0]CN=Server, OU=X, O=Y, L=Z, ST=XY, C=YZ
Enter Message (Type "quit" to exit): Hello, Friend!
Server Returned: Hello, Friend!
Enter Message (Type "quit" to exit): quit
Connection closed.

Notice that it now shows not only the remote certificate but also the local certificate.

Try running the client program without specifying the keystore and see what happens. Also, try the scenario when the EchoServer specifies "-needClientAuth" and the EchoClient doesn't set keystore information.

Server Authentication with CA Signed Certificate

This configuration requires either getting a CA signed certificate from an established CA or setting up a CA of our own. We use our own JSTK utility certtool, explained in Chapter 4, PKI with Java, to set up a simple CA. To perform this setup, issue this command:

C:ch6ex1>%JSTK_HOME%incerttool setupca -password changeit
CA setup successful: cadir

For the above command to work, the environment variable JSTK_HOME must be set to the home directory of the JSTK software.

The next steps are: create server keystore server.ks with a private key and self-signed certificate, generate a Certificate Signing Request, issue a CA signed certificate based on the CSR, and then import the signed certificate to the keystore server.ks. We also need to import the CA certificate in the client's trust store client.ts. The execution of these commands is presented below.

C:ch6ex1>keytool -genkey  -storepass changeit -storetype JCEKS 
							-keypass changeit -keystore server.ks 
							-dname "CN=Server, OU=X, O=Y, L=Z, S=XY, C=YZ"

C:ch6ex1>keytool -certreq -file temp$.csr -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore server.ks

C:ch6ex1>%JSTK_HOME%incerttool issue -csrfile temp$.csr 
							-cerfile server.cer -password changeit
Issued Certificate written to file: server.cer

C:ch6ex1>keytool -import -file server.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore server.ks -noprompt
Certificate reply was installed in keystore

C:ch6ex1>keytool -import -file temp$.cer -storepass changeit 
							-storetype JCEKS -keypass changeit -keystore client.ts -noprompt
Certificate was added to keystore

Note that these commands use the default key algorithm, i.e., DSA, for key generation.

The steps to run the EchoServer and EchoClient programs are the same as in the subsection Server Authentication with Self-Signed Certificate, and hence are skipped.

Mutual Authentication with CA Signed Certificate

Similar to mutual authentication with a self-signed certificate, this configuration needs a client keystore, client.ks, with a valid CA signed client certificate and a server truststore, server.ts, with CA's certificate in it. The commands to accomplish this are similar to those presented in the previous section Server Authentication with CA Signed Certificate, and are skipped.

Execute the EchoServer and EchoClient programs by issuing the same commands as the ones presented in the subsection Mutual Authentication with Self-Signed Certificate.

General Notes on Running Java SSL Programs

If you omit the truststore system properties while running either the client or server, the default truststore of the J2SE installation gets used. Validation of the self-signed certificate will not succeed against this truststore. However, it is possible to import additional certificates to this truststore, thus avoiding the need to specify truststore information with every invocation.

In the previous command executions, we chose to create our own certificates using keytool and certtool utilities. In most environments, this would not be the case. You would be either using certificates obtained by an established CA or using a full-fledged CA software to generate certificates. This would change the specifics of the each step, but conceptually you would be carrying out the same steps.

There are many more possible combinations to run the client and server programs than we have explained here. JSTK utility ssltool lets you try many of these combinations by simply changing the command line parameters and observing the result. Sometimes, it is quite instructive to try out combinations that are likely to fail and observe the error messages.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.20.125