AES文件解密“给定最终块未正确填充”

我想加密然后解密文件使用AES。 我已经阅读了很多关于错误的主题"Given final block not properly padded" 。 但我找不到解决方案。

抱歉指定我的代码的语言,我不知道写java语言

这是我的代码:

变量

 // IV, secret, salt in the same time private byte[] salt = { 'h', 'u', 'n', 'g', 'd', 'h', '9', '4' }; public byte[] iv; public SecretKey secret; 

createSecretKey

 public void createSecretKey(String password){ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256); SecretKey tmp = factory.generateSecret(spec); secret = new SecretKeySpec(tmp.getEncoded(), "AES"); } 

方法加密

 public void encrypt(String inputFile){ FileInputStream fis = new FileInputStream(inputFile); // Save file: inputFile.enc FileOutputStream fos = new FileOutputStream(inputFile + ".enc"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = cipher.getParameters(); // Gen Initialization Vector iv = (byte[]) ((IvParameterSpec) params .getParameterSpec(IvParameterSpec.class)).getIV(); // read from file (plaint text) -----> save with .enc int readByte; byte[] buffer = new byte[1024]; while ((readByte = fis.read(buffer)) != -1) { fos.write(cipher.doFinal(buffer), 0, readByte); } fis.close(); fos.flush(); fos.close(); } 

方法解密

 public void decrypt(String inputFile){ FileInputStream fis = new FileInputStream(inputFile); // Save file: filename.dec FileOutputStream fos = new FileOutputStream(inputFile.substring(0, inputFile.length() - 4) + ".dec"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); // Read from file encrypted ---> .dec int readByte; byte[] buffer = new byte[1024]; while ((readByte = fis.read(buffer)) != -1) { fos.write(cipher.doFinal(buffer), 0, readByte); } fos.flush(); fos.close(); fis.close(); } 

更新

解决方案: buffer编辑大小是16的倍数。使用CipherInput / Output作为读/写文件。

Tks Artjom B.

AES是块密码,因此仅适用于16字节的块。 诸如CBC之类的操作模式使您可以将多个块链接在一起。 诸如PKCS#5填充之类的填充使您能够通过将明文填充到块大小的下一个倍数来加密任意长度的明文。

问题是你要分别加密每1024个字节。 由于1024除以块大小,因此填充在加密之前添加完整块。 因此,密文块长1040字节。 然后在解密期间,您只读取1024缺少填充。 Java尝试解密它,然后尝试删除填充。 如果填充格式错误(因为它不存在),则抛出exception。

轻松修复

只需将缓冲区增加到1040字节即可解密。

正确修复

不要在单独的块中加密它,而是使用Cipher#update(byte[], int, int)而不是Cipher.doFinal来更新您读取的每个缓冲区的密文或使用CipherInputStream


其他安全考虑:

你错过了一个随机的IV。 没有它,攻击者可能只能通过观察密文来看到您在同一个密钥下加密了相同的明文。

您缺少密文身份validation。 没有它,您无法可靠地检测密文中的(恶意)更改,并可能使您的系统受到诸如填充oracle攻击之类的攻击。 使用像GCM这样的身份validation模式,或者通过HMAC运行创建的密文来创建身份validation标记并将其写入结尾。 然后,您可以在解密期间/之前validation标记。

您假设加密数据的长度等于明文数据的长度,但加密的AES数据始终是AES块大小的倍数(16字节),并且可以有一个额外的完整填充块。

处理流加密的最有效方法是使用JCE的CipherOutputStream和CipherInputStream( http://docs.oracle.com/javase/7/docs/api/javax/crypto/CipherInputStream.html )。 这些课程为您完成所有工作。

此外,请确保始终在加密方法中保存新生成的IV,以便能够将其用于解密。