11.2. The KeyStore ClassNow that we understand the pieces that make up a key management system, we can look at the topic of key management itself. From an administrative perspective, the primary tool that provides key management for Java 1.2 is the keytool utility. Keytool operates upon a file (or other storage system) containing a set of private keys and certificates for those keys. The keytool file contains a set of entries; each entry may have the following attributes:
We'd be tempted to call the entries in this database identities, but that's potentially confusing: the entries stored in the keytool database are not instances of the Identity class (although we could create an identity object based on the information retrieved from the database). Figure 11-1 shows the role of the keytool database in the creation and execution of a signed JAR file. Thejarsigner utility consults the keytool database for the private key of the entity that is signing the JAR file. Once the signed JAR file is produced, it is placed on a web server, where it can be downloaded into an appletviewer or other Java-enabled browser.[2] When the JAR file is read on the remote system, the keytool database is consulted in order to retrieve the public key of the entity that signed the JAR file so that the JAR file's signature can be verified.
Figure 11-1. The keytool database in a signed JAR fileNote that the two keytool databases in this example are (probably) separate databases, on separate machines. They probably have completely different entries as well--even for the entry that represents the signer. The signer's entry in her own database must have the private key of the signer, while the signer's entry in the user's database needs only a certificate (public key) for the signer. However, the keytool database could (in this and all examples) be a shared database--but more about that later. The default keytool database is the file .keystore that is held in the user's home directory. The class that implements the keytool database is the KeyStore class (java.security.KeyStore):
The KeyStore class is an engine class; there is a corresponding KeyStoreSpi class that you can use to write your own keystore (more about that a little later). By default, the Sun security provider implements a keystore called JKS (for Java KeyStore). Hence, instances of the KeyStore class are predictably obtained via this method:
If you do not want to hardwire the name of the keystore algorithm into your application, you may use this method to return the string that should be passed to the getInstance() method:
When the keystore object is created, it is initially empty. Although the getInstance() method has constructed the object, it is not expected that the object's constructor will read in a keystore from any particular location. The interaction between the keystore object and the keytool database comes via these two methods:
There is no default file that holds the keystore. Within the core Java API, the only class that opens the keystore is PolicyFile, and that opens the keystore that is listed in the java.policy file. The tools that use the keystore (the jarsigner and keytool tools) allow you to use a command-line argument to specify the file that contains the keystore; by default, that file is .keystore in the user's home directory. This is the convention your own programs will need to use. If your application needs to open the keystore (for example, to obtain a private key to sign an object), it should provide either a command-line argument or a property to specify the name of the file to open. By convention, we'll use the .keystore file in the user's home directory in our examples. While we mentioned that the keystore may not be encrypted, the private keys themselves typically are encrypted so that if someone gains access to the keystore file, they do not have access to the private keys in that file without the password used to encrypt those keys. If you provide a keystore implementation that supplies keys from a protected location, you do not necessarily need to store the private keys in encrypted format. When private keys are delivered over the network, you probably want to make sure that the transmission of those keys is encrypted so that no one can snoop the network and discover the private key. A keystore is arranged in terms of alias names. Aliases are arbitrarily assigned to an entry; while the name embedded in the certificate for a particular entry may be a long, complicated, distinguished name, the alias for that entry can provide a shorter, easier-to-remember name. There are a number of simple methods in the KeyStore class that deal with these alias names:
Note that this list has a method to delete an entry but not one to create an entry--creating an entry in the keystore depends upon the type of entry you want to create. The keystore holds two types of entries: certificate entries and key entries. A certificate entry is an entry that contains only a public key (encapsulated in a certificate) and can be used only to verify a digital signature, while a key entry is an entry that contains both a private and a public key and can be used to create and to verify a digital signature. Hence, you may think of a key entry as a signer and a certificate entry as an identity, although those classes are not used in the keystore interface (they may be used in the keystore implementation). There are two basic differences between key entries and certificate entries:
For a given alias, you can determine what type of entry it represents via these two methods:
For a given alias, you cannot retrieve an object that represents the entire entry. You may use these methods to retrieve information about the entry represented by an alias:
Finally, in order to create or modify an entry, you may use one of these methods. All of these methods create a new entry if the given alias does not exist:
These are the basic methods by which we can manage a keystore. We'll see examples of many of these methods throughout the rest of this book; for now, let's look at a simple example that looks up a given entry in the keystore: Class Definitionpublic class KeyStoreLookup { public static void main(String args[]) { try { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); String fname = System.getProperty("user.home") + File.separator + ".keystore"; FileInputStream fis = new FileInputStream(fname); ks.load(fis, null); if (ks.isKeyEntry(args[0])) { System.out.println(args[0] + " is a key entry in the keystore"); char c[] = new char[args[1].length()]; args[1].getChars(0, c.length, c, 0); System.out.println("The private key for" + args[0] + " is " + ks.getKey(args[0], c)); Certificate certs[] = ks.getCertificateChain(args[0]); if (certs[0] instanceof X509Certificate) { X509Certificate x509 = (X509Certificate) certs[0]; System.out.println(args[0] + " is really " + x509.getSubjectDN()); } if (certs[certs.length - 1] instanceof X509Certificate) { X509Certificate x509 = (X509Certificate) certs[certs.length - 1]; System.out.println(args[0] + " was verified by " + x509.getIssuerDN()); } } else if (ks.isCertificateEntry(args[0])) { System.out.println(args[0] + " is a certificate entry in the keystore"); Certificate c = ks.getCertificate(args[0]); if (c instanceof X509Certificate) { X509Certificate x509 = (X509Certificate) c; System.out.println(args[0] + " is really " + x509.getSubjectDN()); System.out.println(args[0] + " was verified by " + x509.getIssuerDN()); } } else { System.out.println(args[0] + " is unknown to this keystore"); } } catch (Exception e) { e.printStackTrace(); } } } This program expects two arguments: the name of the entity in the keystore for which information is desired, and the password that was used to encrypt the private key. There are a number of points to pick out from this example. First, note that we constructed the keystore using the convention we mentioned earlier--the .keystore file in the user's home directory. After we've read in the data, the first thing we do is determine if the entry that we're interested in is a key entry or a certificate entry--mostly so that we can handle the certificates for these entries differently. In the case of a key entry, we obtain the entire certificate chain, and use the first entry in that chain to print out the Distinguished Name (DN) for the entry, while the last entry in the chain is used to print out the DN for the last certificate authority in the chain. For a certificate entry, our task is simpler: there is a single certificate, and we simply print out its information. Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|