问题描述
按照以下逻辑进行解密时出现异常。请在下面的代码片段中向我建议任何问题。加密没问题,可以加密。
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,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,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 专家,但您需要从 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,ivAndCipherText,ivBytes.length);
System.arraycopy(cipherText,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,iv,iv.length);
byte[] cipherText = new byte[ivAndCipherText.length - iv.length];
System.arraycopy(ivAndCipherText,iv.length,cipherText,cipherText.length);
cipher.init(Cipher.DECRYPT_MODE,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();
}
}
}
,
必须同意 PhazorP 以上的评论。
- 请勿将 AES 与 CBC 单独使用
- 使用经过身份验证的加密方案或算法。
我鼓励您使用经过身份验证的加密算法。这是我不久前写的一篇简短的博客 article,可能会有所帮助。
如果您想看一看,这里还有一些用于 AES-GCM 的 java 代码。