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.