I'm trying to encrypt a file using AES with a SHA-256 Key. When I generate the IVs I preppend it at the beggining of the encrypted file, and append the rest.
When I recover the IV's and compare it (the IV's after and then of the proces) they are the same. The problem is when I try to decrypt the file:
javax.crypto.BadPaddingException: Given final block not properly padded
I think it could be because i don't read properly the following bytes, but I revise the code and it seems Ok.
Crypto class:
private static String password;
private static String salt;
private static int pswdIterations = 65536 ;
private static int keySize = 256;
private byte[] ivBytes;
public void encryptOfFile(byte[] bytes, File out) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8");
System.out.println("Salt bfre:" +salt);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//encrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
//First copy the IVBytes at the beginning of the file
System.out.println("IvBytes Bfore: " + DatatypeConverter.printHexBinary(ivBytes));
FileOutputStream os = new FileOutputStream(out, true);
os.write(ivBytes);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
cos.write(bytes);
os.close();
cos.close();
}
public byte[] decryptToBytes(File in) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8");
System.out.println("Salt afetr:" +salt);
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//Get IVBytes of the first 16 bytes of the file
System.out.println("File Size: " + in.length());
FileInputStream is = new FileInputStream(in);
byte [] ivBytesRecovered = new byte [16];
if (is.read(ivBytesRecovered) != ivBytesRecovered.length) {
//is.close();
throw new IllegalStateException("Too short file");
}
System.out.println("IvBytes After: " + DatatypeConverter.printHexBinary(ivBytesRecovered));
// Decrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytesRecovered));
byte[] encBytes = new byte[(int) in.length()-16];
is.read(encBytes);
byte[] decryptedBytes = null;
try {
decryptedBytes = cipher.doFinal(encBytes);
} catch (IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
is.close();
return decryptedBytes;
}
I recived the error message... Maybe is because I'm no reading properly the bytes after the first 16? Or maybe is because I don't give a good size for the byte[] encBytes?
Used for generating the salt:
public String generateSalt() {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String s = new String(bytes);
return s;
}
Your problem is on the encrypt/write side; you close os (the FileOutputStream) before you close cos (the CipherOutputStream),
so it doesn't write the last encrypted block to the file. Instead you should just cos.close();
which per javadoc
(note that handles the os.close() for you):
This method invokes the doFinal method of the encapsulated cipher object, which causes any bytes buffered by the encapsulated cipher to be processed. The result is written out by calling the flush method of this output stream.
This method resets the encapsulated cipher object to its initial state and calls the close method of the underlying output stream.
Or since your plaintext fits in one buffer (and your read side requires that) skip the CipherOutputStream and just os.write (cipher.doFinal(bytes)); os.close();
Also, you construct the FileOutputStream with (file,true)
which means if the identified file already exists and is nonempty (and not protected so it throws an exception) your new data is appended at the end, but your read side expects it to be at the beginning.
And: you don't show what the salt is, but if it doesn't contain more entropy than a human can remember it probably isn't serving its purpose. Since we can't expect a human to remember and type in adequate salt, the usual practice is to include it in the file/message/database/whatever.