Key Management

The JCA/JCE framework manages keys in two forms, a provider-dependent format and a provider-independent format.

The provider-dependent keys will implement either the java.security.Key interface (or one of its subclasses) for public-key algorithms, or the javax.crypto.SecretKey interface for secret-key algorithms. Provider keys can be generated randomly, via a key agreement algorithm or from their associated provider-independent format.

The provider-independent formats will implement the java.security.spec.KeySpec interface. Subclasses of this type exist for both specific key types and for different encoding types. For example, the java.security.spec.RSAPublicKeySpec can be used to construct an RSA public key from its modulus and exponent and a java.security.spec.PKCS8EncodedKeySpec can be used to construct a private key encoded using PKCS#8.

Each Provider will supply a number of mechanisms that will create the provider-dependent keys or convert the provider-independent keys into provider based keys.

This section contains information on the following:

>Generating Random Keys

>Key Conversion

>Key Agreement Protocols

>Key Storage

>Certificates

Generating Random Keys

The simplest mechanism to create keys for a given provider is to use their random key generators. Random keys are most often generated for use as "session-keys", used for a given dialogue or session and then no longer required. In the case of public-key algorithms, however, they may be generated once and then stored for later use. The JCE framework provides key generation via the following classes:

javax.crypto.KeyGenerator

Generation of symmetric keys (such as DES, IDEA, RC4)

java.security.KeyPairGenerator

Generation of public/private key pairs (such as RSA)

For instance, to create a random 128-bit key for RC4 and initialize a Cipher for encryption with this key:

   /*
    * Create the key generator for the desired algorithm,
    * and then initialize it for the required key size.
    */
   KeyGenerator rc4KeyGen = KeyGenerator.getInstance("RC4");
   rc4KenGen.init(128);

   /*
    * Generate the key and then initialize the Cipher
    */
   SecretKey rc4Key = rc4KeyGen.generateKey();
   Cipher rc4Cipher = Cipher.getInstance("RC4");
   rc4Cipher.init(Cipher.ENCRYPT_MODE, rc4Key);  

Here, the SecretKey returned by the KeyGenerator.generateKey() method is a provider-dependent key. The returned key can then be used with that provider's algorithms.

Some algorithms have keys that are considered weak, for example with a weak DES key the ciphertext may be the same as the plaintext! Generally, the KeyGenerator will not generate those keys, but it is best to check the provider documentation for details on the specific algorithm.

The code to generate a public/private key pair is quite similar:

   KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA");
   rsaKeyGen.initialize(1024);

   KeyPair rsaKeyPair = rsaKeyGen.generateKeyPair();
   Cipher rsaCipher = Cipher.getInstance("RSA");
   rsaCipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPrivate());

Key Conversion

Two interfaces are provided to convert between a provider-dependent Key and the provider-independent KeySpec: java.security.KeyFactory and javax.crypto.SecretKeyFactory. The KeyFactory class is used for public-key algorithms and the SecretKeyFactory class for secret-key algorithms.

An application may choose to store its keys in some way and then recreate the key using a KeySpec. For example, the application may contain an embedded RSA public key as two integers; the RSAPublicKeySpec along with a KeyFactory that can process RSAPublicKeySpec instances could then be used to create the provider-dependent key.

Each provider will generally supply a number of KeyFactory/SecretKeyFactory classes that will accept the various KeySpec classes and produce Key instances that may be used with the provider algorithms. These factories are not likely to support all KeySpec types, so the provider documentation should provide the details as to what conversions will be accepted.

There are a number of KeySpec classes provided by the JCA/JCE:

java.security.spec
PKCS8EncodedKeySpec A DER encoding of a private key according to the format specified in the PKCS#8 standard.
X509EncodedKeySpec A DER encoding of a public or private key, according to the format specified in the X.509 standard.
RSAPublicKeySpec An RSA public key
RSAPrivateKeySpec An RSA private key
RSAPrivateCrtKeySpec An RSA private key, with the Chinese Remainder Theorem (CRT) values
DSAPublicKeySpec A DSA public key
DSAPrivateKeySpec A DSA private key

 

javax.crypto.spec
DESKeySpec A DES secret key
DESedeKeySpec A DESede secret key
PBEKeySpec A user-chosen password that can be used with password base encryption (PBE)
SecretKeySpec A key that can be represented as a byte array and have no associated parameters. The encoding type is known as RAW.

To convert a KeySpec instance into a provider based Key, firstly create a KeyFactory or SecretKeyFactory of the appropriate type using the getInstance() method. Once the instance has been created, use the KeyFactory.generatePrivate(), KeyFactory.generatePublic() or SecretKeyFactory.generateSecret() method.

