Java 使用 AES 解密文件时密码 doFinal pad 块损坏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22829354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 18:09:50  来源:igfitidea点击:

Cipher doFinal pad block corrupted while decrypting file with AES

javaencryptionaesbouncycastle

提问by Tim

I am creating a program that lets users upload and download encrypted files and decrypt them if they have the correct permissions to do so. Encrypting and uploading is fine, and so is downloading, but when I try to decrypt I get the "pad block corrupted" error.

我正在创建一个程序,允许用户上传和下载加密文件并解密它们,如果他们有正确的权限这样做。加密和上传很好,下载也很好,但是当我尝试解密时,我收到了“垫块损坏”错误。

What I am trying to do is take the encrypted file and then make a copy that is unencrypted

我想要做的是获取加密文件,然后制作未加密的副本

I shortened what I could so please don't comment that it looks incomplete.

我缩短了我能做的,所以请不要评论它看起来不完整。

KeyGen:

密钥生成:

public static SecretKey genGroupSecretKey() {
    try {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES", "BC");
        keyGen.init(128);
        return keyGen.generateKey();
    } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
        System.err.println("Error: " + e.getMessage());
        e.printStackTrace(System.err);
    }
    return null;
}

Encrypt:

加密:

try (FileInputStream fis = new FileInputStream(sourceFile)) {
    response = Utils.decryptEnv((byte[]) tempResponse.getObjContents().get(0), fsSecretKey, ivSpec);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);

    do {
        byte[] buf = new byte[4096];

        int n = fis.read(buf); // can throw an IOException
        else if (n < 0) {
            System.out.println("Read error");
            fis.close();
            return false;
        }

        byte[] cipherBuf = cipher.doFinal(buf);

        // send through socket blah blah blah

    } while (fis.available() > 0);

Decrypt:

解密:

   ...     

   Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
   cipher.init(Cipher.DECRYPT_MODE, secKey, ivSpec);

    File file = new File("D_" + filename); // D_ for decrypted
    FileOutputStream fos = null;
    if (!file.exists()) {
        file.createNewFile();
        fos = new FileOutputStream(file);
    }

    try (FileInputStream fis = new FileInputStream(filename)) {
        do {
            byte[] buf = new byte[4096];
            int n = fis.read(buf);
            if (n < 0) {
                System.out.println("Read error");
                fis.close();
                return false;
            }

            byte[] cipherBuf = cipher.doFinal(buf);  // ERROR HERE
            System.out.println("IS THIS WORKING WTF: " + new String(cipherBuf));
            fos.write(cipherBuf, 0, n);

        } while (fis.available() > 0);
        fis.close();
        fos.close();
        return true;

采纳答案by Oleg Estekhin

You should you Cipher.doFinal()only for the very last block of data. You should modify both your encryption and decryption code to use Cipher.update()for all intermediate data and use a single call to Cipher.doFinal()at the end. It is permitted to use Cipher.update()for all intermediate data block and just call the Cipher.doFinal()with the empty array at the end.

您应该Cipher.doFinal()只针对最后一个数据块。您应该修改加密和解密代码以Cipher.update()用于所有中间数据,并Cipher.doFinal()在最后使用单个调用。允许Cipher.update()用于所有中间数据块,并Cipher.doFinal()在末尾使用空数组调用。

Also, you are ignoring the return value of fis.read(buf)when you pass the data read from the stream to the cipher.

此外,您忽略了fis.read(buf)将从流中读取的数据传递给密码时的返回值。

Also, you are using InputStream.available()as a condition to end the cycle. Even if the code inside the cycle would be correct, you would be getting unpredictable results due to spurious triggering of cycle condition.

此外,您正在使用InputStream.available()作为结束循环的条件。即使循环内的代码是正确的,由于循环条件的虚假触发,您也会得到不可预测的结果。

Use the following pattern to work with the ciphers:

使用以下模式处理密码:

Cipher cipher = ...
InputStream in = ...
OutputStream out = ...

byte[] inputBuffer = new byte[ BUFFER_SIZE ];
int r = in.read(inputBuffer);
while ( r >= 0 ) {
    byte[] outputUpdate = cipher.update( inputBuffer, 0, r );
    out.write( outputUpdate );
    r = in.read(inputBuffer);
}
byte[] outputFinalUpdate = cipher.doFinal();
out.write( outputFinalUpdate );

Check other variants of Cipher.update()- it is possible to use two pre-allocated buffers and minimize additional allocations.

检查其他变体Cipher.update()- 可以使用两个预先分配的缓冲区并最小化额外的分配。

Also check if you can reuse javax.crypto.CipherInputStreamand javax.crypto.CipherOutputStreamclasses so that you do not have to work with the cipher directly.

还要检查您是否可以重用javax.crypto.CipherInputStreamjavax.crypto.CipherOutputStream类,以便您不必直接使用密码。

回答by user3664441

If the objective is just to encrypt and decrypt a file, the following program would help.

如果目标只是加密和解密文件,以下程序会有所帮助。

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.fiberlink.security.crypto.CryptoUtils;

public class DecryptDocsController {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    public static final String PKCS7_PADDING = "AES/CBC/PKCS7Padding";
    public static final int CHUNK_SIZE = 16;
    public static final int STARTING_LOCATION = 0;
    public static final int STREAM_FINISH_LOCATION = -1;
    public void encryptFile() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException 
        {
        FileInputStream fis = new FileInputStream(DECRYPTED_FILE_LOCATION_1);
        FileOutputStream fos = new FileOutputStream(ENC_FILE_LOCATION_1);
        Cipher cipher = Cipher.getInstance(PKCS7_PADDING);
        SecretKeySpec skeySpec = new SecretKeySpec(getEcryptionByte(FILE_ENCRYPION_KEY), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        CipherInputStream inputStream = new CipherInputStream(fis, cipher);
        int count =0;
        byte[] data = new byte[CHUNK_SIZE];
        while((count=(inputStream.read(data, STARTING_LOCATION, CHUNK_SIZE))) != STREAM_FINISH_LOCATION) {
            fos.write(data, STARTING_LOCATION, count);
        }
        fis.close();
        fos.close();
        inputStream.close();
       }

    public void decryptFile() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException 
        {
        FileInputStream fis = new FileInputStream(ENC_FILE_LOCATION_2);
        FileOutputStream fos = new FileOutputStream(DECRYPTED_FILE_LOCATION_2);
        Cipher cipher = Cipher.getInstance(PKCS7_PADDING);
        SecretKeySpec skeySpec = new SecretKeySpec(getEcryptionByte(FILE_ENCRYPION_KEY), "AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        CipherInputStream inputStream = new CipherInputStream(fis, cipher);
        int count =0;
        byte[] data = new byte[CHUNK_SIZE];
        while((count=(inputStream.read(data, STARTING_LOCATION, CHUNK_SIZE))) != STREAM_FINISH_LOCATION) {
            fos.write(data, STARTING_LOCATION, count);`enter code here`
        }
        fis.close();
        fos.close();
        inputStream.close();
        }
}