Using block modes and initialisation vectors in Java
Now we've discussed the theory of and
initialisation vectors (IVs), we're ready
to put them into practice in Java. Remember Alice and Bob's problem of
sending encrypted data to one another? Well, now we
want to set an appropriate block mode in Java to prevent it from leaking information.
First, let's look at Alice's code. Before, Alice constructed her Cipher
object very simply, as follows:
Cipher c = Cipher.getInstance("AES");
This gave her a cipher in ECB mode, where every block is encrypted separately.
To create a cipher operating with a different mode, she specifies the desired block
mode as follows:
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
Notice the extended format of the cipher name: AES is still the name of the
encryption algorithm; CBC is the block mode (CTR and OFB are also supported);
PKCS5PADDING specifies the padding— that is,
how to make the data up to a multiple of the block size if it otherwise wouldn't
be. Padding is not terribly exciting; suffice it to say for now that if you're
not sure, use PKCS5PADDING.
Now, Alice proceeds to encrypt her data as before:
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(dataToSend);
But now, before sending the encrypted data, she needs to send the IV to Bob,
so that he can use it to construct his Cipher. Notice that Alice didn't
actually specify an initialisation vector. In general:
If you don't specify an initialisation vector, and one is required
with the block mode you're using, then a random IV will be allocated.
Alice didn't specify the IV in this case because a random one is good enough in this
case. However, she does need to communicate it to Bob. So she calls
getIV() to ask the Cipher which IV was allocated to it.
Then she sends the IV and encrypted data across the network:
byte[] iv = c.getIV();
// send IV to Bob
// send encryptedData to Bob
Initialising the Cipher with a specific IV
On Bob's side of the conversation, he reads in the (unencrypted) IV that
was sent by Alice at the start of the data stream. Then, he needs to initialise his
Cipher with that particular IV. This is done by specifying an IvParameterSpec:
byte[] iv = // read from stream sent by Alice
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
c.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(iv));
Now, Bob proceeds as before, happy in the knowledge that his Cipher is
synchronized with Alice's.
If Alice wants to initialise her Cipher with a specific IV—
in effect dictating the IV for the conversation— then she can decide on the
IV and then initialise her Cipher with an IvParameterSpec just as
Bob does. Of course, Alice still needs to send the IV to Bob, or else, Bob needs
to know the underlying parameters (such as message number) that Alice used, so that
Bob can also reconstruct the same IV.
Next: asymmetric encryption
On the next pages, we introduce asymmetric encryption,
a technique which allows us to transmit a key securely from client to server (or between
two parties generally). We consider in more detail the RSA encryption
scheme, the most common and easiest to use in Java.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.