In the following example we will create a Key from a KeySpec and then recover the KeySpec from the Key.

   /*
    * This is the raw key
    */
   byte[] keyBytes = { (byte)0x1, (byte)0x02, (byte)0x03,
      (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08 };

   /*
    * Create the provider independent KeySpec
    */
   DESKeySpec desKeySpec = new DESKeySpec(keyBytes);

   /*
    * Create the KeyFactory to do the Key<->KeySpec translation
    */
   SecretKeyFactory keyFact = KeyFactory.getInstance("DES");

   /*
    * Create the provider based SecretKey
    */
   SecretKey desKey = keyFact.generateSecret(desKeySpec);

   /*
    * Convert the provider Key into a generic KeySpec
    */
   DESKeySpec desKeySpec2 = keyFact.getKeySpec(desKey,
      DESKeySpec.class);

Key Agreement Protocols

Keys may also be generated using the javax.crypto.KeyAgreement API. This interface provides the functionality of a key agreement (or key exchange) protocol. For example, a Diffie-Hellman KeyAgreement instance would allow two or more parties to generate a shared Diffie-Hellman Key.

To generate the key, it is necessary to call KeyAgreement.doPhase() for each party in the exchange with a Key object that represents the current state of the key agreement. The last call to KeyAgreement.doPhase() should have the lastPhase set to true.

Once all the key agreement phases have been processed, the shared SecretKey may be generated by calling the KeyAgreement.generateSecret() method.

The KeyAgreement API does not define how each of the parties communicates the necessary information for each exchange in the protocol. The required information is passed to the KeyAgreement.doPhase() method as a Key. This Key will generally be generated using either a KeyGenerator or a KeyFactory. The provider documentation will detail the specific steps required for a given protocol.

   /*
    * Create the KeyAgreement instance for the required
    * protocol and initialize it with our key.  In the
    * case of Diffie-Hellman this would be our private
    * key.
    */
   KeyAgreement keyAg = KeyAgreement.getInstance("DH");
   keyAg.init(ourKey);

   /*
    * Exchange information as per the key exchange
    * protocol.  For DH we would exchange public keys.
    * Note since there is only two parties in this
    * case the return value is not relevant.
    */
   keyAg.doPhase(remotePubKey, true);

   /*
    * Create the shared secret-key
    */
   SecretKey key = keyAg.generateSecret("DES");

Key Storage

Once a Key has been generated you may wish to store it for future use. Generally, you'll be saving public/private keys so that you can reuse them at a later date in a key exchange.

The java.security.KeyStore API provides one mechanism for management of a number of keys and certificates. There are two types of entries in a KeyStore: Key entries and Certificate entries. Key entries are sensitive information, whereas certificates are not.

As Key entries are sensitive, they are therefore are protected by the KeyStore. The API allows for a password, or pass phrase, to be attached to each key entry. What the actual implementation does with the password is not defined, although it may be used to encipher the entry. A key entry may either be a SecretKey, or a PrivateKey. In the case of a PrivateKey, the entry is saved along with a Certificate chain, which is the chain of trust. The chain of trust starts with the Certificate containing the corresponding PublicKey and ends with a self-signed certificate.

A certificate entry represents a "trusted certificate entry", that is, a Certificate whose identity we trust. This type of entry can be used to authenticate other parties.

To create a KeyStore instance, use the KeyStore.getInstance() method. This will return an empty KeyStore which may then be populated by calling the KeyStore.load() method. This method accepts an InputStream instance and an optional password. Each individual KeyStore will treat these parameters differently, so check the provider documentation for details.

The Sun provider supplies a KeyStore known as "JKS". This KeyStore is used by the keytool and jarsigner applications.

   /*
    * Create an instance of the Java Key Store (defined by Sun)
    */
   KeyStore keyStore = KeyStore.getInstance("JKS");  

To add a new entry into the KeyStore, use either setCertificateEntry() or one of the setKeyEntry() methods. This will add the new entry with the associated alias.

   char[] myPass;
   SecretKey secretKey;

   /*
    * Store a SecretKey in the KeyStore, with "mypass"
    * as the password.
    */
   keyStore.setKeyEntry("mysecretkey", secretKey,
                                           myPass, null);

   /*
    * assume that privateKey contains my PrivateKey
    * and myCert contains a Certificate with the
    * corresponding PublicKey
    */
   PrivateKey privateKey;
   Certificate myCert;

   keyStore.setKeyEntry("myprivatekey", privateKey,
                                          myPass, myCert);  

To extract an entry, use the getKey() method to extract a Key or getCertificate() for a Certificate.

   /*
    * recover the SecretKey
    */
   SecretKey key = (SecretKey)keyStore.getKey("mysecretkey",
                                                     myPass);

   /*
    * recover the PrivateKey
    */
   PrivateKey privKey =
         (PrivateKey)keyStore.getKey("myprivatekey", myPass);

   /*
    * recover the Certificate (containing the PublicKey)
    * corresponding to our PrivateKey
    */
   Certificate cert = keyStore.getCertificate("myprivatekey");  

If the KeyStore supports persistence via the store() and load() methods, the provider documentation will explain what types of Key types may be stored.

Certificates

The JCA framework provides support for generic certificates, as well as X.509v3 certificates. Certificates may be stored using the KeyStore API, or they may be generated from their encoded format (either the PEM or PKCS#7 encoding).

To create a java.security.cert.Certificate instance from its encoded format, first create a java.security.cert.CertificateFactory instance of the required type (eg X.509). Then use the generateCertificate() or generateCertificates() methods to convert your InputStream into Certificate instances.

   CertificateFactory cf =
         CertificateFactory.getInstance("X.509");
   X509Certificate cert =
        (X509Certificate)cf.generateCertificate(inputStream);  

Two useful methods of the Certificate class are getPublicKey() and verify(). The first of these allows access to the PublicKey of the certificate's owner and the second allows an application to verify that the certificate was signed using the private key that corresponds to the provided public key.

The java.security.cert.X509Certificate class, which extends the Certificate class, provides methods to access the other attributes of a X.509 certificate such as the Issuer's distinguished name or its validity period.

The keytool application provided with JDK can be used to generate certificates and store them in a KeyStore. Check the JDK documentation for information on how to use this application.