【问题标题】:AES use same IV for encryption and decryptionAES 使用相同的 IV 进行加密和解密
【发布时间】:2017-07-07 12:42:52
【问题描述】:

我的代码存在一些问题,因此我没有使用相同的 IV 进行加密和解密。我知道为了正确执行此操作,我必须在数据之前将 IV 写入我的输出文件,但是我正在努力实现这一点。谁能帮我解决这个问题?

再次编辑代码以显示完整范围

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) {}
}

【问题讨论】:

  • byte[] iv = new byte[ALG_KEYLENGTH] 是错误的,因为 IV 需要与块具有相同的大小而不是密钥。对于 AES-CBC,这是 总是 16 个字节。
  • @ArtjomB。意外的密钥长度 128 位是 16 个字节,但更意外的是他让它工作了 :)

标签: java encryption aes


【解决方案1】:

你只需要在密文之前写IV:

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

然后,解密时,读取IV和密文:

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

这里 ALG_BLOCKSIZE 对于 AES-CBC 需要为 16。

【讨论】:

  • 我已经尝试过了,现在我得到了一个 BadPaddingException - “给定最终块没有正确填充”
  • 好的,这是一种工作,除了现在我的文件没有在不同的位置正确解密。之前的输出 - “#ÐP¦ºõk °ò¸这是一个测试 这是一个测试 这是一个测试” 现在输出 - “这是一个测试 #ÐP¦ºõk °ò¸这是一个测试”
  • 已根据您的建议修改了我的代码,是否正确?
  • @dny 快到了。当您读取文件进行解密时,第一个 ALG_KEYLENGTH 字节是 IV(根据我在回答中给出的实现,您也可以将 IV 存储在其他地方......)当您读取文件进行加密时,那里不是 IV,所有输入数据都必须加密。
【解决方案2】:

只是扩展@Javier 的答案。

您似乎希望使用相同的方法进行加密和解密(取决于模式),但是在处理 IV 方面存在差异。

您生成了一个随机 IV,然后用(普通)输入的输入覆盖它,最后将它写入输出(不管它是解密)。

所以你要区分模式是否是

  • 加密 - IV 在密文之前生成并写入输出
  • 解密 - IV 从输入读取并用于解密,但不写入输出

类似的东西:

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();
}

为了省略一些细节,也许你可以直接使用 Java CipherOutputStream 和 CiptherInputStream 并且实现会为你处理这些细节(如果你不关心确切的格式)。

接下来你缺少的是一个身份验证标签,至少是明文的哈希,以确保密文的完整性。 (称为认证加密)

【讨论】:

  • 我有点困惑我应该如何解决我的问题,如果我编辑上面的代码以显示整个程序,你能帮我解决这些问题吗?我有两种加密和解密方法,不知道是否需要修改。
  • 你应该明白它是如何工作的(即使没有代码,在纸上),没有人会代替你编写代码。简单的 if 就可以了。也许您可以将加密和解密方法分开
  • 这不是我想要的。我的意思是我不确定是否应该将输入/输出流分成两部分。就像它们应该在 enc 和 dec 函数中而不是 mainCrypto 函数中一样?
  • @dny 更新了一些代码。在 mainCrypto 你可以说 *if mode==enrypt, call encrypt() else decrypt(); * 单一方法(mainCrypto)的问题是你正在做两件事(enc 和 dec),所以你必须将它分开。事实上,密文不应该只是“密码”,它会包含 IV(它也应该包含散列)
  • @dny 并且代码仍然不是万无一失的,当在代码中调用 readBytes 时,假设所有数组都已读取。这可能不是真的,真正的代码应该检查一下。
猜你喜欢
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2014-01-14
相关资源
最近更新 更多