5.16. Cryptography
The java.security
package includes cryptography-based classes, but it does not contain
classes for actual encryption and
decryption. That is the job of the javax.crypto
package. This package supports symmetric-key cryptography, in which
the same key is used for both encryption and decryption and must be
known by both the sender and the receiver of encrypted data.
5.16.1. Secret Keys
The SecretKey
interface represents an encryption key; the first step of any
cryptographic operation is to obtain an appropriate
SecretKey. Unfortunately, the
keytool program
supplied with the JDK cannot generate and store secret keys, so a
program must handle these tasks itself. Here is some code that shows
various ways to work with SecretKey objects:
import javax.crypto.*;
import javax.crypto.spec.*;
// Generate encryption keys with a KeyGenerator object
KeyGenerator desGen = KeyGenerator.getInstance("DES"); // DES algorithm
SecretKey desKey = desGen.generateKey(); // Generate a key
KeyGenerator desEdeGen = KeyGenerator.getInstance("DESede"); // Triple DES
SecretKey desEdeKey = desEdeGen.generateKey(); // Generate a key
// SecretKey is an opaque representation of a key. Use SecretKeyFactory to
// convert to a transparent representation that can be manipulated: saved
// to a file, securely transmitted to a receiving party, etc.
SecretKeyFactory desFactory = SecretKeyFactory.getInstance("DES");
DESKeySpec desSpec = (DESKeySpec)
desFactory.getKeySpec(desKey, javax.crypto.spec.DESKeySpec.class);
byte[] rawDesKey = desSpec.getKey();
// Do the same for a DESede key
SecretKeyFactory desEdeFactory = SecretKeyFactory.getInstance("DESede");
DESedeKeySpec desEdeSpec = (DESedeKeySpec)
desEdeFactory.getKeySpec(desEdeKey, javax.crypto.spec.DESedeKeySpec.class);
byte[] rawDesEdeKey = desEdeSpec.getKey();
// Convert the raw bytes of a key back to a SecretKey object
DESedeKeySpec keyspec = new DESedeKeySpec(rawDesEdeKey);
SecretKey k = desEdeFactory.generateSecret(keyspec);
// For DES and DESede keys, there is an even easier way to create keys
// SecretKeySpec implements SecretKey, so use it to represent these keys
byte[] desKeyData = new byte[8]; // Read 8 bytes of data from a file
byte[] tripleDesKeyData = new byte[24]; // Read 24 bytes of data from a file
SecretKey myDesKey = new SecretKeySpec(desKeyData, "DES");
SecretKey myTripleDesKey = new SecretKeySpec(tripleDesKeyData, "DESede");
5.16.2. Encryption and Decryption with Cipher
Once you have
obtained an appropriate SecretKey object, the
central class for encryption and decryption is
Cipher. Use it like this:
SecretKey key; // Obtain a SecretKey as shown earlier
byte[] plaintext; // The data to encrypt; initialized elsewhere
// Obtain an object to perform encryption or decryption
Cipher cipher = Cipher.getInstance("DESede"); // Triple-DES encryption
// Initialize the cipher object for encryption
cipher.init(Cipher.ENCRYPT_MODE, key);
// Now encrypt data
byte[] ciphertext = cipher.doFinal(plaintext);
// If we had multiple chunks of data to encrypt, we can do this
cipher.update(message1);
cipher.update(message2);
byte[] ciphertext = cipher.doFinal();
// We simply reverse things to decrypt
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedMessage = cipher.doFinal(ciphertext);
// To decrypt multiple chunks of data
byte[] decrypted1 = cipher.update(ciphertext1);
byte[] decrypted2 = cipher.update(ciphertext2);
byte[] decrypted3 = cipher.doFinal(ciphertext3);
5.16.3. Encrypting and Decrypting Streams
The Cipher
class can also be used with
CipherInputStream or
CipherOutputStream to encrypt or decrypt while
reading or writing streaming data:
byte[] data; // The data to encrypt
SecretKey key; // Initialize as shown earlier
Cipher c = Cipher.getInstance("DESede"); // The object to perform encryption
c.init(Cipher.ENCRYPT_MODE, key); // Initialize it
// Create a stream to write bytes to a file
FileOutputStream fos = new FileOutputStream("encrypted.data");
// Create a stream that encrypts bytes before sending them to that stream
// See also CipherInputStream to encrypt or decrypt while reading bytes
CipherOutputStream cos = new CipherOutputStream(fos, c);
cos.write(data); // Encrypt and write the data to the file
cos.close(); // Always remember to close streams
java.util.Arrays.fill(data, (byte)0); // Erase the unencrypted data
5.16.4. Encrypted Objects
Finally, the
javax.crypto.SealedObject class provides an
especially easy way to perform encryption. This class
serializes a specified object and
encrypts the resulting stream of bytes. The
SealedObject can then be serialized itself and
transmitted to a recipient. The recipient can retrieve the original
object only if she knows the required
SecretKey:
Serializable o; // The object to be encrypted; must be Serializable
SecretKey key; // The key to encrypt it with
Cipher c = Cipher.getInstance("Blowfish"); // Object to perform encryption
c.init(Cipher.ENCRYPT_MODE, key); // Initialize it with the key
SealedObject so = new SealedObject(o, c); // Create the sealed object
// Object so is a wrapper around an encrypted form of the original object o;
// it can now be serialized and transmitted to another party.
// Here's how the recipient decrypts the original object
Object original = so.getObject(key); // Must use the same SecretKey
|