While writing software dealing with cryptography, it’s important to be familiar with a concept of block cipher modes. Most of common encryption algorithms work on data grouped together into blocks. For instance, in case of DES cipher, the block size is equal to 64 bits. While encrypting larger amounts of data, cipher implementation will divide the input into 8 byte chunks and encrypt each of them separately.
This is definitely not desired – in case of specific input that contains repeatable sets of information (like text documents, bitmaps with large areas of the same color), the encrypted chunks might repeat just like original plain-text values do. As a result of that, encrypted data might reveal important details of the original content.
You might want to have a look at a very interesting article about this subject on (ahem) Wikipedia. It clearly describes, how block cipher modes solve the problem and additionaly demonstrates how it manifests itself in case of bitmaps. I wanted to play with various block cipher modes and in order to do so I wrote a simple tool that encrypts a user-provided BMP file while leaving its headers intact. As a result of that we are still able to view the encrypted bitmap file in image browser and observe how particular ciphers, key sizes and block modes influence the output. Below are some examples showing:
- raw image
- image encrypted with 56 bit DES using Electronic Codebook (ECB) mode
- image encrypted with 56 bit DES using Cipher-block Chaining (CBC) mode



In case of the second example I chose a rather high resolution (3923×4656) scan of an old document. What you see here is just a small cropped part of it, which is hopefully more than enough to observe the effect. I guess I could spare us all pictures representing the CBC mode since they are pretty noisy.



We can also look at this subject from slightly different perspective. The chart below shows how byte values are distributed in a file. Horizontal axis represents bytes from 0 to 255 obviously, while vertical axis stands for count of bytes of given value.
You can clearly see that in case of raw data as well as ECB mode some bytes show up more often than others. But if we look at the green line representing CBC mode – the distribution is very even across the whole scale which suggests that the encrypted data would contain less information valuable to a person willing to find out what is behind it.
The code to the BMP encrypter is below.
import java.io.*;
import java.security.GeneralSecurityException;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
public class BitmapPaddingTest {
public static void main(String[] args) throws Exception {
if (args.length < 4) {
throw new Exception("Parameters: [file] [cipher] [keySize] [useInitVector]");
}
new BitmapPaddingTest().encrypt(
new File(args[0]), args[1], Integer.parseInt(args[2]), Boolean.parseBoolean(args[3]));
}
public void encrypt(File file, String cipherName, int keySize, boolean useIv) throws Exception {
File encryptedFile = buildEncryptedFile(file, cipherName, keySize);
System.out.print("Encrypting " + file.getName() + " to " + encryptedFile.getName() + "... ");
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(encryptedFile));
InputStream fileStream = new BufferedInputStream(new FileInputStream(file));
byte[] data = new byte[54]; // first 54 bytes of BMP file contain its header (in most cases)
fileStream.read(data);
outputStream.write(data);
outputStream.flush();
CipherOutputStream cipherStream =
new CipherOutputStream(outputStream, createCipher(cipherName, keySize, useIv));
data = new byte[1024];
int size = 0;
while ((size = fileStream.read(data)) > 0) {
cipherStream.write(data, 0, size);
}
cipherStream.flush();
cipherStream.close();
System.out.println("done.");
}
private SecretKey createSymmetricKey(String cipherName, int keySize) throws GeneralSecurityException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(cipherName.substring(0, cipherName.indexOf("/")));
keyGenerator.init(keySize);
return keyGenerator.generateKey();
}
private Cipher createCipher(String cipherName, int keySize, boolean useIv) throws GeneralSecurityException {
SecretKey symmetricKey = createSymmetricKey(cipherName, keySize);
Cipher cipher = Cipher.getInstance(cipherName);
IvParameterSpec iv = useIv ? new IvParameterSpec(symmetricKey.getEncoded()) : null;
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, iv);
return cipher;
}
private File buildEncryptedFile(File file, String cipherName, int keySize) {
String cipherNameUnderscored = cipherName.replaceAll("/", "_");
return new File(file.getParentFile(),
file.getName().replace(".bmp", "_" + cipherNameUnderscored + "_" + keySize + ".bmp"));
}
}
If run as follows:
java BitmapPaddingTest source.bmp DES/ECB/PKCS5Padding 56 false
java BitmapPaddingTest source.bmp DES/CBC/PKCS5Padding 56 true
will generate two files located in the same directory as the source bitmap.

