I wanted to try my hand at encrypting the file and used the following response overflow stack . However, checking the initialization vector, I found that it produced only the first 16 bytes.
When I pass an empty iv to the decrypted cipher (except for the first 16 bytes), the data was unexpectedly decrypted. [I believe that the library has not broken and that I am doing something wrong; but it’s scary to think that others may be in the same boat and not be aware of it.]
Example:
Initial bytes ..... 2222222222222222222222222222222222222222222222222222
Encrypted bytes ... b35b3945cdcd08e2f8a65b353ff754c32a48d9624e16b616d432
Decrypted bytes ... 3c4154f7f33a2edbded5e5af5d3d39b422222222222222222222
Q: Why not all decryption fails?Speculation: I believe that I could do the encryption by iterating over the data 16 bytes at a time and updating iv of each round by hashing the previous encrypted 16-byte block. However, it seems like this is a busy job that I would expect from a library. And I expected that these would be mentioned by experts who represent the implementation guidelines. But I just grab the straw here. As far as I know, perhaps the security community only cares about breaking the first block.
Note. Just now, I discovered a stack overflow for 5.5 years post , which revealed the same problem; and unfortunately he still has no answer.
package test;
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import java.util.Formatter;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class IvBug {
public static void main(String[] args) throws Exception {
final char[] password = "foo".toCharArray();
final byte[] salt = "bar".getBytes();
byte[] iData = new byte[300];
java.util.Arrays.fill(iData, (byte)0x22);
SecretKey sKey = generateKey(password,salt);
byte[] iv = generateIv(sKey);
byte[] eData = encrypt(sKey, iData, iv);
byte[] badIv = new byte[iv.length];
byte[] dData = decrypt(sKey, eData, badIv);
System.out.println("iv " + hexStr(iv));
System.out.println("bv " + hexStr(badIv));
System.out.println("I: " + hexStr(iData));
System.out.println("E: " + hexStr(eData));
System.out.println("D: " + hexStr(dData));
}
static SecretKey generateKey(char[] password, byte[] salt) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
}
static byte[] generateIv(SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
AlgorithmParameters params = cipher.getParameters();
return params.getParameterSpec(IvParameterSpec.class).getIV();
}
static byte[] encrypt(SecretKey key, byte[] data, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(data);
}
static byte[] decrypt(SecretKey key, byte[] data, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(data);
}
static String hexStr(byte[] bytes) {
try (Formatter formatter = new Formatter()) {
for (byte b : bytes) formatter.format("%02x", b);
return formatter.toString();
}
}
}
code>