Part II : Secure Communications using the Java SE 6 Security API

This part shows you how to build applications that perform secure communications. The Java SE 6 platform provides three standard APIs that allow applications to perform secure communications: The Java Generic Security Service (GSS), the Java SASL API, and the Java Secure Socket Extension (JSSE). When building an application, which of these APIs should you use? The answer depends on many factors, including requirements of the protocol or service, deployment infrastructure, and integration with other security services. For example, if you are building an LDAP client library, you would need to use the Java SASL API because use of SASL is part of LDAP's protocol definition. As an other example, if the service supports SSL, then the client application attempting to access the service would need to use JSSE.

Exercise 3: Using the Java Generic Security Service (GSS) API


Goal of this exercise:


The goal of this exercise is to learn how to use the Java GSS API  to perform secure authentication and communication.

Background for this exercise:

The Generic Security Service API provides a uniform C-language interface to access various security services, such as authentication, message integrity, and message confidentiality. The Java GSS API provides the corresponding interface for Java applications.  It allows applications to perform authentication and establish secure communication with the peer. One of the most common security service accessed via the GSS-API and Java GSS-API is Kerberos.

Resources for this Exercise:

  1. The JAAS and Java GSS-API Tutorials
  2. Generic Security Service API Version 2: Java Bindings (RFC 2853)
  3. Java GSS javadocs: org.ietf.jgss.

Overview of this Exercise:

This exercise is a client-server application that demonstrates how to communicate securely using the Java GSS API. The client and server parts first authenticate to Kerberos, as shown in Exercise 1. This stores the credentials in the subject. The application then executes an action that performs Java GSS operations (with Kerberos as the underlying GSS mechanism) inside of a Subject.doAs using the subject. The Java GSS Kerberos mechanism, because it is executing inside the doAs, obtains the Kerberos credentials from the subject, and uses them to authenticate with the peer and to exchange messages securely.

Steps to follow:

  1. Read the following code. This is located in src/GssServer.java.

    This code fragment defines the action to execute after the service principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The code first creates an instance of GSSManager [line 8], which it uses to obtain its own credentials [line 10-11] and to create an instance of GSSContext [line 18]. It uses this context to perform authentication [the loop between lines 22-34]. Upon completing authentication, it accepts encrypted input from the client and uses the established security context to decrypt the data [line 45]. It then uses the security context to encrypt a reply containing the original input and the date [line 49], and then sends it back to the client.

Code listing for GssServer.java.



  1. static class GssServerAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create server socket for accepting connections
  5.     ServerSocket ss = new ServerSocket(localPort);

  6.     // Get own Kerberos credentials for accepting connection
  7.     GSSManager manager = GSSManager.getInstance();
  8.     Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
  9.     GSSCredential serverCreds = manager.createCredential(null,
  10.       GSSCredential.DEFAULT_LIFETIME, krb5Oid, GSSCredential.ACCEPT_ONLY);

  11.     while (true) {
  12.       Socket socket = ss.accept();
  13.       DataInputStream inStream = new DataInputStream(socket.getInputStream());
  14.       DataOutputStream outStream = new DataOutputStream(socket.getOutputStream());

  15.       GSSContext context = manager.createContext((GSSCredential)serverCreds);

  16.       // Do the context establishment loop
  17.       byte[] token = null;
  18.       while (!context.isEstablished()) {
  19.         // Read token
  20.         token = new byte[inStream.readInt()];
  21.         inStream.readFully(token);

  22.         // Process token
  23.         token = context.acceptSecContext(token, 0, token.length);

  24.         // Send a token to the peer if one was generated by acceptSecContext
  25.         outStream.writeInt(token.length);
  26.         outStream.write(token);
  27.         outStream.flush();
  28.       }

  29.       // Context established!

  30.       // Create MessageProp for use with unwrap (will be set upon return from unwrap)
  31.       MessageProp prop = new MessageProp(0, false);

  32.       // Read token from client
  33.       token = new byte[inStream.readInt()];
  34.       inStream.readFully(token);
  35.       // Unwrap (decrypt) token sent by client
  36.       byte[] input = context.unwrap(token, 0, token.length, prop);
  37.       ...
  38.       // Create new token and send to client
  39.       byte[] reply = ...;
  40.       token = context.wrap(reply, 0, reply.length, prop);

  41.       outStream.writeInt(token.length);
  42.       outStream.write(token);
  43.       outStream.flush();
  44.       context.dispose();
  45.       socket.close();
  46.     }
  47.   }
  48. }


  1. Compile the sample code.
    % javac GssServer.java
  2. Read the following code. This is located in src/GssClient.java.

    This code fragment defines the action to execute after the client principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The code first creates an instance of GSSManager [line 10], which it uses to obtain a principal name for the service that it is going to communicate with [line 12]. It then creates an instance of GSSContext [line 15,16] to perform authentication [the loop between lines 22-33] with the service. Upon completing authentication, it uses the established security context to encrypt a message [line 42] and sends it to the server. It then reads an encrypted message from the server and decodes it using the established security context [line 53].
