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

时间:2019-02-08 17:15:00

标签: 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:

这是我的整个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)

1 个答案:

答案 0 :(得分:2)

Java代码存在一些问题:

  1. 在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;
    }
    
  2. 使用Cipher.getInstance("AES")ECB模式和PKCS5Padding被选择为by defaultCBC模式必须在Java代码中使用

    明确指定
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    
  3. 在PHP方法openssl_encrypt中,第四个参数$options设置为0,这意味着返回的数据是Base64编码的。因此,在Java代码中,解密之前必须对数据进行Base64解码:

    byte[]encryptedData = Base64.decode(text); 
    
  4. 由于使用了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);
    
  5. 在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方法encryptdecrypt重命名为padunpad(或类似名称)。

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

用法如下:

  1. 使用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());
    }
    
  2. 然后,将$ encodedJoinedData中包含的字符串传递给Java方法$data = 'This is a plain text which needs to be encrypted...'; 。在上面的示例(decrypt方法)中,字符串为

    main
  3. 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 sizeJava Security: Illegal key size or default parameters?