java - javax.crypto.BadPaddingException:给定最终 block 未正确填充-奇怪的错误

标签 java security encryption cryptography aes

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 defaultCBC模式必须在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方法encryptdecrypt重命名为padunpad(或类似名称)。

如评论中已经提到的,GCM模式可能比-CBC模式更好。但是,在编码之前了解基本信息将很有用,例如difference between CBC- and GCM-mode
GCM模式herehere以及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/

相关文章:

java - 从签名的 Java 小程序安全地请求网页

c# - 在 c# 中从播放器上的 dvd 加载加密媒体文件,而无需在硬盘上复制

java - 保护字段免受反射 - System.security 的奇怪案例

html - 如何使用 Clear Key 加密 webm 文件以使用 Google 的 Shaka Player 播放?

node.js - 如何在 NodeJs 中进行基于 1-of-X 或 Y-of-X 公钥的加密/解密?

java - 将目录路径的正斜杠更改为反斜杠

java - 在 Guice 中管理同一依赖树的多个版本的最佳模式是什么?

java - JAXB - 可序列化

java - 将参数传递给 Runtime.getRuntime().exec() 并在开头添加一个选项

php - 有什么安全的方法可以允许跨站点 AJAX 请求吗?