I've done some reading about implementing AES256 and deriving a key from a password. If I understand correctly:
- I want to generate a new salt (for the key) and a new IV (for the encrypted message) for every new message.
- It should also not be a problem sending the salt and the IV together with the message.
I decided to pack everything together in one byte array where the first 16 bytes is the salt, the next 16 bytes is the IV and the rest is the encrypted message. This with a key length of 256 bits and 20000 iterations for generating the key. I also encode the whole thing in Base64 for transmission.
Can this approach be improved? Knowing that I'm limited to 256 bytes for the complete message (salt+iv+message).
public class app {
public static void main(String[] args) throws Exception {
int iterations = 20000;
int keyLength = 32;
byte[] salt = getRandomBytes(16);
byte[] iv = getRandomBytes(16);
char[] password = "password_here".toCharArray();
byte[] payload = "payload_here".getBytes();
byte[] key = deriveKey(iterations, keyLength, salt, iv, password);
byte[] encrypted = encrypt(iv, key, payload);
byte[] bytes = concatBytes(salt, iv, encrypted);
String output = Base64.encodeBytes(bytes);
int outputLength = output.getBytes().length;
System.out.println(outputLength + "\n" + output);
}
private static byte[] deriveKey(int iterations, int length, byte[] salt, byte[] iv, char[] password)
throws InvalidKeySpecException, NoSuchAlgorithmException {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, length * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] key = skf.generateSecret(spec).getEncoded();
return key;
}
private static byte[] getRandomBytes(int length) throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[length];
sr.nextBytes(salt);
return salt;
}
private static byte[] concatBytes(byte[]... arrays) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (byte[] array : arrays)
outputStream.write(array);
return outputStream.toByteArray();
}
private static byte[] encrypt(byte[] iv, byte[] key, byte[] text) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec newKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return cipher.doFinal(text);
}
}