Java使用aes256 / CBC / PKCS7Padding加密文件

时间:2022-10-22 18:29:27

I'm trying to encrypt a file using aes256- CBC-PKCS7Padding . I'm using bouncy castle library , but I get exception

我正在尝试使用aes256-CBC-PKCS7Padding加密文件。我正在使用充气城堡图书馆,但我得到例外

java.lang.IllegalArgumentException: invalid parameter passed to AES init - org.bouncycastle.crypto.params.ParametersWithIV
at org.bouncycastle.crypto.engines.AESEngine.init(Unknown Source)
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.init(Unknown Source)

Here the source code :

这里的源代码:

public class Crypto {

    public static final int AES_Key_Size = 256;
    public static int blockSize = 16;
    private final BlockCipher AESCipher = new AESEngine();
    private PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(AESCipher, new PKCS7Padding());
    private byte[] IV;
    private KeyParameter key;

    public Crypto() throws NoSuchAlgorithmException {
        KeyGenerator kg = KeyGenerator.getInstance("AES");
        kg.init(AES_Key_Size);
        SecretKey sk = kg.generateKey();
        key = new KeyParameter(sk.getEncoded());


    }
    public void CryptoZip(File plikZip, File plikAES) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {

        byte[] input = Files.readAllBytes(plikZip.toPath());
        byte[] cryptOut = encrypt(input);
        FileOutputStream fos = new FileOutputStream(plikAES);
        fos.write(cryptOut);
        fos.close();



    }


    private byte[] encrypt(byte[] input) throws DataLengthException, IllegalStateException, InvalidCipherTextException {



        IV = new byte[blockSize];
        SecureRandom random = new SecureRandom();
        random.nextBytes(IV);

        cipher.init(true, new ParametersWithIV(key, IV)); // problem here


        byte[] output = new byte[cipher.getOutputSize(input.length)];
        int bytesWrittenOut = cipher.processBytes(
            input, 0, input.length, output, 0);

        cipher.doFinal(output, bytesWrittenOut);

        return output;

    }
}

Any suggestion how to fix it and explanation what I'm doing wrong will be really helpful.

任何建议如何解决它并解释我做错了什么将是非常有帮助的。

1 个解决方案

#1


1  

What you're missing is the indication of the mode. If that is missing then ECB mode is assumed, and ECB mode doesn't take an IV. So PaddedBufferedBlockCipher does do buffering, but for ECB mode. So the init mode simply passes the parameters to AESEngine, and AESEngine rejects the IV as it only accepts keys.

您缺少的是模式的指示。如果缺少那么则假定ECB模式,并且ECB模式不采用IV。所以PaddedBufferedBlockCipher确实做缓冲,但是对于ECB模式。所以init模式只是将参数传递给AESEngine,AESEngine拒绝IV,因为它只接受密钥。

In your code, the following would be a direct fix for the problem:

在您的代码中,以下内容将直接解决问题:

private final CBCBlockCipher AESCipherCBC = new CBCBlockCipher(AESCipher);
private final PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(AESCipherCBC, new PKCS7Padding());

I'll include the following rewrite to show you a different way of writing this down. Note that I didn't touch upon handling the IV or the exceptions correctly. Obviously, for large files, you may want to stream the contents and/or to map your files.

我将包含以下重写,以向您展示一种不同的写法。请注意,我没有正确处理IV或异常。显然,对于大文件,您可能希望流式传输内容和/或映射文件。

// renamed as crypto is a horrible name
public class FileEncryptor {

    // lets use all uppercase constant names
    public static final int AES_KEY_SIZE = 256;

    // only field needed, the rest can be generated on the fly
    private final KeyParameter key;

    public FileEncryptor() throws NoSuchAlgorithmException {
        key = generateKey();
    }

    private static KeyParameter generateKey() {
        // removed KeyGenerator as that's dependent on JCA crypto-API 
        SecureRandom keyRNG = new SecureRandom();
        byte[] keyData = new byte[AES_KEY_SIZE / Byte.SIZE];
        keyRNG.nextBytes(keyData);
        return new KeyParameter(keyData);
    }

    // the code doesn't do anything with zip itself, so no need to include it in the method name
    public void encryptFile(File plaintextFile, File ciphertextFile) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {
        byte[] plaintext = Files.readAllBytes(plaintextFile.toPath());
        byte[] ciphertext = encrypt(plaintext);
        // try and be symmetric, use Files functionality for reading *and writing*
        Files.write(ciphertextFile.toPath(), ciphertext);
    }


