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

hun*_*x5e 1 java encryption aes badpaddingexception

我想加密然后解密文件使用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;
Run Code Online (Sandbox Code Playgroud)

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");
}
Run Code Online (Sandbox Code Playgroud)

方法加密

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();
}
Run Code Online (Sandbox Code Playgroud)

方法解密

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();
}
Run Code Online (Sandbox Code Playgroud)

更新

解决方案:编辑大小为buffer16的倍数.使用CipherInput/Output作为读/写文件.

TKS Artjom B.

Art*_* B. 8

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

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

轻松修复

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

正确修复

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


其他安全考虑:

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

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