【问题标题】:BadPadding exception when trying to decrypt AES based encrypted text尝试解密基于 AES 的加密文本时出现 BadPadding 异常
【发布时间】:2019-06-07 21:03:19
【问题描述】:

问题场景:我在 Ubuntu 中使用 OpenSSL AES-256-CBC 模式创建加密文件。

openssl aes-256-cbc -a -in avengers.txt -out avengers.enc 
test
test
File Content: avengersAssemble
avengers.enc file content: U2FsdGVkX194TyUFrb4gOn86XYaKjKP98YdOlQDJz+t/76mvVmNKl+NyKKUnYwYH

To Decrypt: openssl aes-256-cbc -a -d -in avengers.enc

现在我想使用 java 代码解密这个加密文件avengers.enc,即我只存储这个文件内容和密码(在这种情况下是测试)来解密它。

我的目标:我想使用密码(对称加密)解密,从上面的命令(128 或 192 或 256 cbc)加密。

请帮忙。

我找到了this 代码,但它显示了 BadPadding 异常。请参考链接中@Maarten Bodewes的回复帖。

我从链接中找到的代码:

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Base64;

public class OpenSSLDecryptor {
    private static final Charset ASCII = Charset.forName("ASCII");
    private static final int INDEX_KEY = 0;
    private static final int INDEX_IV = 1;
    private static final int ITERATIONS = 1;

    private static final int ARG_INDEX_FILENAME = 0;
    private static final int ARG_INDEX_PASSWORD = 1;

    private static final String file = 
    "D:\\Eclipse_Workspace\\Cryptography\\avengers.enc";
     private static final String password = "test";

    private static final int SALT_OFFSET = 8;
    private static final int SALT_SIZE = 8;
    private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;

    private static final int KEY_SIZE_BITS = 256;

