Check out the new USENIX Web site.
JavaUSENIX

 

Security Outside the JVM

rao_prithvi

by Prithvi Rao
<prithvi+@kiwilabs.com>

Prithvi Rao is the founder of Kiwilabs, which specializes in software engineering methodology and Java training. He has worked on the development of the MACH OS and a real-time version of MACH, and he holds two patents resulting from his work on mobile robots.


In a previous ;login: article ("Is Java Secure?" August 1998) I presented some issues related to security inside the JVM.

The focus of this article is security outside the JVM. Specifically, I will examine the Java Cryptography Architecture (JCA), which is in the package "java.security" of the jdk1.1.* release. I'll examine the design of the JCA and present a sample Message Digest (MD5) example.

The JCA is an interface for application developers who wish to use the cryptographic functionality and for those who wish to provide cryptographic functionality; the latter are called "providers."

The security package is based on public key cryptography in which each party participating in a secret conversation has a public key and a private key. The cryptographic functionality refers to message digests, signing messages, public key certification, and many other functions.

How the JVM and JCA Work Together

Java environments can take advantage of cryptographic functionality by attaching to a class file of an applet, the digital signature of the person who wrote the applet. The browser that downloads the applet can examine the digital signature and enforce some policy regarding the "trustability" of the applet. The JDK1.1 appletviewer and HotJava browser will support signed applets.

Regardless of how Java-enabled environments can make use of JCA, it is clear that applications such as electronic commerce (and other transaction-based applications) can make good use of JCA.

Message Digests

As the name suggests, a message digest is a condensed representation of some data. It is a representation in the sense that it is very difficult (not impossible) to find another message that has the same digest. Message digests are commonly provided in most cryptographic software, and so it makes sense to examine it here.

Many algorithms can be applied to provide message digest capability. Some well-known algorithms are MD2, MD5, SHA (signature hash algorithm). Using the JDK security package, it is possible to choose any of these. Naturally, the choice is made by the user on the basis of the stringent security requirements of the application.

JCA Design

JCA has been designed with both "users" and "providers" in mind. For instance:

Users (who need cryptographic support):

  • can think in terms of concepts
  • can request a particular functionality and algorithm
  • can specify a particular implementation.

So if an MD5 algorithm is available from two different providers, the application developer can specify not only that they wish to use the MD5 algorithm, but also that it should be a specific provider's implementation.

Providers (those who provide cryptographic functionality):

  • implement a set of security services
  • can coexist; a particular functionality can be provided by more than one provider.
  • can work together.

Providers can use the basic classes to provide cryptographic functionality. The same functionality can be provided by various providers, and they can work together. For example, support for PGP (Pretty Good Privacy) keys from one provider can work with keys of another provider. This is significant when you consider that code reusability might result in using classes that are not using PGP keys from the same provider (yet another example of why understanding interoperability is important in a different context). Another example is the ability to take the MD5 output of one provider and have it compared with MD5 implementation of another provider.

JCA Classes

The classes that are part of the java.security package consist of a collection of core classes. These core classes are referred to as engines because they encapsulate the actual mechanism that does the work.

Each class provides a particular functionality. Some example classes are:

public abstract class MessageDigest{....}
public abstract class Signature{....}

The file MessageDigest.java contains the class definition for message digests. Another core class is Signature and is used for electronic signing of data. All of these core classes are abstract classes. In other words they encapsulate a concept rather than a specific algorithm.

Core classes must be subclassed for each implementation of a particular algorithm:

public class MD5 extends MessageDigest{...}
public class SHA extends MessageDigest{...}
public class SHAWithDSA extends Signature{...}

A provider wishing to provide a particular functionality must do so by first extending the core class for that functionality and then providing an implementation. This is why these abstract classes are called "engines." The subclass SHA, for example, will define a method that does the work of SHA, and similarly SHAWithDSA will contain a method which does the work of computing the hash using the SHA algorithm and then encrypting the computed hash using the DSA (Digital Signature Algorithm) algorithm.

Users who wish to use these implementations instantiate a class of particular provider:

MessageDigest md = MessageDigest.getInstance("MD5");
MessageDigest md = MessageDigest.getInstance("MD5", "NSA");
Signature sig = Signature.getInstance("DSA", "PrettyGoodSecurity");

Each of the core classes contains a static method called getInstance which searches for and returns an instance of the engine requested. So in the first example, md is an instance of MD5 provided by the "default" provider. The second is an instance of MD5 provided by "NSA." The third is an example in which sig is an instance of the DSA algorithm by a provider called "Pretty Good Security." There is a configuration file in which you specify the availability of providers.

JCA: An MD5 Example

Let's take a look at an MD5 application. Here we use a provider's implementation.

import java.io.*;
import java.security.*;

The second import must be included because it is not loaded automatically.

public class MD5Test
{
 public static void main(String[] args)
 {
  byte[] msg, msgSunDigest;

Msg is the buffer for the message for which we want to compute the digest. MsgSunDigest is the buffer which will eventually contain the MD5 digest.

  File f;
  FileInputStream fis;
  MessageDigest md5Sun;

Md5Sun is the instance of the MD5 digest class. Since it is a subclass of MessageDigest this declaration is ok.

  int c, nbytes, len;

  if(args.length < 1)

   {

    System.out.println("Usage: java <file to digest>");

    System.exit(1);

    }

    try
    {

Have to put this within a try because many of the methods below throw exceptions.

    md5Sun = MessageDigest.getInstance("MD5");
    // md5Sun = MessageDigest.getInstance("MD5", "SUN");

This is where the program creates an instance of the message digest class which implements the MD5 algorithm. The first example asks for any implementation of MD5, and the second requests SUN's implementation, which is actually also the default.

The reason to use getInstance rather than instantiating a class using "new"is that your application might be running on a host in an environment about which you know very little. In order to instantiate a class as "new MD5()" it is necessary to know the package from which it came, and this is not always possible. The danger in requesting a particular provider is that the program will throw an exception if that provider's algorithm is not available. You can always set up exception handlers to try other "well known" providers if the provider of choice throws an UnknownProviderException.

    f = new File(args[0]);
    msg = new byte[len = (int)f.lenght()];
    fis = new FileInputStream(f);
    if ((nbytes = fis.read(msg)) != len)
     throw new IOException(len + "unavailable.");
    fis.close();

Open and read the file containing the message for which we wish to compute the digest.

    md5Sun.update(msg);

Update is an abstract method of the class "security.MessageDigest." It takes a buffer with bytes from the original message and supplies it to the MD5 engine. Here we supply the entire message to update.

    msgSunDigest = md5Sun.digest();

Digest is another abstract method of "security.MessgeDigest." This method returns the (MD5) digest of the original message. The MD5 algorithms always produce digests that are 128 bits (16 bytes) long.

    System.out.println(md5Sun.toString());
    }
}

Print out the data.

Running the Program

To run the program try the following steps:

unix> javac -g MD5Test
unix> java MD%test <name of text file>

The output will show the provider's name, the MD5 hash value and the output of the original text file.

Conclusion

The security package is very intuitive and simple to use. Once again, this is a testimony to Sun's desire to create an easy, extensible, and strong security architecture.

Future articles in this series will include an example on how to be a provider and will examine the Java Cryptographic Extensions (JCE).

Readers wishing to get source for the example can send email to the author.

 

?Need help? Use our Contacts page.
First posted: 1st February 1999 jr
Last changed: 1st February 1999 jr
Java index
Publications index
USENIX home