Code listing for GssClient.java.

  1. static class GssClientAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create socket to server
  5.     Socket socket = new Socket(hostName, port);
  6.     DataInputStream inStream = new DataInputStream(socket.getInputStream());
  7.     DataOutputStream outStream = new DataOutputStream(socket.getOutputStream());

  8.     // Get service's principal name
  9.     GSSManager manager = GSSManager.getInstance();
  10.     Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
  11.     GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE);

  12.     // Get the context for authentication
  13.     GSSContext context = manager.createContext(serverName, krb5Oid, null,
  14.        GSSContext.DEFAULT_LIFETIME);
  15.     context.requestMutualAuth(true); // Request mutual authentication
  16.     context.requestConf(true); // Request confidentiality

  17.     // Do the context establishment loop
  18.     byte[] token = new byte[0];
  19.     while (!context.isEstablished()) {
  20.       token = context.initSecContext(token, 0, token.length);
  21.       outStream.writeInt(token.length);
  22.       outStream.write(token);
  23.       outStream.flush();

  24.       // Check if we're done
  25.       if (!context.isEstablished()) {
  26.         token = new byte[inStream.readInt()];
  27.         inStream.readFully(token);
  28.       }
  29.     }

  30.     // Context established!

  31.     // Create MessageProp for use with unwrap (true means request confidentiality)
  32.     MessageProp prop = new MessageProp(0, true);

  33.     // Create encrypted message and send to server
  34.     byte[] reply = ...;
  35.     token = context.wrap(reply, 0, reply.length, prop);

  36.     outStream.writeInt(token.length);
  37.     outStream.write(token);
  38.     outStream.flush();

  39.     // Read token from server
  40.     token = new byte[inStream.readInt()];
  41.     inStream.readFully(token);

  42.     // Unwrap (decrypt) token sent by server
  43.     byte[] input = context.unwrap(token, 0, token.length, prop);
  44.     ...
  45.     context.dispose();
  46.     socket.close();
  47.     return null;
  48.   }
  49. }


  1. Compile the sample code.
    % javac GssClient.java
    
  2. Launch a new window and start the server.
    % xterm &
    % java -Djava.security.auth.login.config=jaas-krb5.conf \
           GssServer
    
  3. Run the client application. GssClient takes two parameters: the service name and the name of the server that the service is running on. For example, if the service is host running on the machine j1hol-001, you would enter the following. When prompted for the password, enter changeit.
        
    % java -Djava.security.auth.login.config=jaas-krb5.conf \
           GssClient host j1hol-001
    
  4. Observe the following output in the client and server applications' windows.

Output for running GssServer example.


  1. Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]
  2. Waiting for incoming connections...
  3. Got connection from client /129.145.128.102
  4. Context Established!
  5. Client principal is test@J1LABS.EXAMPLE.COM
  6. Server principal is host/j1hol-001@J1LABS.EXAMPLE.COM
  7. Mutual authentication took place!
  8. Received data "Hello There!" of length 12
  9. Confidentiality applied: true
  10. Sending: Hello There! Thu May 06 12:11:15 PDT 2005

