【问题标题】:BadPaddingException while performing symmetric encryption decryption执行对称加密解密时出现 BadPaddingException
【发布时间】:2023-04-06 10:00:02
【问题描述】:

按照以下逻辑进行解密时出现异常。请在下面的代码 sn-p 中向我提出任何问题。加密很好,可以加密。

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(未知 来源)

【问题讨论】:

  • 解密时不要再生成新的密钥,需要使用加密函数中使用的那个。

标签: java exception encryption symmetric


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    必须同意上述 PhazorP 的 cmets。

    • 不要单独使用 AES 和 CBC
    • 使用经过身份验证的加密方案或算法。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-07
      • 2015-08-04
      相关资源
      最近更新 更多