Java security evolution and concepts, Part 4

Learn how optional packages extend and enhance Java security

1 2 3 4 Page 2
Page 2 of 4

The Cipher class provides the functionality of a cryptographic cipher used for encryption and decryption. It forms the core of the JCE 1.2.1 framework. The static getInstance() method serves as a factory method. It takes as its arguments a transformation, which represents the cryptographic algorithm, and optionally a provider. A transformation is of the form:

  • "algorithm", example: "DES"
  • "algorithm/mode/padding", example: "DES/CBC/PKCS5Padding"

If no mode or padding have been specified, provider-specific default values for the mode and padding scheme are used.

The init() method takes a number of arguments including the mode, which is one of encrypt, decrypt, wrap or unwrap, key, or random. If all the data is available, the encryption/decryption can be achieved in one single step using the doFinal() method. Otherwise the update() method is called repeatedly, culminated by a doFinal() method. The Mac class has the same methods intended for the same effects as the Cipher class.

The snippet code below illustrates how to associate a stream as an input to a cipher using the CipherInputStream and CipherOutputStream classes:

FileInputStream fis = new FileInputStream("/tmp/a.txt");
CipherInputStream cis = new CipherInputStream(fis, cipher1);                         

The KeyGenerator class generates secret keys for symmetric algorithms. The methods getInstance() and init() work along the same lines as before. The generateKey() method generates the secret key.

Key factories convert keys (opaque cryptographic keys of type java.security.Key) into key specifications that initialize the algorithm.

The javax.crypto.SecretKeyFactory object operates only on symmetric keys, whereas a java.security.SecretKeyFactory object processes the public and private key components of a key pair.

The KeyAgreement uses a doPhase() to accomplish the key agreement in phases, followed by a generateSecret() for computing the shared secret once all the phases complete.

JCE programming model

Having seen the important classes, let's look at the general methodology for accomplishing encryption/decryption using JCE. Although the details for using individual algorithms vary, the general principles are:

  • Get the appropriate cryptographic provider.
  • Obtain the appropriate cipher using the getInstance() method.
  • Initialize the cipher with the init() method, as seen in Figure 4. The SecretKeyFactory and KeyGenerator generate the Key Object, an input to the algorithm as well as to Random classes and the different algorithm and the key specification classes.
  • Figure 4. Initialize the cipher algorithm
  • Encrypt or decrypt the data by repeatedly using update() methods, as illustrated in Figure 5.
  • Figure 5. Process data using update()
  • Depending on how it was initialized, the multiple-part encryption or decryption process finishes using the doFinal() method. For example, the process may use up a buffer remaining from the previous update, shown in Figure 6.
  • Figure 6. Process data using doFinal()

JCE example programs

The following program uses the Blowfish cipher to encrypt data:

     import java.security.*;
     import javax.crypto.*;
     import javax.crypto.spec.*;
     import java.io.*;
     /**
      * This program generates a Blowfish key, retrieves its raw bytes, and 
      * then reinstantiates a Blowfish key from the key bytes.
      * The reinstantiated key is used to initialize a Blowfish cipher for
      * encryption.
      */
     public class BlowfishKey {
         public static void main(String[] args) throws Exception {
             String message="This is just an example";
             // Install SunJCE provider
             Provider sunJce = new com.sun.crypto.provider.SunJCE();
             Security.addProvider(sunJce);
             // Get the KeyGenerator         
             KeyGenerator kgen = KeyGenerator.getInstance("Blowfish");
             // Generate the secret key specs.
             SecretKey skey = kgen.generateKey();
             byte[] raw = skey.getEncoded();
             SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");
             // Instantiate the cipher           
             Cipher cipher = Cipher.getInstance("Blowfish");
             cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
             // Encrypt ...
             byte[] encrypted = 
                 cipher.doFinal(message.getBytes());
         }
     }

The slightly modified program below illustrates the use of the SecureRandom class in conjunction with the cipher:

     import java.security.*;
     import javax.crypto.*;
     import javax.crypto.spec.*;
     import java.io.*;
     /**
      * This program generates a Blowfish key, retrieves its raw bytes, and 
      * then reinstantiates a Blowfish key from the key bytes.
      * The reinstantiated key is used to initialize a Blowfish cipher for
      * encryption.
      */
     public class BlowfishKey {
         public static void main(String[] args) throws Exception {
             String message="This is just an example";
             // Install SunJCE provider
             Provider sunJce = new com.sun.crypto.provider.SunJCE();
             Security.addProvider(sunJce);
             // Get the KeyGenerator         
             KeyGenerator kgen = KeyGenerator.getInstance("Blowfish");
             // setup the random class and associate with key Generator
             SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
             kgen.init(random);
             // Generate the secret key specs.
             SecretKey skey = kgen.generateKey();
             byte[] raw = skey.getEncoded();
             SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");
             // Instantiate the cipher           
             Cipher cipher = Cipher.getInstance("Blowfish");
             cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
             // Encrypt ...
             byte[] encrypted = 
                 cipher.doFinal(message.getBytes());
         }
     }

The example programs listed above are quite simplified since they do not cover key agreement nor negotiation between multiple parties, both beyond the scope of this discussion. The sample programs shipped with JCE cover some of those details. However, assuming the keys are available, the above program captures the essence of encrypting and decrypting. If confidentiality and integrity are desired over an insecure channel, a channel in which it becomes a nontrivial task to communicate a secret key, it may be easier to use secure sockets, which we will study in the following section.

Java Secure Socket Extension (JSSE)

In keeping with our theme, the Java Secure Socket Extension (JSSE) supplements Java core security just as do JAAS and JCE. JSSE enables secure Internet communications by providing a framework supporting the SSL and TLS protocols, both designed to protect the privacy and integrity of data while it is transferred across the network. Unlike using JCE, the key agreement and the cipher-suite agreements happen transparently over SSL.