Output for running GssClient example.


  1. Kerberos password for test: changeit
  2. Authenticated principal: [test@J1LABS.EXAMPLE.COM]
  3. Connected to address j1hol-001/129.145.128.102
  4. Context Established!
  5. Client principal is test@J1LABS.EXAMPLE.COM
  6. Server principal is host@j1hol-001
  7. Mutual authentication took place!
  8. Sending message: Hello There!
  9. Will read token of size 93
  10. Received message: Hello There! Thu May 06 12:11:15 PDT 2005

Summary:

In this exercise, you learned how to write a client-server application that uses the Java GSS API to authenticate and communicate securely with each other.

Next Steps

  1. Proceed to Exercise 4 to learn how to write a client/server application that uses the Java SASL API to authenticate and communicate securely with each other.
  2. Proceed to Exercise 5 to learn how to write a client/server application that uses the JSSE to authenticate and communicate securely with each other.
  3. Proceed to Exercise 6 to learn how to configure the sample programs that you have just used to achieve single sign-on in a Kerberos environment.

Exercise 4: Using the Java SASL API

Goal of this exercise:

The goal of this exercise is to learn how to use the Java SASL API  to perform secure authentication and communication.

Background for this exercise:

Simple Authentication and Security Layer (SASL) specifies a challenge-response protocol in which data is exchanged between the client and the server for the purposes of authentication and (optional) establishment of a security layer on which to carry on subsequent communications. SASL allows different mechanisms to be used; each such mechanism is identified by a profile that defines the data to be exchanged and a name. SASL is used with connection-based protocols such as LDAPv3 and IMAPv4. SASL is described in RFC 4422.

The Java SASL API defines an API for applications to use SASL in a mechanism-independent way. For example, if you are writing a library for a networking protocol that uses SASL, you can use the Java SASL API to generate the data to be exchanged with the peer. When the library is deployed, you can dynamically configure the mechanisms to use with the library.

In addition to authentication, you can use SASL to negotiate a security layer to be used after authentication. But unlike the GSS-API, the properties of the security layer (such as whether you want integrity or confidentiality) is decided at negotiation time. (the GSS-API allows confidentiality to be turned on or off per message).

Resources for this exercise:

  1. The Java SASL API Programming and Deployment Guide
  2. The Java SASL API javadocs
  3. Simple Authentication and Security Layer (SASL) (RFC 4422)

Overview of this exercise:

This exercise is a client-server application that demonstrates how to communicate securely using the Java SASL API. The client and server parts first authenticate to Kerberos using Exercise 1. This stores the credentials in the subject. The application then executes an action that performs Java SASL API operations (with Kerberos as the underlying SASL mechanism) inside of a Subject.doAs using the subject. The SASL/Kerberos mechanism, because it is executing inside the doAs, obtains the Kerberos credentials from the subject, and uses them to authenticate with the peer and to exchange messages securely.

This example uses a simple protocol implemented by the AppConnection class. This protocol exchanges authentication commands and data commands. Each command consists of a type (e.g., AppConnection.AUTH_CMD), the length of the data to follow, and the data itself. The data is a SASL buffer if it is for authentication or encrypted/integrity-protected application data; it is plain application data otherwise.

Steps to follow:

  1. Read the following code. This is located in src/SaslTestServer.java.

    This code fragment defines the action to execute after the service principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The server specifies the quality-of-protections that it will support [line 9] and then creates an instance of SaslServer to perform the authentication [line 21]. The challenge-response protocol of SASL is performed in the while loop [lines 33-49], with the server sending challenges to the client and processing the responses from the client. After authentication, the identity of the authenticated client can be obtained via a call to getAuthorizedID() [line 61]. If a security layer was negotiated, the server can exchange data securely with the client [lines 66,70].

