关于mcrypt_encrypt和openssl_encrypt加密结果不一致的解决

[ 2018-04-23 ]


最近想将php5.6升级到php7.2,翻阅兼容性文档,发现mcrypt已被移除,官方建议用openssl代替,原先项目用到mcrypt,于是进行替换测试。

$key = '1234567890123456';   //16字节
$iv = '1234567890123456';    //16字节
$str = 'abcdefg测试密文......';

原加密代码:

$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_CBC, $iv);
var_dump(base64_encode($encrypted)) ;

替换代码

$encrypted = openssl_encrypt($str, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $iv);
var_dump(base64_encode($encrypted)) ;

同样的key和iv结果竟然不同

string(44) “iOjsba+z8aP9JBLGzl5qyby3doyqLbgmggExPt6Z8Go=”
string(44) “iOjsba+z8aP9JBLGzl5qyZAa7Usw/KSBWndy+ypyPDs=”

翻阅大量文档,得出点结论
1.mcrypt加密前默认对原文进行padding,padding方式是用0填充
2.openssl的option有三个选项:
OPENSSL_RAW_DATA 会用PKCS#7进行补位
OPENSSL_ZERO_PADDING 看字面意思,是用0填充,但是测试并不起作用,有了解的希望解答一下
OPENSSL_NO_PADDING 不填充,需要手动填充
3.如果要让openssl的加密结果和原先的mcrypt一样,必须要手动用0填充,然后再用openssl加密

再次测试,在openssl_encrypt前加上填充过程

$str_padded = $str;
if (strlen($str_padded) % 16) {
  $str_padded = str_pad($str_padded,strlen($str_padded) + 16 - strlen($str_padded) % 16, "\0");
}
$encrypted = openssl_encrypt($str_padded, 'aes-128-cbc', $key, OPENSSL_NO_PADDING , $iv);
var_dump( base64_encode($encrypted));

结果一致,如下

string(44) “iOjsba+z8aP9JBLGzl5qyby3doyqLbgmggExPt6Z8Go=”

解密就简单的多了,直接把openssl的option设为OPENSSL_NO_PADDING就可以了

$decrypted =openssl_decrypt( base64_decode($str) , 'aes-128-cbc', $key, OPENSSL_NO_PADDING, $iv);
var_dump( rtrim( rtrim( $decrypted,chr(0) ), chr(7) ) );

结尾要去除填充字符’\0’和’\a’。
‘\a’是为了兼容用OPENSSL_RAW_DATA加密的结果。