Java Secure Socket
Java Secure Socket
Table of Contents
If you're viewing this document online, you can click any of the topics below to link directly to that section.
1. About this tutorial....................................................... 2. The Java Cryptography Architecture................................ 3. Whiteboard: An example application................................ 4. Key management ...................................................... 5. Using JSSE sockets ................................................... 6. Wrapup and resources ................................................
2 4 7 11 15 20
Page 1 of 22
ibm.com/developerWorks
Prerequisites
To follow the discussion in this tutorial, you need to know a few things about the Java language, starting with the basics of Java programming in a client/server environment (that is, working with classes, objects, threads, and so on). Because stream and socket communication is central to our discussion, you need to know how to use streams and sockets. In particular, you should know what a stream is and what it is used for. You should know how to create a Socket and a ServerSocket, how to get streams from each, and how to communicate using those streams. You also should know how to create and compile a .java file using the JDK or an IDE. Two developerWorks tutorials, "Java sockets 101" and "Introduction to Java I/O," both accessible from Resources on page 20 , provide useful background information. You do not need to know anything about encryption technology to complete this tutorial. While a comprehensive overview of encryption is beyond the scope of the discussion here, you will be given the information you need to create secure client/server connections within your applications.
Installation requirements
To run the examples in this tutorial, you need the following tools and components: JDK 1.4 from Sun Microsystems A development environment -- either an IDE or a suitable command-shell A computer on which you can run an Internet server See Resources on page 20 to download the JDK 1.4 and complete source files necessary for the completion of this tutorial.
Page 2 of 22
ibm.com/developerWorks
Page 3 of 22
ibm.com/developerWorks
In this tutorial we'll mostly be working with the JSSE component of the JCA.
Public-key cryptography
Page 4 of 22
ibm.com/developerWorks
One problem with many cryptographic algorithms is that they require the distribution of shared keys. A password is a good example of a shared key. The problem with shared keys is that they must be shared between communicating entities before secure communication can start. The sharing process, however, can be vulnerable to eavesdropping, which leads to a chicken-and-egg problem: before we can exchange data securely, we must first exchange secret keys securely. This problem was solved in 1976 by Whitfield Diffie and Martin Hellman, with the creation of public key cryptography. In the Diffie-Hellman public-key system each communicating party holds a pair of keys -- one public and one private. The private key is known only to the communicating party, while the public key can be given to anyone. Data encrypted using one of the keys can only be decrypted with the other. Thus, if you want to create a message to be read only by a particular party, you use their public key to make the encryption, and they then use their private key to decrypt the message. Likewise, if you encrypt a message with your private key, then anyone who has a copy of your public key can use it to decrypt the message. This assures the person on the receiving end that the message came from you and not someone else, since only you have your private key. A message that you have encrypted in this way bears your digital signature.
ibm.com/developerWorks
Before data can be sent across an SSL connection, the two ends must negotiate and exchange key information. This is called the handshake protocol. We won't go into much detail about the handshake protocol here, because it isn't necessary for our purposes. For our purposes, you need to know that the handshake involves the following steps: 1. The server sends its certificate to the client and the client verifies the server certificate. 2. The client sends its certificate to the server and the server verifies the client certificate. 3. The client encrypts password information with the server's public key and sends it to the server. This password information is used by each end of the connection to generate identical secret keys, which will then be used to transmit data. Client authentication (Step 2) is optional: the server can request that the client provide its certificate, but it is not required to make such a request. We will be using client authentication in our example. Now that you have a basic understanding of the infrastructure of JCA and JSSE under your belt, we can move on to the more active part of the tutorial: working with a live example.
Page 6 of 22
ibm.com/developerWorks
In the following two figures, you see two whiteboard client windows. Imagine that each of these windows is running on a different machine. The first user has clicked somewhere in his window and typed a message, but he has not yet pressed Return.
Page 7 of 22
ibm.com/developerWorks
The first user is typing a message and has not yet pressed return.
Now, imagine the first user presses Return, which causes his message to show up in the window of the second user. The second user then sends a response, as illustrated in the two figures below. The first user has pressed return and sent a message, and then received a response from the second user.
The second user received the first user's mess and then sent a response.
Each user sees his own text in black; other users' text appears in different colors assigned by the server.
ibm.com/developerWorks
On the server side of the whiteboard application, we have a class called Server. This class listens for incoming connections on a specified port. Each time a connection comes in, the Server creates a ConnectionProcessor to process the connection. Processing a connection means receiving text messages and sending them back out to other clients. The Server gives each client one ConnectionProcessor. When a client first starts up, it initiates a connection to the server. The client keeps this connection open throughout the whiteboard session. Each text message is sent along this connection. The figure below illustrates the client/server processes for the whiteboard application.
Key files
As we already discussed, the whiteboard uses JSSE, and JSSE implements the SSL and TLS protocols. These protocols use public-key encryption to ensure the privacy of messages sent over the Internet. In a public-key encryption system, both clients and server must have a pair of keys, one public and one private. Before our whiteboard can even begin to mediate messages between users, we must generate these keys. Once the keys have been generated, we'll provide the client side a file containing its public and private keys. It will also have a copy of the server's public key certificate. Keys are stored in a specially formatted file called a keystore. The following table describes the keystore files we'll be using. Keystore file client.private server.public server.private What it contains The client's public/private key pair The server's public key certificate The server's public/private key pair Where it goes Client side Client side Server side
Page 9 of 22
ibm.com/developerWorks
client.public
Server side
Key protection
The server also has a file containing its own public and private keys, as well as the client's public key certificate. Recall that public keys can be given out freely -- there's no need to hide them from any other party. It is important that each end of the client/server connection has only the key files it needs to work properly. In particular, it's important that only the server has a copy of its own private key. In the wrong hands, this key could do much damage, since it would essentially allow a malicious entity to cloak itself under the server's identity. Now that you have an idea of how the whiteboard is put together, we can begin working more directly with each of its components. In the next section, you'll learn how to generate and manage public/private key pairs in a client/server system.
Page 10 of 22
ibm.com/developerWorks
When you run this command, you will be asked a series of questions. These questions concern you as an entity (your name, organization, and the like). The information you provide will be used to create a self-signed certificate that associates the information with a public key and testifies to the authenticity of the association. You will also be asked to enter passwords for the keystore and, optionally, passwords for the key pair you are creating.
keytool -genkey -alias clientprivate -keystore client.private -storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your Organizational Unit, O=Your Organization, L=Your City, S=Your State, C=Your Country" -storepass clientpw -keypass clientpw
Option -genkey -alias clientprivate -keystore client.private -storetype JKS -keyalg rsa -dname "CN=Your Name..."
What it means Tells keytool to generate a key pair. Identifies the new key pair within the keystore. Uses the file client.private as the keystore. Declares the type of the keystore. JKS is the default. Declares the algorithm to be used; we're using the RSA public key algorithm, which is the default. Provides information about the entity owning the key pair.
Page 11 of 22
ibm.com/developerWorks
Specifies the password for the entire keystore. Specifies the password for the new key pair.
keytool -genkey -alias clientprivate -keystore client.private -storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your Organizational Unit, O=Your Organization, L=Your City, S=Your State, C=Your Country" -storepass clientpw -keypass clientpw
And here is the command to generate the file server.private, which is the key pair for the server:
keytool -genkey -alias serverprivate -keystore server.private -storetype JKS -keyalg rsa -dname "CN=Your Name, OU=Your Organizational Unit, O=Your Organization, L=Your City, S=Your State, C=Your Country" -storepass serverpw -keypass serverpw
As mentioned in the panel Key files on page 9 , the private client and server key files need to be installed in specific places. The client.private file is installed on the client side; the server.private file is installed on the server side.
Export/import commands
Page 12 of 22 Using JSSE for secure socket communication
ibm.com/developerWorks
We'll use the keytool -export command to extract the public key into a file, and then use the keytool -import command to insert it into a new keystore. Here's the command to extract the client's public key:
keytool -export -alias clientprivate -keystore client.private -file temp.key -storepass clientpw
And here's the command to insert the client's private key into its own keystore:
keytool -import -noprompt -alias clientpublic -keystore client.public -file temp.key -storepass public
We'll also extract and store the server's public key. Here's the command to extract the key:
keytool -export -alias serverprivate -keystore server.private -file temp.key -storepass serverpw
keytool -import -noprompt -alias serverpublic -keystore server.public -file temp.key -storepass public
The script generatekeys.sh (for UNIX) and generatekeys.bat (for DOS or Microsoft Windows) automatically generates the client and server key files for you and cleans up any temporary files. See Resources on page 20 to download the script.
Page 13 of 22
ibm.com/developerWorks
With the public/private key pairs properly installed on our client and server systems, we're ready to begin using the whiteboard application to exchange messages. In the next section, we'll examine the code for the whiteboard application itself.
Page 14 of 22
ibm.com/developerWorks
Similarly, the following code demonstrates how we listen for incoming connections. This example creates a ServerSocket listening on port port, and then enters an infinite loop, accepting and processing incoming connections:
ServerSocket serverSocket = new ServerSocket( port ); while (true) { Socket socket = serverSocket.accept(); doSomethingWithNewConnection( socket ); }
Secure sockets work in a very similar manner, but before we can implement them for our example we must complete several steps. We'll go over these steps in the panels that follow.
Connection setup
To initiate a secure socket connection to a remote server, we must carry out the following steps: 1. Create a SecureRandom, a source of secure random numbers. Secure random numbers are numbers that are random enough that they will not make the encryption vulnerable to attack. 2. Create a KeyStore object containing the remote server's public key. This is read from server.public.
Page 15 of 22
ibm.com/developerWorks
3. Create a KeyStore object containing the client's public/private key pair, including its public key certificate. This is read from client.private. 4. Create a TrustManagerFactory from the remote server's KeyStore. This is used to authenticate the remote server. 5. Create a KeyManagerFactory from the client's KeyStore. This is used for encrypting and decrypting data. 6. Create an SSLContext object, using the KeyManagerFactory, the TrustManagerFactory, and the SecureRandom. 7. Use the SSLContext to create an SSLSocketFactory. 8. Use the SSLSocketFactory to create an SSLSocket, which acts just like a regular Socket, except that it is secure.
Listening setup
To listen for incoming connections, we must carry out a similar set of steps: 1. Create a SecureRandom, a source of secure random numbers. 2. Create a KeyStore object containing the remote client's public key. This is read from client.public. 3. Create a KeyStore object containing the server's public/private key pair, including its public key certificate. This is read from server.private. 4. Create a TrustManagerFactory from the remote client's KeyStore. This is used to authenticate the remote client. 5. Create a KeyManagerFactory from the server's KeyStore. This is used for encrypting and decrypting data. 6. Create an SSLContext object, using the KeyManagerFactory, the TrustManagerFactory, and the SecureRandom. 7. Use the SSLContext to create an SSLServerSocketFactory. 8. Use the SSLServerSocketFactory to create an SSLServerSocket, which acts just like
Page 16 of 22
ibm.com/developerWorks
a regular ServerSocket, except that it is secure. 9. Call the accept() method of the SSLServerSocket to wait for an incoming connection. It's all pretty complicated, but the process is the same each time, so it makes sense to follow along and see how it all works. In the panels that follow, we'll walk through the code that carries out these steps. We'll examine only the client-side process in detail, because the server-side process is nearly the same. Afterwards, we'll note the differences between the two sides.
Creating a SecureRandom
Creating a SecureRandom object is easy; just use the following lines in your code:
The first line actually creates the SecureRandom. Creating a SecureRandom takes a lot of computation, and this computation may not be executed until it's actually used. By calling the nextInt() method here, we get the creation process going and ensure the delay will happen at the start of the program, not later when it might inconvenience us.
Creating keystores
Next, we need to create some KeyStore objects. We create an empty KeyStore using the static method KeyStore.getInstance(), and initialize it using its load() method, as illustrated below:
private void setupServerKeystore() throws GeneralSecurityException, IOException { serverKeyStore = KeyStore.getInstance( "JKS" ); serverKeyStore.load( new FileInputStream( "server.public" ), passphrase.toCharArray() ); }
Note that we've created a KeyStore of type "JKS"; this is the standard keystore format used in JCA.
Reading keystores
In the previous panel, we read the key information from server.public, which contains the server side's public key. We also need to read the client key pair from client.private, as shown here:
Page 17 of 22
ibm.com/developerWorks
private void setupClientKeyStore() throws GeneralSecurityException, IOException { clientKeyStore = KeyStore.getInstance( "JKS" ); clientKeyStore.load( new FileInputStream( "client.private" ), "public".toCharArray() ); }
Setting up factories
The next step is to use the KeyStore objects we've created to initialize key and trust manager factories. We'll create a TrustManagerFactory from the server keystore; this will be used to authenticate (that is, begin to trust) the remote server:
Note that the TrustManagerFactory is of type "SunX509"; 509 is the name of the certification protocol we're using throughout this program. In the second code line, the TrustManagerFactory is loaded with the server's keystore. We must also create a KeyManagerFactory from the client's KeyStore, as shown below:
Creating an SSLContext
We're getting close to finalizing our secure socket setup, so stay with me! The next step is to create an SSLContext. An SSLContext contains all the key and certificate information we've mentioned so far, and is used to create an SSLSocketFactory, which in turn creates secure sockets. Once you've created an SSLContext at the start of an application, you can use it for each connection you need to make, as long as each connection uses the same keys. To create an SSLContext, we use our factories and the SecureRandom, as shown here:
Note that we've created an SSLContext of type "TLS". As you suspect, this stands for Transport Layer Security, which is the new name for the Secure Sockets Layer, or SSL. We then initialize it with the TrustManagerFactory and KeyManagerFactory objects we
Page 18 of 22
ibm.com/developerWorks
Establishing a connection
We're at the last step. All the work we've done so far has given us an SSLContext, which we're going to use to make a connection to a remote machine, as shown in the code sample below:
We've just made a secure connection to port port on the machine host.
Server-side setup
Setting up the server side is more or less the same as setting up the client side, so we won't go over it in detail. Of course, the server reads its key information from client.public and server.private, rather than from server.public and client.private. In addition, the code to carry out the final step (establishing a connection) is a little different for the server side, as shown here:
Note that we called SSLServerSocket.setNeedClientAuth(). This is the server call indicating that the client should authenticate itself. Client applications do not authenticate themselves by default, so you must make this call if you want client authentication to be part of the handshaking process.
Page 19 of 22
ibm.com/developerWorks
Resources
Download jsse-source.zip, the code source for the examples and scripts used in this tutorial. Download the JDK 1.4 (http://java.sun.com/j2se/1.4/download.html) from Sun Microsystems. Read the installation instructions for JSSE 1.0.2 (http://java.sun.com/products/jsse/INSTALL.html), an extension to the Java 2 platform. The Sans Institute offers a good introduction to the history of public-key (Diffie-Hellman) cryptography (http://rr.sans.org/encryption/diffie.php).
Page 20 of 22
ibm.com/developerWorks
The IEEE maintains a page documenting specifications for public-key cryptography (http://grouper.ieee.org/groups/1363/). Verisign, an IBM affiliate, is a major third-party provider of SSL certificates (http://www.verisign.com/products/site/index.html). Thawte, another major provider of SSL certificates, will let you try before you buy (http://www.thawte.com/). Sun Microsystems's Security enhancements for the Java 2 SDK, Standard Edition v1.4 (http://java.sun.com/j2se/1.4/docs/guide/security/) describes the security features included in JDK 1.4. Read Sun's guide to the Java Cryptography Extension (http://java.sun.com/products/jce/) to find out more about JCE. The Java Secure Socket Extension (JSSE) Reference Guide (http://java.sun.com/j2se/1.4/docs/guide/security/jsse/JSSERefGuide.html) provides a comprehensive guide to JSSE. For more hands-on learning about sockets programming for the Java platform, take the "Java sockets 101" tutorial by Roy Miller and Adam Williams (developerWorks, August 2001, http://www-106.ibm.com/developerworks/education/r-jsock.html). For details on streams, check out the tutorial Introduction to Java I/O by IBM Learning Services (developerWorks, April 2000, http://www-106.ibm.com/developerworks/education/r-jio.html). Note that this tutorial is not based on Java 1.4. The JDK 1.4 tutorial, also by Greg Travis, includes a chapter on JSSE (Manning Publications, May 2002, http://www.amazon.com/exec/obidos/ASIN/1930110456/103-2127487-6951840). To learn more about socket programming, read Java Network Programming : A Complete Guide to Networking, Streams, and Distributed Computing, by Merlin Hughes, Michael Shoffner, Derek Hamner, and Conrad Hughes (Manning Publications, 1999, http://www.amazon.com/exec/obidos/ASIN/188477749X/qid=1016060142/sr=8-1/ ref=sr_8_71_1/103-2127487-6951840). Another good resource for learning about socket programming and security is Java Network Programming, by Elliotte Rusty Harold (O'Reilly and Associates, 2000, http://www.amazon.com/exec/obidos/ASIN/1565928709/103-2127487-6951840).
Page 21 of 22
ibm.com/developerWorks
If you need to learn more about security on the Java 2 platform, see Professional Java Security, by Jess Garms and Daniel Somerfield (Wrox Press, 2001, http://www.amazon.com/exec/obidos/ASIN/1861004257/103-2127487-6951840). The whitepaper "Security in a Web services world" (developerWorks, April 2002, http://www-106.ibm.com/developerworks/library/ws-secmap/) discusses the new joint IBM-Microsoft proposal for integrated, standardized security on Web services architectures; the proposal includes a provision for the use of public-key technology. The IBM Zurich Research Lab (http://www.zurich.ibm.com/csc/infosec/) has a long history in the study and implementation of security systems, especially in the area of cryptography.
Feedback
Please let us know whether this tutorial was helpful to you and how we could make it better. We'd also like to hear about other tutorial topics you'd like to see covered. Thanks!
Colophon
This tutorial was written entirely in XML, using the developerWorks Toot-O-Matic tutorial generator. The open source Toot-O-Matic tool is an XSLT stylesheet and several XSLT extension functions that convert an XML file into a number of HTML pages, a zip file, JPEG heading graphics, and two PDF files. Our ability to generate multiple text and binary formats from a single source file illustrates the power and flexibility of XML. (It also saves our production team a great deal of time and effort.) You can get the source code for the Toot-O-Matic at www6.software.ibm.com/dl/devworks/dw-tootomatic-p. The tutorial Building tutorials with the Toot-O-Matic demonstrates how to use the Toot-O-Matic to create your own tutorials. developerWorks also hosts a forum devoted to the Toot-O-Matic; it's available at www-105.ibm.com/developerworks/xml_df.nsf/AllViewTemplate?OpenForm&RestrictToCategory=11. We'd love to know what you think about the tool.
Page 22 of 22