Code listing for SaslTestServer.java.


  1. static class SaslServerAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create server socket for accepting connections
  5.     ServerSocket ss = new ServerSocket(localPort);

  6.     // Support all quality-of-protection options
  7.     HashMap<String,Object> props = new HashMap<String,Object>();
  8.     props.put(Sasl.QOP, "auth-conf,auth-int,auth");

  9.     while (true) {
  10.       // Create application-level connection to handle request
  11.       Socket socket = ss.accept();
  12.       AppConnection conn = new AppConnection(socket);

  13.       // Normally, the application protocol will negotiate which
  14.       // SASL mechanism to use. In this simplified example, we
  15.       // will always use "GSSAPI" (the name of the mechanism that does Kerberos via GSS-API)

  16.       // Create SaslServer to perform authentication
  17.       SaslServer srv = Sasl.createSaslServer("GSSAPI", service, serviceName, props, cbh);

  18.       if (srv == null) {
  19.         // ... handle error
  20.       }

  21.       // Read the initial response from client
  22.       byte[] response = conn.receive(AppConnection.AUTH_CMD);
  23.       AppConnection.AppReply clientMsg;
  24.       boolean auth = false;

  25.       // Perform authentication
  26.       while (!srv.isComplete()) {
  27.         try
  28.           // Generate challenge based on response
  29.           byte[] challenge = srv.evaluateResponse(response);
  30.           if (srv.isComplete()) {
  31.             conn.send(AppConnection.SUCCESS, challenge);
  32.             auth = true;
  33.           } else {
  34.             clientMsg = conn.send(AppConnection.AUTH_IN_PROGRESS, challenge);
  35.             response = clientMsg.getBytes();
  36.           }
  37.         } catch (SaslException e) {
  38.           // Send failure notification to client
  39.           conn.send(AppConnection.FAILURE, null);
  40.           break;
  41.         }
  42.       }

  43.       // Authentication completed!

  44.       // Check status of authentication
  45.       if (srv.isCompleted() && auth) {
  46.         System.out.println("authorized client is: " + srv.getAuthorizationID());
  47.       } else {
  48.         // Report failure ...
  49.       }

  50.       // Find out whether security layer was negotiated
  51.       String qop = (String) srv.getNegotiatedProperty(Sasl.QOP);
  52.       boolean sl = (qop.equals("auth-conf") || qop.equals("auth-int"));

  53.       // Read and decrypt message from client
  54.       byte[] msg = conn.receive(AppConnection.DATA_CMD);
  55.       byte[] realMsg = (sl ? srv.unwrap(msg, 0, msg.length) : msg);
  56.       ...
  57.       // Create and encrypt message to send to client
  58.       byte[] reply = ...;
  59.       byte[] realReply = (sl ? srv.wrap(reply, 0, reply.length) : reply);
  60.       conn.send(AppConnection.SUCCESS, realReply);
  61.     }
  62.   }
  63. }


  1. Compile the sample code.
    % javac SaslTestServer.java
  2. Read the following code. This is located in src/SaslTestClient.java. This code fragment defines the action to execute after the client principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The program first specifies the quality of protection that it wants (in this case, confidentiality) [line 8] and then creates an instance of SaslClient to use for authentication [lines 11-12]. It then checks whether the mechanism has an initial response and if so, gets the response by invoking evaluateChallenge() with an empty byte array [line 20]. It then sends the response to the server to begin the authentication. The challenge-response protocol of SASL is performed in the while loop [lines 24-39], with the client evaluating the challenges that it gets from the server and sending the server the corresponding responses to the challenges. After authentication, the client can proceed to communicate with the server using the negotiated security layer [lines 48,55].

