I am trying to use Java's cryptography library to implement AES encryption using GCM mode (because it's an AEAD scheme and I'd like to make sure the data arrives intact) but I'd like to use a password for user friendliness. Getting the program to compile without errors was an difficult until I slammed my head against a brick wall a few times and read this other stackoverflow response.
Now, unfortunately the program compiles but the ciphertext it returns is a fraction of the amount of bytes in the message (I suspect it's only the header that GCM uses and the actual message isn't being written at all). Can somebody please lend a hand?
Here are some program variables:
private static final byte[] SALT = {(byte)0x3b,(byte)0x12,(byte)0x44,(byte)0x61,
(byte)0xec,(byte)0xc0,(byte)0xa1,(byte)0x82,
(byte)0x4d, (byte)0x97, (byte)0x4d, (byte)0x3c,
(byte)0x57, (byte)0xd9, (byte)0x94, (byte)0x52};
private static final String CIPHER_ALG = "AES/GCM/PKCS5Padding";
private static final String SEC_RANDOM_ALG = "SHA1PRNG";
private static final String S_KEY_FACTORY_ALG = "PBKDF2WithHmacSHA1";
private static final int AES_KEY_SIZE = 128; //num bits
private static final int GCM_NONCE_LENGTH = 16; //num bytes
private static final int GCM_TAG_LENGTH = 16; //num bytes
private static final int KEY_SPEC_ROUNDS = 65536;
And here is the encrypt method:
//FIXME somehow the cipher is truncating the message or something
private byte[] encrypt(File file, String password) throws IOException{
Path path = Paths.get(file.toURI());
byte[] messageBytes = Files.readAllBytes(path);
//Message bytes: 3253
System.out.printf("Message bytes: %d%n", messageBytes.length);
byte[] messageName = path.getFileName().toString().getBytes();
byte[] cipherBytes = {};
byte[] iv = {};
try{
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(S_KEY_FACTORY_ALG);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), SALT, KEY_SPEC_ROUNDS, AES_KEY_SIZE);
SecretKey key = keyFactory.generateSecret(pbeKeySpec);
System.out.printf("Secret key take 1: " + Arrays.toString(key.getEncoded()) + "%n");
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");
//FIXME this is not encrypting the message correctly
Cipher cipher = Cipher.getInstance(CIPHER_ALG);
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
GCMParameterSpec paramSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
iv = paramSpec.getIV();
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);
cipher.updateAAD(messageName);
cipher.update(messageBytes);
cipherBytes = cipher.doFinal();
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}catch(NoSuchPaddingException e){
e.printStackTrace();
}catch(InvalidKeyException e){
e.printStackTrace();
}catch(InvalidAlgorithmParameterException e){
e.printStackTrace();
}catch(InvalidKeySpecException e){
e.printStackTrace();
}catch(BadPaddingException e){
e.printStackTrace();
}catch(IllegalBlockSizeException e){
e.printStackTrace();
}
//IV bytes: 16
System.out.printf("IV bytes: %d%n", iv.length);
//Cipher bytes: 21
System.out.printf("Cipher bytes: %d%n", cipherBytes.length);
//Cipher bytes: [121, 68, 7, -69, -35, -9, -83, 101, -60, -80, 42, 59, -67, 126, 18, -82, 79, -60, 34, -125, 12]
System.out.printf("Cipher bytes: " + Arrays.toString(cipherBytes) + "%n");
//FIXME somehow the cipher is shortening the message or something
return cipherBytes;
}