javax.crypto.BadPaddingException:给最终块未正确填充-这是我得到的错误。我不知道为什么。我的代码似乎还好。加密和解密期间的密钥相同。我可以说的是,已经回答的问题都没有包含解决我的问题的方法。这是我的代码:
public String decrypt(String text) throws Exception {
String key = "SiadajerSiadajer"; // 128 bit key
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
byte[]encrypted = text.getBytes();
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted)); //Here is the error
return decrypted;
}
以及php中的加密机制
function encrypt($data, $size)
{
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function decrypt($data)
{
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $iv);
编辑
现在在php部分中,我的代码是:
$key = "SiadajerSiadajer";
$iv_size = 16; // 128 bits
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$EIV = base64_encode($iv);
$name = openssl_encrypt(encrypt($name, 16), 'AES-256-CBC', $key, 0, $EIV);
它给了我警告:警告:openssl_encrypt():传递的IV为24个字节长,比所选密码预期的16个字节长,在第68行的C:\ xampp2 \ htdocs \ standardfinalinserting.php中被截断
在Java部分中,我的解密方法与我的问题的答案完全相同,但运行后给我一个错误:java.security.InvalidKeyException:行上的密钥大小非法:
cipher.init(Cipher.DECRYPT_MODE,aesKey,ivParameterSpec);
编辑2:
这是我整个班级。它包含您的整个Java代码示例
public class Main {
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec); //HERE IS THE ERROR
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
}
运行代码会导致错误:
Exception in thread "main" java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.dd.escuel.Main.decrypt(Main.java:43)
at com.dd.escuel.Main.main(Main.java:57)
最佳答案
Java代码存在一些问题:
因此,在PHP代码中使用AES-256,密钥的长度必须为32字节。较短的键将自动用零填充。因为您的键SiadajerSiadajer
的长度只有16字节,所以这在您的PHP代码中发生。键盘填充也必须用Java代码完成。为此,例如可以使用以下Java方法:
private byte[] padKey(byte[] key) {
byte[] paddedKey = new byte[32];
System.arraycopy(key, 0, paddedKey, 0, key.length);
return paddedKey;
}
使用
Cipher.getInstance("AES")
时,选择ECB
模式和PKCS5Padding
by default。 CBC
模式必须在Java代码中用Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
在PHP方法
openssl_encrypt
中,第四个参数$options
设置为0
,这意味着返回的数据是Base64编码的。因此,在Java代码中,解密之前必须对数据进行Base64解码:byte[]encryptedData = Base64.decode(text);
由于使用了
CBC
模式,因此必须考虑加密的IV
。一种可能的方法是使用以下代码对PHP代码中的IV
进行Base64编码(加密之后):$encodedIV = base64_encode($iv);
并将该值作为第二个参数传递给Java方法
decrypt
。在这里,必须解码IV
并将其用于解密:byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
...
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
在PHP方法
openssl_encrypt
中,第四个参数$options
设置为0
,这意味着将使用默认的填充(PKCS7
)。此外,在PHP方法encrypt
中,实现了一种自定义填充(btw:该方法的名称由于不加密而不合适),因此将其填充两次。因此,解密后,必须在Java代码中删除自定义填充(可能由空格组成):byte[] unpaddedData = unpad(decryptedData);
与
private byte[] unpad(byte[] data) {
byte[] unpaddedData = new byte[data.length - data[data.length - 1]];
System.arraycopy(data, 0, unpaddedData, 0, unpaddedData.length);
return unpaddedData;
}
共:
public String decrypt(String text, String ivEncoded) throws Exception {
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Base64 decode data
byte[]encryptedData = Base64.decode(text);
// Base64 decode iv
byte[] iv = Base64.decode(ivEncoded);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
测试:
PHP代码输入:纯文本(
$data
),键($key
):$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
PHP代码输出:Base64编码的IV(
$encodedIV
),加密的数据($name
):$encodedIV = 'Dg+Zs3mqIJeDOOEPMT5F4Q==';
$name = '8dXjeQhx2WSswQOQXLcyMKNpa5s413yI2Ku8WiIB/xtA2pEjrKcl5kWtrOh9k4A12Jl0N/z6tH67Wybhp/OwTi1NtiJOZxl3w6YQufE29oU=';
如果将该输出用作Java方法
decrypt
的输入,则解密后的数据等于纯文本。关于PHP代码,我建议删除自定义或默认(
PKCS7
)填充(如果可以选择的话)。后者可以通过使用标志OPENSSL_ZERO_PADDING
作为openssl_encrypt
方法中的第四个参数来实现(请注意:该标志不是指“填充零值”,而是“不填充”)。如果保留自定义填充,则应至少将PHP方法encrypt
和decrypt
重命名为pad
和unpad
(或类似名称)。如评论中已经提到的,
GCM
模式可能比-CBC
模式更好。但是,在编码之前了解基本信息将很有用,例如difference between CBC
- and GCM
-mode,GCM模式here和here以及pitfalls与GCM模式一起提供的说明(GCM是安全的,但前提是您遵循某些准则,例如,使用相同的键)。
您可以使用PHP方法openssl_get_cipher_methods找出PHP支持的允许的AES模式。 here对此进行了更详细的说明。对于AES-256和AES模式
GCM
,您必须指定aes-256-gcm
(使用小写(!)字母)。据推测,这就是为什么您会收到“未知密码算法”错误的原因。编辑:
您可以使用以下PHP代码进行加密(从问题中对PHP代码进行了稍微修改):
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, 0, $iv);
print base64_encode($iv)."\n".$encryptedData."\n";
编辑2:
IV
和加密的数据可以在Base64编码之前或之后加入。第一种方法效率更高,因此,我实现了此变体。但是,PHP和Java代码需要进行一些更改,因此,我发布了所有已更改的方法。PHP代码变为:
<?php
function pad($data, $size) {
$length = $size - strlen($data) % $size;
return $data . str_repeat(chr($length), $length);
}
function unpad($data) {
return substr($data, 0, -ord($data[strlen($data) - 1]));
}
$data = 'This is a plain text which needs to be encrypted...';
$key = "SiadajerSiadajer";
$iv_size = 16;
$iv = openssl_random_pseudo_bytes($iv_size, $strong);
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$joinedData = $iv.$encryptedData;
$encodedJoinedData = base64_encode($joinedData);
print $encodedJoinedData."\n";
Jave方法
decrypt
变为:public String decrypt(String encodedJoinedData) throws Exception {
// Base64-decode the joined data
byte[] joinedData = Base64.decode(encodedJoinedData);
// Get IV and encrypted data
byte[] iv = new byte[16];
System.arraycopy(joinedData, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[joinedData.length - iv.length];
System.arraycopy(joinedData, iv.length, encryptedData, 0, encryptedData.length);
// Pad key
byte[] key = padKey("SiadajerSiadajer".getBytes());
Key aesKey = new SecretKeySpec(key, "AES");
// Specify CBC-mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt data
byte[] decryptedData = cipher.doFinal(encryptedData);
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
}
Java方法
main
的示例是:public static void main(String[] args) throws Exception {
String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
String decryptedData = new Main().decrypt(encodedJoinedData);
System.out.println(decryptedData + " - " + decryptedData.length());
}
用法如下:
用PHP代码加密纯文本。在上面的示例(PHP代码)中,纯文本为
$data = 'This is a plain text which needs to be encrypted...';
然后,将$ encodedJoinedData中包含的字符串传递给Java方法
decrypt
。在上面的示例(main
-方法)中,字符串为String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
Java方法
decrypt
将提供初始的纯文本。最后说明:如果您决定删除(冗余)自定义填充,请在PHP代码中替换以下行:
$encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
与
$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
并删除
pad
-和unpad
方法。在Java代码中替换行
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
与
return new String(decryptedData);
并删除
unpad
方法。编辑3:
问题的Edit2部分提到的
InvalidKeyException (Illegal key size)
已经讨论过了,例如在这里InvalidKeyException Illegal key size和这里Java Security: Illegal key size or default parameters?。
关于java - javax.crypto.BadPaddingException:给定最终 block 未正确填充-奇怪的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54597331/