小编前几天在做一个数据加密的时候,发现php中的openssl_decrypt竟然解不开密文, 此景之下只好寻找答案,并总结下结果。
场景:
使用python加密的一个数据,方法如下:
#AES加密
def aes_encrypt(text, key, mode, iv):
import base64
cryptor = AES.new(key, mode, iv)
length = 16
count = len(text)
if (count % length) != 0:
add = length - (count % length)
else:
add = 0
text = text + ('\0' * add)
ciphertext = cryptor.encrypt(text)
return base64.b64encode(ciphertext)
#AES解密
def aes_decrypt(text, key, mode, iv):
cryptor = AES.new(key, mode, iv)
plain_text = cryptor.decrypt(text)
return plain_text.rstrip('\0').strip()
#测试
aes_key = '123456ABCD!@#$%^'
aes_iv = '123456ABCD!@#$%^'
content = '0123456789123456'
aes_encrypt(content,aes_key, AES.MODE_CBC, aes_iv)#输出FSaxaPsYOrt063lkbocFnQ==
然后交给php使用openssl_decrypt解密,一直返回false,但是python解密是没问题的。苦恼之下,小编使用了php已经废弃的函数mcrypt_decrypt进行解密,发现可行,代码如下:
<?php
$aes_key = '123456ABCD!@#$%^';
$aes_iv = '123456ABCD!@#$%^';
$content = "FSaxaPsYOrt063lkbocFnQ==";
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $aes_key, base64_decode($content), MCRYPT_MODE_CBC, $aes_iv);
echo rtrim($decrypted, "\0")."\n";//成功
$decrypted1 = openssl_decrypt($content, 'AES-128-CBC', $aes_key,0, $aes_iv);
echo rtrim($decrypted1, "\0")."\n";//失败
为什么mcrypt_decrypt能解开,openssl_decrypt却解不开?
后来小编研究了下,明白了,也顺便回答了下这位仁兄的问题(https://segmentfault.com/q/1010000012199406)。
其实归根结底是openssl_decrypt解密方式不对,options参数使用OPENSSL_ZERO_PADDING或者是2,就能解开。
<?php
$aes_key = '123456ABCD!@#$%^';
$aes_iv = '123456ABCD!@#$%^';
$content = "FSaxaPsYOrt063lkbocFnQ==";
$decrypted1 = openssl_decrypt($content, 'AES-128-CBC', $aes_key,2, $aes_iv);
echo rtrim($decrypted1, "\0")."\n";//bingo
为什么呢?
因为在python加密的时候小编采用的是补0方式进行填充加密,但是在openssl_decrypt中options参数0和1都是默认采用
PKCS7方式去填充的,所以解密失败,而参数为2时,则是采用去0方式解密的,刚好对应。
相关知识
openssl_encrypt($data, $method, $key, $options = 0, $iv = '') : 以指定方式 method 和密钥 key 加密 data, 返回 false 或加密后的数据.
- data : 明文
- method : 加密算法
- key : 密钥
- options :
- 0 : 自动对明文进行 padding, 返回的数据经过 base64 编码.(默认PKCS7填充方式)
- 1 : OPENSSL_RAW_DATA, 自动对明文进行 padding, 但返回的结果未经过 base64 编码.(默认PKCS7填充方式)
- 2 : OPENSSL_ZERO_PADDING, 自动对明文进行 0 填充, 返回的结果经过 base64 编码. 但是, openssl 不推荐 0 填充的方式, 即使选择此项也不会自动进行 padding, 仍需手动 padding.
- iv : 非空的初始化向量, 不使用此项会抛出一个警告. 如果未进行手动填充, 则返回加密失败.
参考文章:
三种填充模式的区别(PKCS7Padding/PKCS5Padding/ZeroPadding):
https://blog.youkuaiyun.com/panjiapengfly/article/details/102757763
PHP由mcrypt扩展加密改为openssl扩展加密:
https://www.xxling.com/blog/article/3114.aspx
https://www.cnblogs.com/z1298703836/p/9252612.html