Code listing for SaslTestClient.java.


  1. static class SaslClientAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create application-level connection
  5.     AppConnection conn = new AppConnection(serverName, port);

  6.     HashMap<String,Object> props = new HashMap<String,Object>();
  7.     props.put(Sasl.QOP, "auth-conf"); // Request confidentiality

  8.     // Create SaslClient to perform authentication
  9.     SaslClient clnt = Sasl.createSaslClient(
  10.       new String[]{"GSSAPI"}, null, service, serverName, props, cbh);
  11.     if (clnt == null) {
  12.       // ... handle error
  13.     }

  14.     byte[] challenge;

  15.     // Get initial response for authentication
  16.     byte[] response = clnt.hasInitialResponse() ? clnt.evaluateChallenge(EMPTY) : EMPTY;
  17.     AppConnection.AppReply reply = conn.send(AppConnection.AUTH_CMD, response);

  18.     // Repeat until authentication terminates
  19.     while (!clnt.isComplete() &&
  20.       (reply.getStatus() == AppConnection.AUTH_INPROGRESS ||
  21.        reply.getStatus() == AppConnection.SUCCESS))
  22.       // Evaluate challenge to generate response
  23.       challenge = reply.getBytes()
  24.       response = clnt.evaluateChallenge(challenge)

  25.       if (reply.getStatus() == AppConnection.SUCCESS) {
  26.         if (response != null) {
  27.           throw new Exception("Protocol error")
  28.         }
  29.         break;
  30.       }
  31.       // Send response to server and read server's next challenge
  32.       reply = conn.send(AppConnection.AUTH_CMD, response);
  33.     }
  34.     // Authentication completed!
  35.     // Find out whether security layer was negotiated
  36.     String qop = (String) srv.getNegotiatedProperty(Sasl.QOP);
  37.     boolean sl = (qop.equals("auth-conf") || qop.equals("auth-int"));

  38.     byte[] msg = ...;

  39.     // Create and send encrypted data to server
  40.     byte[] encrypted = (sl ? clnt.wrap(msg, 0, msg.length) : msg);
  41.     reply = conn.send(AppConnection.DATA_CMD, encrypted);) {

  42.     ...

  43.     // Read and decrypt data from server
  44.     byte[] encryptedReply = reply.getBytes();
  45.     byte[] clearReply = (sl ? clnt.unwrap(encryptedReply, 0, encryptedReply.length)
  46.       : encryptedReply);
  47.     conn.close();
  48.     return null;
  49.   }
  50. }

  1. Compile the sample code.
    % javac SaslTestClient.java
  2. Launch a new window and start the server. SaslTestServer takes two parameters: the service name and the name of the server that the service is running on. For example, if the service is host running on the machine j1hol-001, you would enter the following.
    % xterm &
    % java
    -Djava.security.auth.login.config=jaas-krb5.conf \
    SaslTestServer host j1hol-001

  3. Run the client application. SaslTestClient takes two parameters: the service name and the name of the server that the service is running on. For example, if the service is host running on the machine j1hol-001, you would enter the following. When prompted for the password, enter changeit.

    % java -Djava.security.auth.login.config=jaas-krb5.conf \
    SaslTestClient host j1hol-001
  4. Observe the following output in the client and server applications' windows.

Output for running the SaslTestServer example.


  1. Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]
  2. Waiting for incoming connections...
  3. Got connection from client /129.145.128.102
  4. Client authenticated; authorized client is: test@J1LABS.EXAMPLE.COM
  5. Negotiated QOP: auth-conf
  6. Received: Hello There!
  7. Sending: Hello There! Fri May 07 15:32:37 PDT 2005
  8. Received data "Hello There!" of length 12

Output for running the SaslTestClient example.


  1. Kerberos password for test: changeit
  2. Authenticated principal: [test@J1LABS.EXAMPLE.COM]
  3. Connected to address j1hol-001/129.145.128.102
  4. Client authenticated.
  5. Negotiated QOP: auth-conf
  6. Sending: Hello There!
  7. Received: Hello There! Fri May 07 15:32:37 PDT 2005

  1. To try the program using different quality-of-protection, change line 8 in SaslTestClient. For example, replace line 8 with the following line to use integrity protection on (no confidentiality).
    props.put(Sasl.QOP, "auth-int");

Summary:

In this exercise, you learned how to write a client-server application that uses the Java SASL API to authenticate and communicate securely with each other.

Next Steps

  1. Proceed to Exercise 5 to learn how to write a client/server application that uses the JSSE to authenticate and communicate securely with each other.
  2. Proceed to Exercise 6 to learn how to configure the sample programs that you have just used to achieve single sign-on in a Kerberos environment.

Exercise 5: Using the Java Secure Socket Extension with Kerberos

Goal of this exercise:

The goal of this exercise is to learn how to use the JSSE API  to perform secure authentication and communication using Kerberos cipher suites.

Background for this exercise:

Secure Socket Layer (SSL) and Transport Layer Security (TLS) are the most widely used protocols for implementing cryptography on the Internet. TLS is the Internet standard evolved from SSL. SSL/TLS provides application-level protocols (such as HTTP and LDAP) with secure authentication and communication. For example, HTTPS is the resulting protocol of using HTTP over SSL/TLS. SSL/TLS is used not only for standard protocols such as HTTP, it is also widely used when building custom applications using custom protocols that need to communicate securely.