Table 2 illustrates the algorithms and the key lengths used by JSSE for key agreement and/or authentication.

Table 2. JSSE key lengths for key agreement and/or authentication algorithms
AlgorithmValid Key Lengths(bits)
RSA public key 2,048 or 512 
Diffie-Hellman public key 1,024 or 512 
DSA public key 2,048 

Table 3 illustrates the algorithms and the key lengths used by JSSE for bulk encryption and decryption. Note: public key algorithms tend to be much slower than secret key algorithms; therefore, they are used only in the initial phase of key agreement and authentication, as we saw earlier.

Table 3. Key lengths for JSSE bulk encryption algorithms
AlgorithmValid Key Length(bits)
RC4 128 or 128 (40 effective) 
DES 64 (56 effective) or 64 (40 effective) 
Triple DES 192 (112 effective) 

The reference implementation, SunJSSE, provides some limited support for accessing PKCS12 keys as well. PKCS12 keys can be used in the keytool command with the -storetype option set to pkcs12. The default supports X.509 formats.

JSSE classes

JSSE classes can be found in the following packages:

  • javax.net
  • javax.net.ssl
  • javax.security.cert

The main classes comprise:

  • SSLSocket
  • SSLServerSocket
  • SocketFactory
  • ServerSocketFactory
  • SSLSocketFactory
  • SSLServerFactory

The class javax.net.ssl.SSLSocket stems from the java.net.Socket class. It supports all of the standard socket methods and adds additional methods specific to secure sockets. The SSLServerSocket class creates server sockets. The method createSocket() can create instances of sockets. Alternatively, the method accept() can also create server-side sockets.

The class SSLSocketFactory, a concrete implementation of the abstract SocketFactory, acts as a factory for creating secure sockets. The SSLServerSocketFactory class creates server sockets. We will use its getDefault() static method to obtain an instance of the class.

Several other support classes and interfaces exist, as detailed in the API documentation provided with the kit.

JSSE programming model

The programs use a keyStore and a trustStore for the purposes of authentication, provided via the properties javax.net.ssl.keyStore and javax.net.ssl.trustStore, respectively. These work in tandem, that is, a key entry amongst the keyStore entries should correspond to an entry in the trustStore entry on the other side. For example, a keyEntry, duke on the server side, should have a trustedCertEntry for the same key on the client side for server authentication to succeed.

The outline for the server code looks like:

import javax.net.ssl.*;
    // Provide entries for keyStore which contains server key
    // Create an instance of the factory
    SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
    // Create a server socket
    SSLServerSocket s =(SSLServerSocket)sslSrvFact.createServerSocket(port);
    // Accept connections
    SSLSocket in = (SSLSocket)s.accept();

Here's the corresponding client code:

import javax.net.ssl.*;
    // Provide entries for trustStore to enable server authentication
    // Create an instance of the factory
    SSLSocketFactory sslFact =(SSLSocketFactory)SSLSocketFactory.getDefault();
    // Create a socket and connect
    SSLSocket s = (SSLSocket)sslFact.createSocket(host, port);

A session key and a cipher suite are negotiated transparently for use during the session as long as the handshake process completes.

JSSE example programs

The following program pairs enable socket communications between systems without the use of SSL. Using a simple program like snoop on Unix systems, you can see the communications in the clear. Later, we will see how this problem can be avoided by using JSSE.

Below you'll find the code for the server. It waits for a connection, communicates once, then terminates. The examples use port 8181. Any other free port can be used as far as these examples are concerned:

import java.io.*;
import java.net.*;
public class HelloServer {
    public static void main(String[] args) {
        try {
            ServerSocket s = new ServerSocket(8181);
            Socket in = s.accept();
            PrintWriter out = new PrintWriter (in.getOutputStream(),
                                               true);
            out.println("Hello on a socket");
            in.close();
        } catch (Exception e) {}
    }
}

You'll find the corresponding client code below. 127.0.0.1 is the localhost. Using it in the program indicated below enables both the server and the client to run on the same machine, providing an argument will enable a connection to the respective server, if possible:

import java.io.*;
import java.net.*;
public class HelloClient {
    public static void main(String[] args) {
        try {
            Socket s = new Socket(args.length == 0 ? "127.0.0.1" : args[0], 8181);
            OutputStream out = s.getOutputStream();
            BufferedReader in = new BufferedReader (
                                 new InputStreamReader(s.getInputStream()));
            String str = in.readLine();
            System.out.println("Socket message: " + str);
            in.close();
        } catch (Exception e) {}
    }
}

From a programming viewpoint, it's easy to convert these simple programs to use SSL. To do so, we employ the javax.net.ssl package and the relevant classes. The code for the server and client looks as below, with the bold indicating changes made to the previous programs. The server uses the SSLServerSocket and SSLServerSocketFactory classes for SSL:

import java.io.*;
import java.security.*;
import javax.net.ssl.*;
public class HelloServerSSL {
    public static void main(String[] args) {
        SSLServerSocket s;
        try {
            Security.addProvider(
                new com.sun.net.ssl.internal.ssl.Provider());
            SSLServerSocketFactory sslSrvFact =
                (SSLServerSocketFactory)
                    SSLServerSocketFactory.getDefault();
            s =(SSLServerSocket)sslSrvFact.createServerSocket(8181);
            SSLSocket in = (SSLSocket)s.accept();
            PrintWriter out = new PrintWriter (in.getOutputStream(),
                                               true);
            out.println("Hello on a SSL socket");
            in.close();
        } catch (Exception e) {
            System.out.println("Exception" + e);
        }
    }
}

Next, you'll find the corresponding client code:

1 2 3 4 Page 2
Page 2 of 4