Figured I should probably provide a full java crypto implementation for AES, given my recent ignite-style presentation.

Here’s an (untested) implementation of AES in Java using the vendor-provided algorithms.

I’ll leave it as an excercise to the reader to modify this to support different cryptography and padding algorithms, as well as any magic number support.

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptDecryptHandler {

	private final byte[] key;
	private final String algorithm = "AES";
	private final String transform = "AES/CBC/PKCS5Padding";

	private final SecureRandom rng = new SecureRandom();

	private final int blockSize;

	public EncryptDecryptHandler(byte[] key) 
			throws NoSuchAlgorithmException, 
			NoSuchPaddingException {

		// should be 16, 24, or 32 bytes
		switch (key.length) {
			case 16:
			case 24:
			case 32:
				this.key = key;
				break;
			default:
				throw new UnsupportedOperationException
						("Key size unsupported or insecure");
		}

		blockSize = Cipher.getInstance(transform).getBlockSize();
	}

	public byte[] encrypt(byte[] data)
			throws IllegalBlockSizeException,
			BadPaddingException,
			InvalidKeyException,
			InvalidAlgorithmParameterException,
			NoSuchAlgorithmException,
			NoSuchPaddingException {

		Cipher encryptCipher = getEncryptCipher();

		byte[] encrypted = encryptCipher.doFinal(data);
		byte[] iv = encryptCipher.getIV();
		byte[] result = new byte[encryptCipher.getIV().length + data.length];

		System.arraycopy(iv, 0, result, 0, iv.length);
		System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);

		return result;
	}

	public byte[] decrypt(byte[] data)
			throws IllegalBlockSizeException,
			BadPaddingException,
			InvalidKeyException,
			InvalidAlgorithmParameterException,
			NoSuchAlgorithmException,
			NoSuchPaddingException {

		byte[] iv = new byte[blockSize];
		System.arraycopy(data, 0, iv, 0, iv.length);

		Cipher decryptCipher = getDecryptCipher(iv);

		byte[] encrypted = new byte[data.length - iv.length];
		System.arraycopy(data, iv.length, encrypted, 0, encrypted.length);

		byte[] result = decryptCipher.doFinal(encrypted);

		return result;
	}

	private Cipher getEncryptCipher()
			throws InvalidKeyException,
			InvalidAlgorithmParameterException,
			NoSuchAlgorithmException,
			NoSuchPaddingException {

		Cipher result = Cipher.getInstance(transform);
		byte[] iv = new byte[blockSize];
		rng.nextBytes(iv);

		SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
		AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);

		result.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec);

		return result;

	}

	private Cipher getDecryptCipher(byte[] iv)
			throws InvalidKeyException,
			InvalidAlgorithmParameterException,
			NoSuchAlgorithmException,
			NoSuchPaddingException {

		Cipher result = Cipher.getInstance(transform);

		SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
		AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);

		result.init(Cipher.DECRYPT_MODE, keySpec, paramSpec);

		return result;
	}

}

Just remember: if you are a U.S. citizen and you use a key larger than 16 bytes, you cannot export that software outside the country without express permission of U.S. Customs.

Have fun!