SSL/TLS traditionally used certificate-based authentication and is commonly used for server-authentication. For example, when a Web client such as a browser accesses a secure Web site (server) on behalf of a user, the server sends its certificate to the browser so that the browser can verify the identity of the server. This ensures that the user does not divulge confidential information (such as credit card information) to a bogus server. Recently, a new standard allows the use of Kerberos with TLS. This means instead of using certificate-based authentication, an application can use Kerberos credentials and take advantage of the Kerberos infrastructure in the deployment environment. Using Kerberos cipher suites also provides automatic support for mutual authentication in which the client is also authenticated in addition to the server.

The decision of whether to use Java GSS, Java SASL, or JSSE for a particular application often depends upon several factors, including (the protocols being used by) the services with which the application interacts, the deployment environment (PKI or Kerberos-based), and the application's security requirements. JSSE provides a secure end-to-end channel that takes care of the I/O and transport, while Java GSS and Java SASL provide encryption and integrity-protection on the data, but the application is responsible for transporting the secured data to its peer. Some details about factors for deciding when to use JSSE versus Java GSS are presented in the document, When to use Java GSS vs. JSSE.

Resources for this exercise:

  1. Java Secure Socket Extension (JSSE) Reference Guide
  2. The JSSE javadocs: javax.net and javax.net.ssl
  3. The SSL Protocol version 3.0
  4. The TLS Protocol Version 1.0 (RFC 2246)
  5. Addition of Kerberos Cipher Suites to Transport Layer Security TLS (RFC 2712)
  6. When to use Java GSS vs. JSSE

Overview of this exercise:

This exercise is a client-server application that demonstrates how to communicate securely using the JSSE and Kerberos cipher suites. The client and server parts first authenticate to Kerberos using Exercise 1. This stores the credentials in the subject. The application then executes an action that performs JSSE operations (using a Kerberos cipher suite) inside of a Subject.doAs using the subject. The Kerberos cipher suite implementation, because it is executing inside the doAs, obtains the Kerberos credentials from the subject, and uses them to authenticate with the peer and to exchange messages securely. This example sends newline-terminated messages, encrypted using the negotiated cipher suite and integrity-protected, back and forth between client and server.

According to the standard (RFC 2712) all Kerberos-enabled TLS applications use the same service name, namely, "host". That is why in this exercise, you do not need to explicitly supply the Kerberos service name.

Steps to follow:

  1. Read the following code. This is located in src/JsseServer.java

    This code fragment defines the action to execute after the service principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The server first creates an SSLServerSocket [lines 5-8]. This is analogous to an application creating a plain ServerSocket except an SSLServerSocket will provide automatic authentication, encryption and decryption, as needed. The server then sets the cipher suites that it wants to use [lines 11-12]. The server then runs in a loop, accepting connections from SSL clients [line 17], and reads and writes from the SSL socket [lines 23, 28]. The server can find out the identities of the owners of socket by invoking the getLocalPrincipal() and getPeerPrincipal() methods [lines 32-33].

Code listing for JsseServer.java.


  1. static class JsseServerAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create TLS socket for accepting connections
  5.     SSLServerSocketFactory sslssf =
  6.       (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
  7.     SSLServerSocket sslServerSocket =
  8.       (SSLServerSocket) sslssf.createServerSocket(localPort);

  9.     // Enable only a Kerberos cipher suite
  10.     String enabledSuites[] = { "TLS_KRB5_WITH_3DES_EDE_CBC_SHA" };
  11.     sslServerSocket.setEnabledCipherSuites(enabledSuites);
  12.     // Should handle exception if enabledSuites is not supported

  13.     while (true) {
  14.       // Create socket to handle request
  15.       SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
  16.       BufferedReader in = new BufferedReader(new InputStreamReader(
  17.         sslSocket.getInputStream()));
  18.       BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
  19.         sslSocket.getOutputStream()));

  20.       String inStr = in.readLine();
  21.       // ... use inStr

  22.       // Compose and send reply
  23.       String outStr = inStr + " " + new Date().toString() + "\n";
  24.       out.write(outStr);
  25.       out.flush();

  26.       // Get names of principal at both ends of secure connection
  27.       Principal self = sslSocket.getSession().getLocalPrincipal();
  28.       Principal peer = sslSocket.getSession().getPeerPrincipal();

  29.       sslSocket.close();
  30.     }
  31.   }
  32. }


  1. Compile the sample code.

    % javac JsseServer.java
  2. Read the following code. This is located in src/JsseClient.java. This code fragment defines the action to execute after the client principal has authenticated to the KDC. It replaces the MyAction of line 11 of Exercise 1. Note the highlighted lines. The client first creates an SSLSocket. The client then sets the cipher suites that it wants to use [lines 11-12]. The client then exchanges messages with the server using the SSLSocket by reading and writing to the socket's input/output streams. The client can find out the identities of the owners of socket by invoking the getLocalPrincipal() and getPeerPrincipal() methods [lines 26-27].

