0

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: Illegal key size on the line:

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

1 回答 1

2

Java代码有几个问题:

  1. 在 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;
    }
    
  2. 使用-modeCipher.getInstance("AES")默认选择。-mode 必须在 Java 代码中显式指定ECBPKCS5PaddingCBC

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

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

    $encodedIV = base64_encode($iv);
    

    并将该值decrypt作为第二个参数传递给 Java 方法。在这里,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-methodsencryptdecrypttopadunpad(或类似的东西)。

如评论中所述,GCM-mode 可能是比-mode 更好的选择CBC。但是,在编码之前了解基础知识会很有用,例如- 和-mode之间的区别、CBCGCM此处此处的 GCM 模式解释以及 GCM 模式带来的陷阱(GCM 是安全的,但前提是您遵循某些准则,例如使用相同密钥加密的每条消息的唯一IV/nonce)。

您可以使用 PHP 方法openssl_get_cipher_methods找出 PHP 支持的允许 AES 模式。此处将对此进行更详细的说明。对于 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());
}

用法如下:

  1. 使用 PHP 代码加密您的纯文本。在上面的示例(PHP 代码)中,纯文本是

    $data = 'This is a plain text which needs to be encrypted...';
    
  2. 然后,将 $encodedJoinedData 中包含的字符串传递给 Java-method decrypt。在上面的示例(main-method)中,字符串是

    String encodedJoinedData = "RB6Au33KGOF1/z3BQKqievUmh81Y8q4uw7s4vEs9xurQmNZAKwmhRQtS9wGYKQj8cJaPpK2xaDzx3RppQ8PsM/rQ9/p0Lme+x75dozBEbmFn6Q9eCXOCiCivVsKzZ8Vz";
    
  3. 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:

问题的InvalidKeyException (Illegal key size)Edit2部分中提到的已经讨论过,例如这里InvalidKeyException Illegal key size和这里​​ Java Security: Illegal key size or default parameters?.

于 2019-02-09T14:27:05.020 回答