    /**
     * Thanks go to Ola Bini for releasing this source on his blog.
     * The source was obtained from <a href="http://olabini.com/blog/tag/evp_bytestokey/">here</a> .
     */
    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md,
            byte[] salt, byte[] data, int count) {
        byte[][] both = new byte[2][];
        byte[] key = new byte[key_len];
        int key_ix = 0;
        byte[] iv = new byte[iv_len];
        int iv_ix = 0;
        both[0] = key;
        both[1] = iv;
        byte[] md_buf = null;
        int nkey = key_len;
        int niv = iv_len;
        int i = 0;
        if (data == null) {
            return both;
        }
        int addmd = 0;
        for (;;) {
            md.reset();
            if (addmd++ > 0) {
                md.update(md_buf);
            }
            md.update(data);
            if (null != salt) {
                md.update(salt, 0, 8);
            }
            md_buf = md.digest();
            for (i = 1; i < count; i++) {
                md.reset();
                md.update(md_buf);
                md_buf = md.digest();
            }
            i = 0;
            if (nkey > 0) {
                for (;;) {
                    if (nkey == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    key[key_ix++] = md_buf[i];
                    nkey--;
                    i++;
                }
            }
            if (niv > 0 && i != md_buf.length) {
                for (;;) {
                    if (niv == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    iv[iv_ix++] = md_buf[i];
                    niv--;
                    i++;
                }
            }
            if (nkey == 0 && niv == 0) {
                break;
            }
        }
        for (i = 0; i < md_buf.length; i++) {
            md_buf[i] = 0;
        }
        return both;
    }


    public static void main(String[] args) {
        try {
            // --- read base 64 encoded file ---

            //File f = new File(args[ARG_INDEX_FILENAME]);
            File f = new File(file);
            List<String> lines = Files.readAllLines(f.toPath(), ASCII);
            StringBuilder sb = new StringBuilder();
            for (String line : lines) {
                sb.append(line.trim());
            }
            String dataBase64 = sb.toString();
            byte[] headerSaltAndCipherText = Base64.decode(dataBase64);

            // --- extract salt & encrypted ---

            // header is "Salted__", ASCII encoded, if salt is being used (the default)
            byte[] salt = Arrays.copyOfRange(
                    headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
            byte[] encrypted = Arrays.copyOfRange(
                    headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);

            // --- specify cipher and digest for EVP_BytesToKey method ---

            Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
            MessageDigest md5 = MessageDigest.getInstance("MD5");

            // --- create key and IV  ---

            // the IV is useless, OpenSSL might as well have use zero's
            final byte[][] keyAndIV = EVP_BytesToKey(
                    KEY_SIZE_BITS / Byte.SIZE,
                    aesCBC.getBlockSize(),
                    md5,
                    salt,
                    password.getBytes(ASCII),
                    ITERATIONS); //args[ARG_INDEX_PASSWORD]
            SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);

            // --- initialize cipher instance and decrypt ---

            aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
            byte[] decrypted = aesCBC.doFinal(encrypted);

            String answer = new String(decrypted, ASCII);
            System.out.println(answer);
        } catch (BadPaddingException e) {
            // AKA "something went wrong"
            throw new IllegalStateException(
                    "Bad password, algorithm, mode or padding;" +
                    " no salt, wrong number of iterations or corrupted ciphertext.");
        } catch (IllegalBlockSizeException e) {
            throw new IllegalStateException(
                    "Bad algorithm, mode or corrupted (resized) ciphertext.");
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }        
}

例外:

javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
    at javax.crypto.Cipher.doFinal(Cipher.java:2164)
    at symmetric.main(symmetric.java:146)

【问题讨论】:

  • Prashant,您能否尝试在 Java 代码中将 "MD5" 字符串替换为 "SHA-256" 并查看是否也有效?我目前没有安装 1.1。如果我在 1.0 的命令行中使用-md sha256,它可以完美运行。
  • 它适用于 SHA-256

标签: java encryption openssl aes encryption-symmetric


【解决方案1】:

openssl 版本

 LibreSSL 2.6.5

在这个 jar 文件中使用你的示例

http://www.java2s.com/Code/Jar/b/Downloadbouncycastlejar.htm

除了这个导入之外的代码完全相同

import bwmorg.bouncycastle.util.encoders.Base64;

用相同的密钥解密相同的数据

而且效果很好

可能是参数问题

【讨论】:

  • 我在上面的 jar 上试过了,但没有运气仍然得到同样的异常 javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption. at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847) at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446) at javax.crypto.Cipher.doFinal(Cipher.java:2164) at symmetric.main(symmetric.java:150)
  • 然后您以正确的参数顺序运行 main,首先是 *.enc 文件,然后是密码?
  • 您可以尝试将-md md5 添加到您的命令行界面吗?如果我没记错的话,它说明了用于EVP_BytesToKey 的哈希函数。
  • 也谢谢你们,我会在其他帖子中添加警告。当然,不推荐使用 MD5 是有原因的,我会尝试看看我是否可以让它与 SHA256 一起使用,如果我没记错的话,它会替换 MD5 作为默认值。不过,我首先必须安装 1.1 版本。这是change from 1.0 to 1.1 that I found on the internet
  • 我认为如果@PrashantJeetSingh 你能回答这个问题并解释问题是什么以及你如何解决它,那就太好了
【解决方案2】:

OpenSSL 使用 EVP_BytesToKey 并使用消息摘要算法,例如 md5sha256 等。在上面的代码中,函数 EVP_ByteToKey 有一个 MessageDigest 参数,MD5 传递给该参数,但 MD5 是加密时未在 OpenSSL 命令中添加。下面是与消息摘要算法(即 MD5)一起使用的实际命令。

openssl aes-256-cbc -a -in avengers.txt -out avengers.enc -md md5
 openssl aes-256-cbc -a -d -in avengers.enc -md md5 

要使用任何其他消息摘要算法,只需在 MessageDigest md5 = MessageDigest.getInstance("SHA-256"); 中传递该算法,请参阅 MessageDigest 算法下的 link。此外,必须在命令中传递-md sha256。请参阅 openSSL 手册页

【讨论】:

  • 酷,我也会在我的其他答案中注意到这一点。如果你可以在没有-md5"SHA-256" 的情况下尝试它,你会为我节省很多时间:) 请注意,你可以在一两天后接受你自己的答案。我会从我的另一篇文章中参考这个答案。
  • 很好,如果您不需要 MD5 来实现向后兼容性,最好使用 SHA-256。请注意,对于这种功能(密钥派生),MD5尚未被破坏
  • 当然。感谢@MaartenBodewes 的帮助和建议
  • 干得好@PrashantJeetSingh
猜你喜欢
  • 1970-01-01
  • 2019-09-02
  • 2020-12-17
  • 1970-01-01
  • 2015-08-03
相关资源
最近更新 更多