Code listing for JsseClient.java


  1. static class JsseClientAction implements PrivilegedExceptionAction {
  2. ...
  3.   public Object run() throws Exception {
  4.     // Create SSL connection
  5.     SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();
  6.     SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port);

  7.     // Enable only a Kerberos cipher suite
  8.     String enabledSuites[] = { "TLS_KRB5_WITH_3DES_EDE_CBC_SHA" };
  9.     sslSocket.setEnabledCipherSuites(enabledSuites);
  10.     // Should handle exception if enabledSuites is not supported

  11.     BufferedReader in = new BufferedReader(new InputStreamReader(
  12.       sslSocket.getInputStream()));
  13.     BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
  14.       sslSocket.getOutputStream()));

  15.     String outStr = ...;
  16.     out.write(outStr);
  17.     out.flush();

  18.     String inStr = in.readLine();
  19.     // ... use inStr

  20.     // Get names of principal at both ends of secure connection
  21.     Principal self = sslSocket.getSession().getLocalPrincipal();
  22.     Principal peer = sslSocket.getSession().getPeerPrincipal();

  23.     sslSocket.close();
  24.     return null;
  25.   }
  26. }


  1. Compile the sample code.
    % javac JsseClient.java
  2. Launch a new window and start the server. JsseServer takes one parameter: the name of the server that the JSSE service is running on. For example, if it is running on the machine j1hol-001, you would enter the following.
    % xterm &
    % java
    -Djava.security.auth.login.config=jaas-krb5.conf \
    JsseServer j1hol-001

  3. Run the client application. JsseClient takes one parameter: the name of the server that the JSSE service is running on. For example, if the service is running on the machine j1hol-001, you would enter the following. When prompted for a password, enter changeit.

    % java -Djava.security.auth.login.config=jaas-krb5.conf \
    JsseClient j1hol-001
  4. Observe the following output in the client and server applications' windows.

Output for running the JsseServer example.


  1. Authenticated principal: [host/j1hol-001@J1LABS.EXAMPLE.COM]
  2. Waiting for incoming connections...
  3. Got connection from client /129.145.128.102
  4. Received: Hello There!
  5. Sending: Hello There! Fri May 07 15:32:37 PDT 2005
  6. Cipher suite in use: TLS_KRB5_WITH_3DES_EDE_CBC_SHA
  7. I am: host/j1hol-001@J1LABS.EXAMPLE.COM
  8. Client is: test@J1LABS.EXAMPLE.COM

Output for running the JsseClient example.


  1. Kerberos password for test: changeit
  2. Authenticated principal: [test@J1LABS.EXAMPLE.COM]
  3. Sending: Hello There!
  4. Received: Hello There! Fri May 07 15:32:37 PDT 2005
  5. Cipher suite in use: TLS_KRB5_WITH_3DES_EDE_CBC_SHA
  6. I am: test@J1LABS.EXAMPLE.COM
  7. Server is: host/j1hol-001@J1LABS.EXAMPLE.COM

Summary:

In this exercise, you learned how to write a client-server application that uses JSSE to authenticate and communicate securely with each other, using Kerberos as the underlying authentication system.

Next Steps

  1. Proceed to Exercise 6 to learn how to configure the sample programs in Exercises 3, 4, and 5 to achieve single sign-on in a Kerberos environment.