Principals The KeyStore Class

import java.security.cert.; public class KeyStoreHandler { KeyStore ks; private char[] pw; Well use this to look up the keystore in the default location. You can specify a password if you like, but this will also work if you pass null in which case the keystore isnt verified. public KeyStoreHandlerchar[] pw { Make a private copy so the original can be collected so that other objects cant locate it. if pw = null { this.pw = new char[pw.length]; System.arraycopypw, 0, this.pw, 0, pw.length; } else this.pw = null; Load from the default location try { ks = KeyStore.getInstanceKeyStore.getDefaultType ; String fname = System.getPropertyuser.home + File.separator + .keystore; FileInputStream fis = new FileInputStreamfname; ks.loadfis, pw; } catch Exception e { throw new IllegalArgumentExceptione.toString ; } } public KeyStore getKeyStore { return ks; } Store to the default location public void store throws FileNotFoundException, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { If we didnt read with a password, we cant store if pw == null { throw new IllegalArgumentExceptionCant store wo pw; } FileOutputStream fos = new FileOutputStream System.getPropertyuser.home + File.separator + .keystore; ks.storefos, pw; fos.close ; } public static void mainString args[] { try { KeyStore ks = new KeyStoreHandlernull.getKeyStore ; if ks.isKeyEntryargs[0] { System.out.printlnargs[0] + is a key entry in the keystore; char c[] = new char[args[1].length ]; args[1].getChars0, c.length, c, 0; System.out.printlnThe private key for + args[0] + is + ks.getKeyargs[0], c; java.security.cert.Certificate certs[] = ks.getCertificateChainargs[0]; if certs[0] instanceof X509Certificate { X509Certificate x509 = X509Certificate certs[0]; System.out.printlnargs[0] + is really + x509.getSubjectDN ; } if certs[certs.length − 1] instanceof X509Certificate { X509Certificate x509 = X509Certificate certs[certs.length − 1]; System.out.printlnargs[0] + was verified by + x509.getIssuerDN ; } } else if ks.isCertificateEntryargs[0] { System.out.printlnargs[0] + is a certificate entry in the keystore; java.security.cert.Certificate c = ks.getCertificateargs[0]; if c instanceof X509Certificate { X509Certificate x509 = X509Certificate c; System.out.printlnargs[0] + is really + x509.getSubjectDN ; System.out.printlnargs[0] + was verified by + x509.getIssuerDN ; } } else { System.out.printlnargs[0] + is unknown to this keystore; } } catch Exception e { e.printStackTrace ; } } } Well use this class in the rest of the book to manage the default keystore. Its main method for testing 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 users home directory. After weve read in the data, the first thing we do is determine if the entry that were 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 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.

10.4 A Key Management Example

