The Java Cryptography Extension (JCE) supplies a uniform way for applications to use encryption and digital signatures in Java. The encryption classes (in the packages java.security and javax.crypto) provide a convenient way to perform encryption and digital signing. Methods are also available within the classes to support the storage and retrieval of keys.
This article will use a set of test classes to demonstrate one method of sharing key information. The focus will be on allowing sharing of key information with environments other than Java. Even within Java, once you have created a key for use in encryption, you will want to continue using the same key so that the consumers of the encrypted information can decrypt it.
JDK 1.4 includes the necessary providers for some of the most common cryptography algorithms. For information on installing providers with a JDK prior to 1.4, see “Master the basics of Java Cryptography Extension (JCE).”
Choose a format
To share key information with another platform or programming environment, you must first decide on a standard for exchanging the keys. Several standards are available that can be implemented with Java. The ASN.1 (Abstract Syntax Notation One) encoding is the default used within the Java classes responsible for keys. The Java Key interface (java.security.Key) specifies a method for retrieving the encoded version of the key (getEncoded()). This format can be written to the disk in a file and then used to read the key back into Java. Another option for the format of the key data is text. Depending on the nature of the algorithm used, this has to be customized, since different algorithms have different requirements. In the case of RSA (named for its creators, Ron Rivest, Adi Shamir, and Leonard Adleman), keys can be stored by saving the modulus and exponent of the key.
For a recent project, this method of storing the keys worked best since it could be represented in plain text and allowed any code that understood RSA encryption to create the key. Since this text representation did not require reading a particular format like ASN.1, it is lightweight and easy to manipulate. Any programming language capable of generating RSA-style keys can read the modulus and exponent to create the necessary key.
Create and store the keys
Generating the keys in Java is fairly straightforward. First, a KeyPairGenerator is instantiated with the desired algorithm and parameters. In this case, a 512-bit key is produced. Note that for the best security and speed, a 2,048-bit key is recommended, although there are some export issues with 2,048-bit keys for international applications. From the KeyPairGenerator, a KeyPair object is obtained, and from that, the individual PrivateKey and PublicKey objects can be obtained and cast to their respective RSA versions.
To store the key, the methods on the public key and private key objects can be used to obtain the modulus and exponent values for the keys. The modulus and exponent are returned as BigIntegers that can be output as a string with base 16 for the output (hex). Using the hex version of the key allows you to store it in a text file for later retrieval. In Listing A, the class GenerateRSAKeys performs these steps and writes the individual keys to files. This class writes both the ASN.1 encoding (as a binary file) and the text or hex version for this demonstration.
Read the keys
Since the key is now stored on disk, some amount of code is required to read the key data and transform it back into a Key object. The Key object is necessary to implement the functions for encryption, decryption, digital signing, and signature verification. Listing B shows a simple class that is instantiated by passing a filename containing the necessary hex dumps of the modulus and exponent. This class doesn’t care if it is a private or public key, since it supplies only the BigInteger version of the modulus and exponent for other classes to use in creating the appropriate PublicKey or PrivateKey objects.
Re-create the keys
Using the class that reads the keys, a program can now instantiate the appropriate key and then use that key for an encryption or digital signature operation. This example uses two classes. The first class digitally signs a file, and the second class takes the signature and the corresponding key to verify that the signature is for the supplied file.
In order to re-create a key, a specification object must be created using the modulus and exponent values. For a private key, an RSAPrivateKeySpec is used; for the public key, an RSAPublicKeySpec is used. The key specification object is fed into a KeyFactory that constructs the Key object. See Listing C and Listing D for the sample signing and verification programs. To create the signature, call the SignFile program with the private key generated by the creation program and a file that is to be signed. For instance, the command line java SignFile private_raw.txt data.txt would create the signature files rawsignature.sig and hexsignature.txt. To verify that the signature worked and that the public key can be read properly, use the VerifySignature class (java VerifySignature public_raw.txt data.txt rawsignature.sig) with the signature file created by the SignFile class.
Download the code presented in this article
Other alternatives for sharing keys
These classes demonstrate a method for writing RSA key data to disk and using that data to read and re-create the required keys. Other storage methods are available within the Java security classes. One alternative is to use the KeyStore, which stores keys in a secure password-protected file. Of course, using the KeyStore doesn’t enable the sharing of keys with other applications unless they too are written in Java. Another possibility for sharing the key data is to use the standard encoding mechanisms like X509 certificates. All of these methods are possible using the classes and interfaces available in the Java security and crypto packages.