Build secure network applications with SSL and the JSSE API

Get started with SSL and JSSE using these two simple apps

The Internet is a dangerous place. It's simply too easy to snoop, spoof, and steal unprotected information as it travels over the wires. Last month, I wrote the final article in a series on X.509 certificates and public key infrastructure (PKI), the technologies that secure most e-commerce activity on the Internet. Near the end of the article, I suggested looking at the SSL (Secure Socket Layer) protocol to learn how X.509 certificates are used in practice. SSL is the X.509 killer app -- nearly every browser and most popular Web and application servers support it.

This month, I will explore SSL as implemented by the JSSE (Java Secure Socket Extension), and show you how to build secure network applications in Java using SSL and JSSE.

Let's begin with a simple demonstration. JSSE provides an SSL toolkit for Java applications. In addition to the necessary classes and interfaces, JSSE provides a handy command-line debugging switch that you can use to watch the SSL protocol in action. In addition to providing useful information for debugging a recalcitrant application, playing with the toolkit is a great way to get your feet wet with SSL and JSSE.

To run the demonstration, you must first compile the following class:

  public
  class Test
  {
    public
    static
    void
    main(String [] arstring)
    {
      try
      {
        new java.net.URL("https://" + arstring[0] + "/").getContent();
      }
      catch (Exception exception)
      {
        exception.printStackTrace();
      }
    }
  }

Next, you need to turn on SSL debugging and run the above application. The application connects to the secure Website that you specify on the command line using the SSL protocol via HTTPS. The first option loads the HTTPS protocol handler. The second option, the debug option, causes the program to print out its behavior. Here's the command (replace <host> with the name of a secure Web server):

  java -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl Test <host>

You need to install JSSE; refer to Resources if you're unsure how.

Now let's get down to business and talk about SSL and JSSE.

A brief look at SSL

The code in the introduction demonstrates the easiest way to add SSL to your applications -- via the java.net.URL class. This approach is useful, but is not flexible enough to let you create a secure application that uses generic sockets.

Before I show you how to add that flexibility, let's take a quick look at SSL's features.

As its name suggests, SSL aims to provide applications with a secure socketlike toolkit. Ideally, it should be easy to convert an application that uses regular sockets into an application that uses SSL.

SSL addresses three important security issues:

  1. It provides authentication, which helps ensure the legitimacy of the entities involved in a dialog.
  2. It provides privacy. SSL helps warrant that a third party cannot decipher the dialog between two entities.
  3. It maintains integrity. The use of a MAC (message authentication code), which is similar to a checksum, helps guarantee that a dialog between two entities is not modified by a third party.

SSL relies heavily on both public-key and secret-key cryptography. It uses secret-key cryptography to bulk-encrypt the data exchanged between two applications. SSL provides the ideal solution because secret-key algorithms are both secure and fast. Public-key cryptography, which is slower than secret-key cryptography, is a better choice for authentication and key exchange.

Sun's JSSE reference implementation comes with all the technology necessary to add SSL to your applications. It includes RSA (Rivest-Shamir-Adleman) cryptography support -- the de facto standard for security on the Internet. It includes an implementation of SSL 3.0 -- the current SSL standard -- and TLS (Transport Layer Security) 1.0, the next generation of SSL. JSSE also provides a suite of APIs for creating and using secure sockets.

The JSSE API

The Java security architecture uses the Factory design pattern heavily. For the uninitiated, the Factory design pattern uses special factory objects to construct instances, rather than calling their constructors directly. (See Resources for the pros and cons of the factory class.)

In JSSE, everything begins with the factory; there's a factory for SSL sockets and a factory for SSL server sockets. Since generic sockets and server sockets are already quite fundamental to Java network programming, I'll assume that you're familiar with the two and you understand their roles and differences. If you are not, I recommend picking up a good book on Java network programming.

SSLSocketFactory

Methods in the javax.net.ssl.SSLSocketFactory class fall into three categories. The first consists of a single static method that retrieves the default SSL socket factory: static SocketFactory getDefault().

The second category consists of four methods inherited from javax.net.SocketFactory that mirror the four key constructors found on the java.net.Socket class, and one method that wraps an existing socket with an SSL socket. They each return an SSL socket:

  1. Socket createSocket(String host, int port)
  2. Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
  3. Socket createSocket(InetAddress host, int port)
  4. Socket createSocket(InetAddress host, int port, InetAddress clientHost, int clientPort)
  5. Socket createSocket(Socket socket, String host, int port, boolean autoClose)

The two methods in the third category return the list of SSL cipher suites that are enabled by default, and the complete list of supported SSL cipher suites:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

A cipher suite is a combination of cryptographic algorithms that define a particular level of security for an SSL connection. A cipher suite defines whether the connection is encrypted, whether content integrity is verified, and how authentication occurs.

