Book Home Java Security Search this book

Chapter 10. Keys and Certificates

Contents:

Keys
The KeyPairGenerator Class
The KeyFactory Class
Certificates
Keys, Certificates, and Object Serialization
Summary

In this chapter, we discuss the classes in the Java security package that handle keys and certificates. Keys are a necessary component of many cryptographic algorithms--in particular, keys are required to create and verify digital signatures. The keys we're going to discuss in this chapter are public keys and private keys, since those are the keys most often used in a digital signature. Secret keys--used for encryption algorithms--are discussed in Chapter 13, "Encryption". We defer that discussion because secret keys do not come with standard Java implementations; they come only with the Java Cryptography Extension.

We also cover the implementation of certificates in this chapter. Certificates are used to authenticate keys; when keys are transmitted electronically, they are often embedded within certificates.

Keys and certificates are normally associated with some person or organization, and the way in which keys are stored, transmitted, and shared is an important topic in the security package. Management of keys is left for the next chapter, however; right now, we're just concerned about the APIs that implement keys and certificates. As usual, we'll show how a programmer interacts with keys and certificates, as well as how you might implement your own versions of each.

The classes and engines we discuss in this chapter are outlined in Figure 10-1. There are two engines that operate on keys:

figure

Figure 10-1. The interaction of key classes

There are a number of classes and interfaces we'll discuss to facilitate support for Figure 10-1; in addition to the engine classes themselves, there are several classes and interfaces that represent the key objects and the key specifications (the encoded key data is always an array of bytes). In an effort to provide the complete story, we'll delve into the details of all of these classes; for the most part, however, the important operations that most developers will need are:

This means that, for the most part, the data objects we explore in this chapter--the Key classes and interfaces as well as the various KeySpec classes (key specification classes)--can be treated by most programmers as opaque objects. We'll show their complete interface (which you might be curious about, and which is absolutely needed if you're writing your own security provider), but we'll try not to lose sight of the two goals of this chapter.

Also note that the idea of the key factory and key specifications is available only with Java 1.2.[1] In Java 1.1, you can get the encoded key data directly from a key, but that's a one-way operation.

[1]1.2 is now Java 2.

10.1. Keys

Let's start with the various classes that support the notion of keys within Java.

10.1.1. The Key Interface

The concept of a key is modeled by the Key interface (java.security.Key):

public interface Key extends Serializable

Model the concept of a single key. Because keys must be transferred to and from various entities, all keys must be serializable.

As we discussed in Chapter 8, "Security Providers", there might be several algorithms available for generating (and understanding) keys, depending on the particular security providers that are installed in the virtual machine. Hence, the first thing a key needs to be able to tell us is what algorithm generated it:

public String getAlgorithm()

Return a string describing the algorithm used to generated this key; this string should be the name of a standard key generation algorithm.

We listed the standard algorithm names for key generation in Chapter 8, "Security Providers", but with the default provider with the JDK, this string is always DSA.

When a key is transferred between two parties, it is usually encoded as a series of bytes; this encoding must follow a format defined for the type of key. Keys are not required to support encoding--in which case the format of the data transferred between the two parties in a key exchange is either obvious (e.g., simply the serialized data of the key) or specific to a particular implementation. Keys tell us the format they use for encoding their output with this method:

public String getFormat()

Return a string describing the format of the encoding the key supports.

For DSA keys produced by the Sun security provider, this format is always PKCS#8 for private keys and X.509 for public keys. The encoded data of the key itself is produced by this method:

public byte[] getEncoded()

Return the bytes that make up the particular key in the encoding format the key supports. The encoded bytes are the external representation of the key in binary format.

Those are the only methods that a key is guaranteed to implement (other than methods of the Object class, of course; most implementations of keys override many of those methods). In particular, you'll note that there is nothing in the key interface that says anything about decoding a key. We'll say more about that later.

There are two additional key interfaces in the Java security API:

public interface PublicKey extends Key

public interface PrivateKey extends Key

These interfaces contain no additional methods. They are used simply for type convenience. A class that implements the PublicKey interface identifies itself as a public key, but it contains no methods that are different from any other key.

10.1.1.1. DSA keys

The keys supported by the Sun security provider are built around the DSA algorithm. DSA-generated keys are important enough to have several interfaces built around them; these interfaces enhance your ability to work with these specific types of keys. These interfaces are necessary because DSA keys have certain pieces of information that are not reflected in the default key interfaces: the DSA algorithm-specific parameters p, q, and g that are used to generate the keys. Knowledge of these variables is abstracted into the DSAParams interface (java.security.interfaces.DSAParams):

Class Definition

public interface DSAParams {
	public BigInteger getP();
	public BigInteger getQ();
	public BigInteger getG();
}

Keys that are generated by DSA will typically implement the DSAKey interface (java.security.interfaces.DSAKey):

public interface DSAKey

Provide DSA-specific information about a key.

Implementing this interface serves two purposes. First, it allows the programmer to determine if the key is a DSA key by checking its type. The second purpose is to allow the programmer to access the DSA parameters using this method in the DSAKey interface:

public DSAParams getParams()

Return the DSA parameters associated with this key.

These methods and interfaces allow us to do specific key manipulation like this:

Class Definition

public void printKey(Key k) {
	if (k instanceof DSAKey) {
		System.out.println("key is DSA");
		System.out.println("P value is " +
						((DSAKey) k).getParams().getP());
	}
	else System.out.println("key is not DSA");
}

The idea of a DSA key is extended even further by these two interfaces (both of which are in the java.security.interfaces package):

public interface DSAPrivateKey extends DSAKey
public interface DSAPublicKey extends DSAKey

These interfaces allow the programmer to retrieve the additional key-specific values (known as y for public keys and x for private keys in the DSA algorithm):

Class Definition

public void printKey(DSAKey k) {
	if (k instanceof DSAPublicKey)
		System.out.println("Public key value is " +
					((DSAPublicKey) k).getY());
	else if (k instanceof DSAPrivateKey)
		System.out.println("Private key value is " +
					((DSAPrivateKey) k).getX());
	else System.out.println("Bad key implementation");
}

DSA keys are often used in the Java world (and elsewhere in cryptography), and if you know you're dealing with DSA keys, these interfaces can be very useful. In particular, if you're writing a security provider that provides an implementation of DSA keys, you should ensure that you implement all of these interfaces correctly. For most programmers, however, keys are opaque objects, and the algorithm-specific features of DSA keys are not needed.

10.1.2. The KeyPair Class

There are no classes in the core JDK that implement any of the Key interfaces. However, there is one concrete class, the KeyPair class (java.security.KeyPair), that extends the abstraction of keys:

public final class KeyPair

Model a data object that contains a public key and a private key.

The KeyPair class is a very simple data structure class, containing two pieces of information: a public key and a private key. When we need to generate our own keys (which we'll do next), we'll need to generate both the public and private key at once. This object will contain both of the necessary keys. If you're not interested in generating your own keys, this class may be ignored.

The KeyPair class contains only two methods:

public PublicKey getPublic()
public PrivateKey getPrivate()

Return the desired key from the key pair.

A key pair object is instantiated through a single constructor:

public KeyPair(PublicKey pub, PrivateKey priv)

Create a key pair object, initializing each member of the pair.

In theory, a key pair should not be initialized without both members of the pair being present; there is nothing, however, that prevents us from passing null as one of the keys. Similarly, there are no security provisions within the KeyPair class that prevent the private key from being accessed--no calls to the security manager are made when the getPrivate() method is invoked. Hence the KeyPair class should be used with caution.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.