AES use same IV for encryption and decryption

馋奶兔 提交于 2021-02-18 08:46:13

问题


I'm having some issues with my code whereby I am not using the same IV for encryption and decryption. I know in order to do this correctly I have to write the IV to my output file before the data however I am struggling implementing this. Could anyone help me with this issue?

Edited code code again to show full scope

public class TestFileEncryption {
    private static void mainCrypto(int cipherMode, File inputFile, File outputFile) throws Exception{
        //Let the user enter the key they wish to use
        Key secretKey = new SecretKeySpec(UITest.getStoreKey().getBytes(), UITest.getSendAlg()); //Generates a key based on the default keysize for the specified algorithm

        //Generate an Initialization Vector (IV)
        final int ALG_KEYLENGTH = UITest.getStoreKey().length(); //Change this as desired for the security level you want
        byte[] iv = new byte[ALG_KEYLENGTH]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
        SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
        prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method

        //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
        Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode

        //Initialize the Cipher for Encryption
        cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));

        //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
        FileInputStream inputStream = new FileInputStream(inputFile);
        byte[] inputBytes = new byte[(int) inputFile.length() - ALG_KEYLENGTH];
        inputStream.read(iv);
        inputStream.read(inputBytes);
        byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        outputStream.write(iv);
        outputStream.write(outputBytes);
        inputStream.close();
        outputStream.close();
    }

    public static void encrypt(File inputFile, File outputFile) throws Exception {
        mainCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
    }

    public static void decrypt(File inputFile, File outputFile) throws Exception {
        mainCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
    }

    public static void main(String[] args) {}
}

回答1:


Just extending answer of @Javier.

it looks like you'd like to use the same method for encryption and decrpytion (depending on the mode) however there's a difference in handling the IV.

You generated a random IV, then you overwrote it with the input of the (plain) input and at the end you wrote it to the output (regardless it's decryption).

So you have to distinguish if the mode is

  • encryption - the IV is generated and written to the output before ciphertext
  • decryption - the IV is read from the input and used for decryption, but not written to the output

something like that:

private void encrypt(File inputFile, File outputFile)  {
    //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
    FileInputStream inputStream = new FileInputStream(inputFile);
    byte[] inputBytes = new byte[(int) inputFile.length()];
    byte[] iv = new byte[16]; // 16 for AES-CBC
    SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
    prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method

    //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
    Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode

    //Initialize the Cipher for Encryption
    cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));      
        inputStream.read(inputBytes);
        byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
        FileOutputStream outputStream = new FileOutputStream(outputFile);
        outputStream.write(iv);
        outputStream.write(outputBytes);
        outputStream.flush();
        inputStream.close();
        outputStream.close();
    }
}

private void decrypt(File inputFile, File outputFile) {
    //Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
    FileInputStream inputStream = new FileInputStream(inputFile);
    byte[] inputBytes = new byte[(int) inputFile.length()-16];
    byte[] iv = new byte[16]; // 16 for AES-CBC

    //Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
    Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode

    //Initialize the Cipher for Encryption
    cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));      
    inputStream.read(iv);
    inputStream.read(inputBytes);
    byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
    FileOutputStream outputStream = new FileOutputStream(outputFile);
    outputStream.write(outputBytes);
    outputStream.flush();
    inputStream.close();
    outputStream.close();
}

To leave out some detail, maybe you could directly use Java CipherOutputStream and CiptherInputStream and the implementation will handle these details for you (if you don't care about exact format).

Next what are you missing is an authentication tag, at least hash of the plaintext assuring integrity of the ciphertext. (it's called authenticated encryption)




回答2:


You just have to write the IV before the ciphertext:

outputStream.write(iv);
outputStream.write(outputBytes);

Then, when decrypting, read the IV and the ciphertext:

byte[] iv = new byte[ALG_BLOCKSIZE];
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_BLOCKSIZE];
inputStream.read(iv);
inputStream.read(inputBytes);

Here ALG_BLOCKSIZE needs to be 16 for AES-CBC.



来源:https://stackoverflow.com/questions/44971076/aes-use-same-iv-for-encryption-and-decryption

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!