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:
这是我的整个Main Class。它包含您的整个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)
答案 0 :(得分:2)
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
。一种可能的方法是使用加密密码将IV
用PHP代码(在加密之后)
$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
中,实现了一种自定义填充(顺便说一句:由于该方法的名称不加密,因此该方法的名称不合适),因此将其填充两次。因此,解密后,必须在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
(或类似名称)。
正如评论中已经提到的那样,{-{1}}模式可能比-GCM
模式更好。但是,在 编码之前了解基本信息会很有用,例如difference between CBC
- and GCM
-mode,
GCM模式here和here以及pitfalls和GCM模式一起的解释(GCM是安全的,但前提是您遵循某些准则,例如 uniqe 对于使用相同密钥加密的每封邮件,IV /一次。
您可以使用PHP方法openssl_get_cipher_methods来查找PHP支持的允许的AES模式。 here对此进行了更详细的说明。对于AES-256和AES模式的CBC
,您必须指定GCM
(带有小写(!)字母)。据推测,这就是为什么您会收到“未知密码算法”错误的原因。
编辑:
您可以使用以下PHP代码(从问题中对您的PHP代码进行稍微修改的版本)进行加密:
aes-256-gcm
编辑2:
<?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";
和加密的数据可以在Base64编码之前 或 之后连接。第一种方法效率更高,因此,我实现了此变体。但是,PHP和Java代码需要进行一些更改,因此,我将发布所有已更改的方法。
PHP代码变为:
IV
Jave方法<?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";
变为:
decrypt
Java方法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);
}
的示例是:
main
用法如下:
使用PHP代码加密纯文本。在上面的示例(PHP代码)中,纯文本为
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());
}
然后,将$ encodedJoinedData中包含的字符串传递给Java方法$data = 'This is a plain text which needs to be encrypted...';
。在上面的示例(decrypt
方法)中,字符串为
main
Java方法String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
将提供初始的纯文本。
最后的提示:如果您决定删除(多余的)自定义填充,请在PHP代码中替换行
decrypt
与
$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
方法。
在Java代码中,替换行
unpad
使用
// Remove custom padding
byte[] unpaddedData = unpad(decryptedData);
return new String(unpaddedData);
并删除return new String(decryptedData);
方法。
修改3:
问题的Edit2部分中提到的unpad
已被讨论过,例如InvalidKeyException Illegal key size和Java Security: Illegal key size or default parameters?。