javax.crypto.BadPaddingException:给定的最后一个块没有正确填充-奇怪的错误

javax.crypto.BadPaddingException:给定的最后一个块没有正确填充-奇怪的错误,java,security,encryption,cryptography,aes,Java,Security,Encryption,Cryptography,Aes,javax.crypto.BadPaddingException:给定的最后一个块没有正确填充-这就是我得到的错误。我不知道为什么。我的代码似乎没问题。加密和解密期间密钥相同。我能说的是,已经回答的问题中没有一个包含我问题的解决方案。这是我的密码: public String decrypt(String text) throws Exception { String key = "SiadajerSiadajer"; // 128 bit key // Create key

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_模式,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代码有几个问题:

byte[] unpaddedData = unpad(decryptedData);
  • 在PHP代码中使用AES-256,因此,密钥的长度必须为32字节。较短的关键点会自动右键填充零。这发生在PHP代码中,因为键
    SiadajerSiadajer
    的长度只有16字节。键填充也必须在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
    。必须在Java代码中使用

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

    byte[]encryptedData = Base64.decode(text); 
    
  • 由于使用了
    CBC
    -模式,因此必须考虑加密的
    IV
    。一种可能的方法是使用Base64在PHP代码(加密之后)中对
    IV
    进行编码

    并将该值作为第二个参数传递给Java方法
    decrypt
    。在此,必须对
    IV
    进行解码并用于解密:

    byte[] iv = Base64.decode(ivEncoded);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); 
    ...
    cipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
    
  • 在PHP方法
    openssl\u encrypt
    中,第四个参数
    $options
    设置为
    0
    ,这意味着使用默认填充(
    PKCS7
    )。此外,在PHP方法
    encrypt
    中,实现了一种自定义填充(顺便说一句:该方法的名称不合适,因为它不加密),因此,它被填充了两次。因此,解密后,必须在Java代码中删除自定义填充(可能包含空格):

    byte[] unpaddedData = unpad(decryptedData);
    

  • 总共:

    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
    ):

    PHP代码输出:Base64编码IV(
    $encodedIV
    ),加密数据(
    $name
    ):

    如果该输出用作Java方法
    decrypt
    的输入,则解密的数据等于纯文本

    关于PHP代码,我建议删除自定义或默认(
    PKCS7
    )填充(如果您有选择的话)。后者可以通过使用标志
    OPENSSL\u ZERO\u PADDING
    作为
    OPENSSL\u encrypt
    方法中的第四个参数来实现(注意:该标志不是指“带零值的pad”,而是“无填充”)。如果保留了自定义填充,则至少应将PHP方法
    encrypt
    decrypt
    分别重命名为
    pad
    unpad
    (或类似的名称)

    如评论中所述,
    GCM
    -模式可能比-
    CBC
    模式更好。但是,在编码之前了解基本知识是很有用的,例如, 解释GCM模式以及随GCM模式一起出现的(GCM是安全的,但前提是您遵循某些指导原则,例如,使用相同密钥加密的每条消息的uniqe IV/nonce)

    您可以使用PHP方法找出PHP中支持的允许AES模式。这将得到更详细的解释。对于AES-256和AES模式
    GCM
    ,必须指定
    AES-256-GCM
    (小写(!)字母)。这大概就是为什么会出现“未知密码算法”——错误

    编辑:

    您可以使用以下PHP代码进行加密(这是问题中PHP代码的稍微修改版本):

    Jave方法
    解密
    变为:

    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
    -method)中,字符串是

    String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
    
  • Java方法
    decrypt
    将提供初始纯文本

  • 最后一点注意:如果您决定删除PHP代码中的(冗余)自定义填充,请替换该行

    $encryptedData = openssl_encrypt(pad($data, 16), 'AES-256-CBC', $key,  OPENSSL_RAW_DATA, $iv);
    

    并移除
    pad
    -和
    unpad
    -方法

    在Java代码中,替换行

    // Remove custom padding
    byte[] unpaddedData = unpad(decryptedData);         
    
    return new String(unpaddedData);
    

    并删除
    unpad
    -方法

    编辑3:


    问题的Edit2部分中提到的
    InvalidKeyException(非法密钥大小)
    已经在这里和这里进行了讨论。

    Java代码有几个问题:

    byte[] unpaddedData = unpad(decryptedData);
    
  • 在PHP代码中使用AES-256,因此,密钥的长度必须为32字节。较短的关键点会自动右键填充零。这发生在PHP代码中,因为键
    SiadajerSiadajer
    的长度只有
    $data = 'This is a plain text which needs to be encrypted...';
    
    String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
    
    $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);
    
    // Remove custom padding
    byte[] unpaddedData = unpad(decryptedData);         
    
    return new String(unpaddedData);
    
    return new String(decryptedData);