SSLServerSocketFactory

Methods on the javax.net.ssl.SSLServerSocketFactory class fall into the same three categories as SSLSocketFactory. First, there is the single static method that retrieves the default SSL server socket factory: static ServerSocketFactory getDefault().

The methods that return SSL server sockets mirror the constructors found in the java.net.ServerSocket class:

  1. ServerSocket createServerSocket(int port)
  2. ServerSocket createServerSocket(int port, int backlog)
  3. ServerSocket createServerSocket(int port, int backlog, InetAddress address)

Finally, the SSLServerSocketFactory features the two methods that return the list of ciphers enabled by default and the list of supported ciphers, respectively:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

So far, the API is pretty straightforward.

SSLSocket

Things get interesting in the javax.net.ssl.SSLSocket class. I assume you are already familiar with the methods provided by its parent, the Socket class, so I will concentrate on the methods that provide SSL-related functionality.

Like the two SSL factory classes, the first two methods listed below retrieve the enabled and supported SSL cipher suites, respectively. The third method sets the enabled cipher suites. An application can use the third operation to upgrade or downgrade the range of acceptable security that the application will allow:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods determine whether the socket can establish new SSL sessions, which maintain connection details -- like the shared secret key -- between connections:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The next two methods determine whether the socket will require client authentication. The methods only make sense when invoked on server mode sockets. Remember, according to the SSL specification, client authentication is optional. For example, most Web applications don't require it:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean need)

The methods below change the socket from client mode to server mode. This affects who initiates the SSL handshake and who authenticates first:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean mode)

Method void startHandshake() forces an SSL handshake. It's possible, but not common, to force a new handshake operation in an existing connection.

Method SSLSession getSession() retrieves the SSL session. You will seldom need to access the SSL session directly.

The two methods listed below add and remove an SSL handshake listener object. The handshake listener object is notified whenever an SSL handshake operation completes on the socket.

  1. void addHandshakeCompletedListener(HandshakeCompletedListener listener)
  2. void removeHandshakeCompletedListener(HandshakeCompletedListener listener)

SSLServerSocket

The javax.net.ssl.SSLServerSocket class is similar to the javax.net.ssl.SSLSocket class; it doesn't require much individual attention. In fact, the set of methods on javax.net.ssl.SSLServerSocket class is a subset of the methods on the javax.net.ssl.SSLSocket class.

The first two methods listed below retrieve the enabled and supported SSL cipher suites. The third method sets the enabled cipher suite:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods control whether or not the server socket can establish new SSL sessions:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The following methods determine whether the accepted sockets will require client authentication:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean flag)

The methods below change the accepted socket from client mode to server mode:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean flag)

A simple example

To make this toolkit tutorial clearer, I've included the source code for a simple server and a compatible client below. It's a secure variation on the typical echo application that many introductory networking texts provide.

The server, shown below, uses JSSE to create a secure server socket. It listens on the server socket for connections from secure clients. When running the server, you must specify the keystore to use. The keystore contains the server's certificate. I have created a simple keystore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
public
class EchoServer
{
  public
  static
  void
  main(String [] arstring)
  {
    try
    {
      SSLServerSocketFactory sslserversocketfactory =
        (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
      SSLServerSocket sslserversocket =
        (SSLServerSocket)sslserversocketfactory.createServerSocket(9999);
      SSLSocket sslsocket = (SSLSocket)sslserversocket.accept();
      InputStream inputstream = sslsocket.getInputStream();
      InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
      BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
      String string = null;
      while ((string = bufferedreader.readLine()) != null)
      {
        System.out.println(string);
        System.out.flush();
      }
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }
  }
}

Use the following command to start the server (foobar is both the name of the keystore file and its password):

  java -Djavax.net.ssl.keyStore=foobar -Djavax.net.ssl.keyStorePassword=foobar EchoServer

The client, shown below, uses JSSE to securely connect to the server. When running the client, you must specify the truststore to use, which contains the list of trusted certificates. I have created a simple truststore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public
class EchoClient
{
  public
  static
  void
  main(String [] arstring)
  {
    try
    {
      SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
      SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999);
      InputStream inputstream = System.in;
      InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
      BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
      OutputStream outputstream = sslsocket.getOutputStream();
      OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
      BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);
      String string = null;
      while ((string = bufferedreader.readLine()) != null)
      {
        bufferedwriter.write(string + '\n');
        bufferedwriter.flush();
      }
    }
    catch (Exception exception)
    {
      exception.printStackTrace();
    }
  }
}

Use the following command to start the client (foobar is both the name of the truststore file and its password):

  java -Djavax.net.ssl.trustStore=foobar -Djavax.net.ssl.trustStorePassword=foobar EchoClient
1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more