    private byte[] encrypt(byte[] plaintext) throws DataLengthException, IllegalStateException, InvalidCipherTextException {
        // create cipher
        final BlockCipher aes = new AESFastEngine();
        CBCBlockCipher aesCBC = new CBCBlockCipher(aes);
        PaddedBufferedBlockCipher aesCBCPadded =
                new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());

        // create IV
        byte[] iv = new byte[aes.getBlockSize()];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);

        // initialize cipher with IV
        ParametersWithIV paramsWithIV = new ParametersWithIV(key, iv);
        aesCBCPadded.init(true, paramsWithIV); // problem here

        // encrypt
        byte[] ciphertext = new byte[aesCBCPadded.getOutputSize(plaintext.length)];
        int bytesWrittenOut = aesCBCPadded.processBytes(
            plaintext, 0, plaintext.length, ciphertext, 0);
        aesCBCPadded.doFinal(ciphertext, bytesWrittenOut);

        // that's great, but where is your IV now? you need to include it in the returned ciphertext!
        return ciphertext;
    }
}

#1


1  

What you're missing is the indication of the mode. If that is missing then ECB mode is assumed, and ECB mode doesn't take an IV. So PaddedBufferedBlockCipher does do buffering, but for ECB mode. So the init mode simply passes the parameters to AESEngine, and AESEngine rejects the IV as it only accepts keys.

您缺少的是模式的指示。如果缺少那么则假定ECB模式,并且ECB模式不采用IV。所以PaddedBufferedBlockCipher确实做缓冲,但是对于ECB模式。所以init模式只是将参数传递给AESEngine,AESEngine拒绝IV,因为它只接受密钥。

In your code, the following would be a direct fix for the problem:

在您的代码中,以下内容将直接解决问题:

private final CBCBlockCipher AESCipherCBC = new CBCBlockCipher(AESCipher);
private final PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(AESCipherCBC, new PKCS7Padding());

I'll include the following rewrite to show you a different way of writing this down. Note that I didn't touch upon handling the IV or the exceptions correctly. Obviously, for large files, you may want to stream the contents and/or to map your files.

我将包含以下重写,以向您展示一种不同的写法。请注意,我没有正确处理IV或异常。显然,对于大文件,您可能希望流式传输内容和/或映射文件。

// renamed as crypto is a horrible name
public class FileEncryptor {

    // lets use all uppercase constant names
    public static final int AES_KEY_SIZE = 256;

    // only field needed, the rest can be generated on the fly
    private final KeyParameter key;

    public FileEncryptor() throws NoSuchAlgorithmException {
        key = generateKey();
    }

    private static KeyParameter generateKey() {
        // removed KeyGenerator as that's dependent on JCA crypto-API 
        SecureRandom keyRNG = new SecureRandom();
        byte[] keyData = new byte[AES_KEY_SIZE / Byte.SIZE];
        keyRNG.nextBytes(keyData);
        return new KeyParameter(keyData);
    }

    // the code doesn't do anything with zip itself, so no need to include it in the method name
    public void encryptFile(File plaintextFile, File ciphertextFile) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {
        byte[] plaintext = Files.readAllBytes(plaintextFile.toPath());
        byte[] ciphertext = encrypt(plaintext);
        // try and be symmetric, use Files functionality for reading *and writing*
        Files.write(ciphertextFile.toPath(), ciphertext);
    }


    private byte[] encrypt(byte[] plaintext) throws DataLengthException, IllegalStateException, InvalidCipherTextException {
        // create cipher
        final BlockCipher aes = new AESFastEngine();
        CBCBlockCipher aesCBC = new CBCBlockCipher(aes);
        PaddedBufferedBlockCipher aesCBCPadded =
                new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());

        // create IV
        byte[] iv = new byte[aes.getBlockSize()];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);

        // initialize cipher with IV
        ParametersWithIV paramsWithIV = new ParametersWithIV(key, iv);
        aesCBCPadded.init(true, paramsWithIV); // problem here

        // encrypt
        byte[] ciphertext = new byte[aesCBCPadded.getOutputSize(plaintext.length)];
        int bytesWrittenOut = aesCBCPadded.processBytes(
            plaintext, 0, plaintext.length, ciphertext, 0);
        aesCBCPadded.doFinal(ciphertext, bytesWrittenOut);

        // that's great, but where is your IV now? you need to include it in the returned ciphertext!
        return ciphertext;
    }
}