Java默认的AES / ECB / PKCS5Padding加密在php里用OpenSSL解密问题

昨天有人在群里问了一个问题,就是某平台加密是用的Java aes加密的,现在需要在php解密,因为他并不熟悉php,所以在群里求助,java的代码如下:

要想实现这个加密解密,就必须先知道Java这个默认的aes加密的模式是什么。这个在网上搜一下告知是AES / ECB / PKCS5Padding

其实我最开始直接搜到php实现java默认aes加密,就发现了一段代码

<?php

class Security
{
    public static function encrypt($input, $key)
    {
        $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $input = Security::pkcs5_pad($input, $size);
        $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        mcrypt_generic_init($td, $key, $iv);
        $data = mcrypt_generic($td, $input);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        $data = base64_encode($data);
        return $data;
    }

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

    }

    public static function decrypt($sStr, $sKey)
    {
        $decrypted = mcrypt_decrypt(
            MCRYPT_RIJNDAEL_128,
            $sKey,
            base64_decode($sStr),
            MCRYPT_MODE_ECB
        );

        $dec_s = strlen($decrypted);
        $padding = ord($decrypted[$dec_s - 1]);
        $decrypted = substr($decrypted, 0, -$padding);
        return $decrypted;
    }
}


$key = "vNh4KDg8fWhbRrtQd7n3OQ==";

$data = "isy4hRbptvsBl42JAkIbY6uA/TiiHQQr1Aj3HMEezCs=";

echo "解密字符串:" . Security::ssl_decrypt($data, base64_decode($key));

运行了一下,发现是可行的,但是php7.1后,mcrypt_系列函数就被警告或移除了,所以需要将其改成openssl系列函数,但恰恰在改成openssl的过程中,走了很多弯路。

我开始没有注意ECB模式,一直尝试的是CBC

然后一直纠结于iv的值是多少,试了各种方法,比如

 $cipher = 'aes-128-cbc';
 $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));

或者

$zeroPack = pack('i*', 0);
$iv = str_repeat($zeroPack, 4);
$iv=substr($key . '0000000000000000', 0, 16);

总之尝遍了网上的各种办法,但依旧不好使。

后来又怀疑是不是对齐方式有问题,毕竟openssl默认的对齐方式是PKCS7,于是又试图通过pkcs5填充后再加密,但还是不行。

后面几乎就是各种方案的排列组合了,但是一番折腾下来,一个小时还没搞定。

直到要放弃的时候,扫了眼AES / ECB / PKCS5Padding然后对比下我用的aes-128-cbc,突然发现一个是ecb一个是cbc,以后查了下区别。

在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。

CBC模式加解密过程如下:

可以看到的是ecb根本没有XOR运算这一步,所以根本不需要初始化向量iv.

所以,最终的代码异常简单

$key = "vNh4KDg8fWhbRrtQd7n3OQ==";

$data = "isy4hRbptvsBl42JAkIbY6uA/TiiHQQr1Aj3HMEezCs=";


$s = openssl_decrypt(base64_decode($data), 'AES-128-ECB', base64_decode($key), OPENSSL_RAW_DATA);
echo "解密字符串:" . $s,"\n";