【问题标题】:Decrypting of AES/CBC/PKCS5Padding Error: Given final block not properly padded解密 AES/CBC/PKCS5Padding 错误:给定最终块未正确填充
【发布时间】:2017-03-17 14:31:39
【问题描述】:

我在解密大型加密文件上的 AES/CBC/PKCS5Padding 密码时遇到 Given final block not proper padding 错误。

我认为这个问题是由于在 cipher.init() 方法中添加了错误的初始化向量造成的。

我无法在运行时读取整个文件,所以我需要加密固定大小的块。此时我正在创建 IV 并将其存储到 .txt 文件中。但在解密方法中,我在每个解密周期都使用相同的 IV。我应该如何改变这个?

加密:

void encrypt() throws Exception{
    char[] password = passwordText.getText().toCharArray();
    byte[] salt = new byte[8];

    /* Creating and saving salt */
    salt = saveSalt(salt);

    /* Securing password */
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

    if (choosedFile != null) {
        /* Choosing algorithm for decryption */
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

        /* Getting plain file */
        CipherInputStream fis = new CipherInputStream(new  FileInputStream(choosedFile), cipher);
        CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher);

        /* Encrypting and Measuring */
        long startTime = System.currentTimeMillis();
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] rawText = new byte[128];
        int count;
        while((count = fis.read(rawText)) > 0) {
            System.out.println(count);
            byte[] encryptedText = cipher.doFinal(rawText);
            fos.write(encryptedText, 0, count);
        }
        long stopTime = System.currentTimeMillis();

        fis.close();
        fos.close();

        /* Creating initialization vector and storing*/
        byte[] iVector = cipher.getIV();
        saveIVector(iVector);

        text.setText(text.getText() + "File was encrypted in " + (stopTime - startTime) + "ms.\n");
    }

}

解密:

    void decrypt() throws Exception {
    /* Getting salt */
    byte[] salt = getSalt();
    /* Getting initialization vector */
    byte[] iVector = getIVector();
    /* Getting user password */
    char[] password = passwordText.getText().toCharArray();


    /* Securing password */
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

    if (choosedFile != null) {

        /* Choosing algorithm for decryption */
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        /* Getting ciphered file */


        CipherInputStream fis = new CipherInputStream(new  FileInputStream(choosedFile), cipher);
        CipherOutputStream fos = new CipherOutputStream(new FileOutputStream(choosedFile+".decrypted"), cipher);

        /* Decrypting and Measuring */
        long startTime = System.currentTimeMillis();
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iVector));
        byte[] rawText = new byte[128];
        int count;
        while((count = fis.read(rawText)) > 0) {
                byte[] encryptedText = cipher.doFinal(rawText);
                fos.write(encryptedText, 0, count);
            }

        long stopTime = System.currentTimeMillis();

        fis.close();
        fos.close();

【问题讨论】:

  • 由于您指定 PKCS#5 添加加密和解密,因此错误实际上是由于不正确的参数(密钥、iv、加密数据)导致的解密错误。解密失败,所以填充也是垃圾。重新处理问题以仅包括加密并以十六进制提供加密数据。使用小输入使其变得容易。提供minimal reproducible example。由于错误与加密相关,因此删除所有其他代码。
  • @zaph 对不起,我忘了提到加密和解密算法都工作得很好,直到我不得不将大文件切割成固定大小的块。因此,我认为不需要加密数据的示例。但是我试图加密和解密小文件。结果是一样的。不过,你是对的。加密无法正常工作,但我不知道为什么。

标签: java security encryption cryptography


【解决方案1】:

当使用CipherInputStreamCipherOutputStream 时,流处理对密码的所有调用(这就是为什么在初始化时将密码传递给它的原因)。您只需正确初始化它,并通过流传输数据,密码流将对update()doFinal() 进行所需的调用。记得关闭 Steam 以触发doFinal()

目前您的代码以不受控制的方式通过密码多次传递数据,并且数据混乱。

此外,您只需要 CipherInputStream 进行解密,CipherOutputStream 进行加密。在您当前的代码中,您同时使用加密和解密。

加密可能是这样的(这不处理 iv ..):

...     
cipher.init(Cipher.ENCRYPT_MODE, secret);
InputStream is = new FileInputStream(choosedFile);
OutputStream os = new CipherOutputStream(new FileOutputStream(choosedFile+".encrypted"), cipher);

byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
    os.write(buffer, 0, len);
}

is.close();
os.close();
...

【讨论】:

  • 我想我知道你想说什么。但是你能给我一些更好的例子吗?我不明白为什么在加密时只需要CipherOutputStream,当我需要输入文件块时,对其进行加密然后将其传递给输出。然后做同样的事情,直到我加密了所有文件块。我在运行时没有完整的 1GB 原始数据。
  • 谢谢!像魅力一样为我工作。
【解决方案2】:

担心“直到我不得不将大文件切成固定大小的块”。

使用“块”代替上面的“块”,因为“块”在 AES 等分组密码中具有特定含义。

玩具对块做了什么,将它们连接起来?

在 CBC 模式下,在第一个块之后,前一个加密的块值被有效地用作下一个块的 IV。因此,在拆分然后连接块时,前一个块的最后一个块的值是下一个块的 IV。

CBC mode

或者你正在做一些完全不同的事情?

【讨论】:

  • 如您在上面看到的byte[] rawText 代表这些文件块。 (对不起,误导性名称应该是 rawChunkOfFile 之类的东西)我正在尝试加密这个块并将其写入输出文件。比取另一个块并做同样的事情,直到没有剩下的块为止。但我认为,我应该以某种方式使用cipher.update() 方法而不是cipher.doFinal()
  • 是的,使用cipher.update(),它传播区块链。不要忘记致电cipher.final()。见CBC mode
  • 我应该使用cipher.update()然后将块写入输出,在while()之后我应该调用cipher.final()。或者我应该在最后一块文件上调用 final 吗?
  • 我仍然收到 javax.crypto.BadPaddingException: Given final block not proper padding 错误。不过这次是从(count = fis.read(rawText)) > 0处解密方法。
猜你喜欢
  • 1970-01-01
  • 2018-03-03
  • 2015-05-22
  • 2015-03-31
  • 2015-03-28
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多