Symmetric Encryption
83
FileInputStream input = new FileInputStream("plaintext_filename");
FileOutputStream output = new FileOutputStream("ciphertext_filename");
CipherOutputStream cipherOutput = new CipherOutputStream(output, cipher);
int r = 0;
while (r=input.read() != -1) {
cipherOutput.write(r);
}
cipherOutput.close();
output.close();
input.close();
Decrypting is similar to this we simply place the cipher in DECRYPT_MODE, and use the cipher streams in
the same fashion.
When using cipher streams, it is important to set the mode properly. ECB mode, as we mentioned, isn't a
good idea for large chunks of data that are non-random, like a text file. Instead, we'll need to use a mode like
CBC, which requires an initialization vector. We'll show setting the mode in the code example coming up.
Initializing a Cipher with an IV
javax.crypto.spec.IVParameterSpec encapsulates an initialization vector in the JCE. One is
constructed with a byte array and a call to new() for IVParameterSpec.
To create the bytes for the IV, we should use java.security.SecureRandom to generate a byte array
equivalent to the block size for the cipher we are using. Most block ciphers have a block size of 64 bits (8
bytes). AES has a variable block size, either 128, 192, or 256 bits, but is typically set to 128-bit (16 bytes).
SecureRandom
SecureRandom is easy to use. We can construct one normally, with the default constructor, and then pass
byte arrays to it with nextBytes() and receive them back filled with random data. For instance, to create
an 8-byte array using secure random we could do the following:
byte[] randomBytes = new byte[8];
SecureRandom random = new SecureRandom();
random.nextBytes(randomBytes);
As an aside, you may have noticed a delay during the running of the examples we have seen so far and
wondered what causes it. Well, it's not the encryption that's so slow; it's the initialization of
java.security.SecureRandom. Sun's implementation of SecureRandom creates a great number of
threads and checks their interaction over a period of time. It then uses that to initialize a Pseudo-Random
Number Generator (PRNG). It's the thread creation and interaction that takes so long.
To combat this, a couple of providers have created implementations of SecureRandom that are at least
partially native, giving a huge speed increase in the seeding of the random number generator. Cryptix
(http://www.cryptix.org) provides an implementation that runs on Linux and the various BSDs, and Virtual
Unlimited (http://www.virtualunlimited.com) provides one for Windows.
Also, the initialization of SecureRandom is only expensive the first time it's done in a single Java VM
instance. If you have a long-running program or a server, you can initialize SecureRandom on startup and
all later operations will run quite quickly. You may also want to try to architect your programs so that they
run for a longer period of time rather than starting and stopping often. This would allow you to amortize
the cost of initialization.