Now well proceed to a framework for enterprise−wide key management. Figure 10−1 shows the role of the keystore in the creation and execution of a signed jar file. The jarsigner utility consults the keystore 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 the Java Plug−in. When the jar file is read on the remote system, the keystore is consulted in order to retrieve the public key of the entity that signed the jar file so that the jar files signature can be verified. Figure 10−1. the keytool database in a signed JAR file Note that the two keystores in this example are probably separate files on separate machines. They probably have completely different entries as well −− even for the entry that represents the signer. The signers entry in her own database must have the private key of the signer while the signers entry in the users database needs only a certificate for the signer. However, the keystore could in this and all examples be a shared database. Since access to the private key of the signer is protected by a password, the signer and the end user are able to share a single database without concern that the end user may obtain access to the signers private key assuming that she keeps her password secret, of course. In the case of a corporate network, this flexibility is important since an enterprise may want to maintain a single database that contains the private keys of all of its employees as well as the certificates of all known external entities. We could have these users share the keystore by using the appropriate filename in the application and the java.policy files. But sharing the keystore by a file is somewhat inefficient. If the global file is on a machine in New York and is referenced by a user in Tokyo, you will want to use a better network protocol to access it than a file−based protocol. In addition, the load method reads in the entire file. If there are 10,000 users in your corporate keystore database, you shouldnt need to read each entry into memory to find the one entry you are interested in using. Hence, for many applications, youll want to provide your own implementation of the KeyStore class. Well show a very simple example here as a starting point for your own implementations. For the payroll application being deployed by XYZ Corporation, a database containing each employee in the corporation is necessary. The HR department could set up its own keystore for this purpose, but a similar keystore will be needed by the finance department to implement its 401K application; a better solution is to have a single keystore that is shared by all departments of XYZ Corporation. In this case, the question becomes how best to share this keystore. A single global file would be too large for programs to read into memory and too unwieldy for administrators to distribute to all locations of XYZ Corporation. A better architecture is shown in Figure 10−2. Here, the application uses the security provider architecture to instantiate a new keystore object of a class that well sketch out below. Unknown to the users of this object, the keystore class uses RMI or CORBA, or any other distributed computing protocol to talk to a remote server, which accesses the 10,000 employee records from a database set up for that purpose. Figure 10−2. A distributed keystore example Without getting bogged down in the details of the network and database programming required for this architecture, lets look at how the KeyStore class itself would be designed. Implementing a keystore requires that we write a KeyStoreSpi class, just as with any engine class. For most methods in the KeyStore class, there is a corresponding abstract engine method in the KeyStoreSpi class that you must provide an implementation for. A complete list of these methods is given in Table 10−1. Table 10−1. Engine Methods in the KeyStoreSpi Class KeyStore Class KeyStoreSpi class aliases engineAliases containsAlias engineContainsAlias deleteEntry engineDeleteEntry getCertificate engineGetCertificate getCertificateAlias engineGetCertificateAlias getCertificateChain engineGetCertificateChain getCreationDate engineGetCreationDate getKey engineGetKey isCertificateEntry engineIsCertificateEntry isKeyEntry engineIsKeyEntry load engineLoad setCertificateEntry engineSetCertificateEntry setKeyEntry engineSetKeyEntry size engineSize store engineStore Many of the methods of our new class are simple passthroughs to the remote server. If the handle to the remote server is held in the instance variable rks , a typical method looks like this: public Date engineGetCreationDateString alias { return rks.getCreationDatealias; } The methods that could be implemented in this manner are: engineGetKey engineGetCertificateChain engineGetCertificate engineGetCreationDate engineAliases engineContainsAlias engineSize engineIsKeyEntry engineIsCertificateEntry engineGetCertificateAlias On the other hand, many methods should probably throw an exception −− especially those methods that are designed to alter the keystore. In an architecture such as this one, changes to the keystore should probably be done through the database itself −− or at least through a different server than the server used by all employees in the corporation. Many functions may look simply like this: public void engineSetKeyEntryString alias, Key key, char[] passphrase, Certificate chain[] throws KeyStoreException { throw new KeyStoreExceptionCant change the keystore; } Methods that could be implemented in this manner are: engineSetKeyEntry engineSetCertificateEntry engineDeleteEntry engineStore Note that we did not include the engineLoad method in the above list. The engineLoad method is useful to us because it allows the application to require a password from the user before a connection to the remote server can be made. This differs slightly from normal programming for this class. Typically, the engineLoad method is called with the input stream from which to read the keystore. In this case, the engineLoad method is expected to be called with a null input stream and sets up the connection to the remote server itself: public void engineLoadInputStream is, char[] password throws IOException, NoSuchAlgorithmException, CertificateException { RemoteKeyServer rks = RemoteKeyServer Naming.lookuprmi:KSServerDistributedKeyServer; if rks.authenticatepassword { rks = null; throw new IOExceptionIncorrect password; } } Since the keystore database in this architecture cannot be written through the server, there is some question as to whether a password should be required to access the keystore at all since there are individual passwords on the private keys. Every employee will potentially have access to the password unless it is embedded into the application itself; you can decide if a password really adds security in that case. If no password is desired, the engineLoad method could be empty and the connection to the remote server could be made in the constructor. On the server side, implementation of the required methods is simply a matter of making appropriate database calls: public int engineSize { int sz = −1; try { Connection conn = connectToDatabase ; Statement st = conn.createStatement ; boolean restype = st.executeselect count from entries; if restype { ResultSet rs = st.getResultSet ; sz = Integer.parseIntrs.getString1; } st.cancel ; } catch Exception e {