执行对称加密解密时出现 BadPaddingException

时间:2021-01-18 10:11:46

标签: java exception encryption symmetric

按照以下逻辑进行解密时出现异常。请在下面的代码片段中向我建议任何问题。加密没问题,可以加密。

public String encryptDataSymmetric(String dataTobeEncrypted) {
    String encryptedData = null;
    try {
        Charset CHARSET = Charset.forName("UTF8");
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        SecureRandom securerandom = new SecureRandom();
        KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
        keygenerator.init(192, securerandom);
        SecretKey key = keygenerator.generateKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        // cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        encryptedData = DatatypeConverter.printBase64Binary(cipher.doFinal(dataTobeEncrypted.getBytes(CHARSET)))
                .trim();
        System.out.println("---encryptedData-----" + encryptedData);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return encryptedData;
}

public String decryptDataSymmetric(String dataTobeDecrypted) {
    String decryptedData = null;
    try {
        Charset CHARSET = Charset.forName("UTF8");
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        SecureRandom securerandom = new SecureRandom();
        KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
        keygenerator.init(192, securerandom);
        SecretKey key = keygenerator.generateKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
        decryptedData = new String(
                DatatypeConverter.parseBase64Binary(new String(cipher.doFinal(dataTobeDecrypted.getBytes()))));
        System.out.println("---decryptedData----" + decryptedData);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return decryptedData;
}
<块引用>

javax.crypto.BadPaddingException:填充块损坏于 org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(未知 来源)

2 个答案:

答案 0 :(得分:3)

不是 Java 专家,但您需要从 Base64 解码为字节数组,然后在解密时将其传递给密码,而不是在尝试解密之后。

Base64 编码是将字节流编码为 ascii 字符,用于传输目的。在传输的接收端需要先将base64编码再转换为字节流。

解密后,您必须将解密后的字节数组转换为字符串。

总而言之,解密必须撤销加密方法的步骤。

加密方式:

  • 通过 UTf8 从字符串到纯字节数组
  • 通过密码将普通字节数组转换为加密字节数组
  • 通过 base64 编码将字节数组加密为可打印/可序列化的字符串

解密方法:

  • 通过 base64 解码将可打印/序列化的字符串转换为加密字节数组
  • 通过密码加密字节数组到纯字节数组
  • 通过 UTF 从字节到字符串调用的普通字节数组到字符串。

据说,您的代码存在一些问题。 我能够将这个 Java 示例代码放在一起,仅用于演示我将在这里提出的要点。

  • 首先不要将 AES 与 CBC 单独使用!!!它很容易受到 Padding Oracle 攻击。无需详细说明,这意味着有人可以非常有效地解密您的加密值,如果您的系统甚至提供最轻微的 提示(即使是时差)加密消息是否正确填充。出于同样的原因,请勿使用我在下面编写的代码。
  • 使用经过身份验证的加密方案或算法。
  • 最好找一个信誉良好的库来执行此操作并使用它,而不是编写自己的库。
  • 您需要为每条加密消息创建一个 IV。IV 必须从密码学上强大的随机生成器生成。下面的代码试图做到这一点。
  • 您发送(或存储)带有加密消息(密文)的 IV。 IV 不是秘密。
  • 在下面的代码中,密钥是随机生成的。然后传递给加密和解密方法。您需要做同样的事情,创建您将使用一次的密钥。然后找到一种安全的方式(保险库类型的结构/服务)来存储您使用的密钥。
  • 如果密钥以某种方式被泄露,您需要考虑并制定计划吗?查看密钥过期方案、滑动密钥等...
  • 在下面的代码中,我不是 Java 开发人员,而是尝试使用我能找到的最简单和标准的方法来演示。您的里程可能会有所不同。
/*
DO NOT USE THIS CODE! It is vulnerable to Padding Oracle Attack!
Instead use an Authenticated Encryption scheme or algorithm.
This code is written for demonstration purpose only!! Not secure!!
*/
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.io.Console;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Base64;

public class Main {


public static String encryptDataSymmetric(String dataTobeEncrypted, SecretKey key) {
    String encryptedData = null;
    try {
        Charset CHARSET = Charset.forName("UTF8");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        
        SecureRandom securerandom = new SecureRandom();
        byte[] ivBytes = new byte[cipher.getBlockSize()];
        securerandom.nextBytes(ivBytes);
        IvParameterSpec iv = new IvParameterSpec(ivBytes); 
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] cipherText = cipher.doFinal(dataTobeEncrypted.getBytes(CHARSET));

        System.out.println("---IV----" + Base64.getEncoder().encodeToString(ivBytes));
        
        byte[] ivAndCipherText = new byte[ivBytes.length + cipherText.length];
        
        System.arraycopy(ivBytes, 0, ivAndCipherText, 0, ivBytes.length);
        System.arraycopy(cipherText, 0, ivAndCipherText, ivBytes.length, cipherText.length);
        encryptedData = Base64.getEncoder().encodeToString(ivAndCipherText);
        System.out.println("---encryptedData-----" + encryptedData);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return encryptedData;
}

public static String decryptDataSymmetric(String cipherBase64Encoded, SecretKey key) {
    String encryptedData = null;
    try {
        Charset CHARSET = Charset.forName("UTF8");
         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        
        byte[] ivAndCipherText = Base64.getDecoder().decode(cipherBase64Encoded);
        
        byte[] iv = new byte[cipher.getBlockSize()];
        System.arraycopy(ivAndCipherText, 0, iv, 0, iv.length);

        byte[] cipherText = new byte[ivAndCipherText.length - iv.length];
        System.arraycopy(ivAndCipherText, iv.length, cipherText, 0, cipherText.length);
        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
        return new String(cipher.doFinal(cipherText),CHARSET);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return encryptedData;
}


    public static void main(String args[]) {
        try {
            SecureRandom securerandom = new SecureRandom();
            KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
            keygenerator.init(192, securerandom);
            SecretKey key = keygenerator.generateKey();
            System.out.println("---key---" + key.getEncoded());
        
        
            String str = "Hello bye";
            //Console.Write(decryptDataSymmetric(encryptDataSymmetric(str)));
            System.out.println(str);
            String encrypted = encryptDataSymmetric(str, key);
            String plainText = decryptDataSymmetric(encrypted, key);
            System.out.println("plaintext: " + plainText);
            System.out.println("The end!");
        } catch (Exception ex) {
        ex.printStackTrace();
    }
    }
}

答案 1 :(得分:0)

必须同意 PhazorP 以上的评论。

  • 请勿将 AES 与 CBC 单独使用
  • 使用经过身份验证的加密方案或算法。

我鼓励您使用经过身份验证的加密算法。这是我不久前写的一篇简短的博客 article,可能会有所帮助。

如果您想看一看,这里还有一些用于 AES-GCM 的 java 代码。