使用 PHP 获得与 java 相同的 AES-128-CBC 加密

Use PHP to get the same AES-128-CBC encryption as java does

我知道围绕 Whosebug 有很多关于 PHP Java AES CBC 加密的讨论,但是 none 解决了我的问题

这里是java函数:

public static String encrypt(String input, String key){
    byte[] crypted = null;

    SecretKeySpec skey = new SecretKeySpec(getHash("MD5", key), "AES");
    IvParameterSpec iv = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skey, iv);
    crypted = cipher.doFinal(input.getBytes());
    return new String(Base64.encodeBase64(crypted));
}

 private static byte[] getHash(String algorithm, String text) {
    try {
        byte[] bytes = text.getBytes("UTF-8");
        final MessageDigest digest = MessageDigest.getInstance(algorithm);
        digest.update(bytes);
        return digest.digest();
    } catch (final Exception ex) {
        throw new RuntimeException(ex.getMessage());
    }
}

这是我在 PHP

中所做的
public static function encrypt($input, $key) {

    $key = hash('md5', $key, true);
    $iv = '0000000000000000';
    return openssl_encrypt($input, 'aes-128-cbc', $key, 0, $iv);
}

让我们看看结果

key:"790757e76c8942f995675b247aa57c2a"
input:"1234"

result in java:UfczMtIAm8ewSuIGRdPTDQ==
result in PHP:wd/OTHoIXwgHGDHcj8OTgg==    

在php中,我也有使用其他的方法,比如mcrypt_encryptmcrypt_generic加上pkcs5 padding(见代码blow),它们都可以得到相同的加密方式openssl_encrypt 做到了,但仍然与 Java 函数结果不一样

public static function encrypt($input, $key) {

    $key = hash('md5', $key, true);


    $iv = '0000000000000000';

    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $input = Security::pkcs5_pad($input, $size);


    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $input, MCRYPT_MODE_CBC, $iv);

    // output wd/OTHoIXwgHGDHcj8OTgg==
    echo base64_encode($encrypted);

    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

    mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $data = base64_encode($data);

    // output wd/OTHoIXwgHGDHcj8OTgg==
    echo $data;
}

private static function pkcs5_pad ($text, $blocksize) {
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

我注意到另一件奇怪的事情,如果我将密码方法从 CBC 更改为 ECB,java 和 php 加密将是相同的,但遗憾的是访问第 3 方 API, 我必须使用 CBC

终于想通了

$iv = '0000000000000000';

应该替换为

$iv = str_repeat(chr(0), 16);

我从其他地方复制它并认为 '0000000000000000' 等于 IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }),那是完全错误的..