记录一次OpenSSL使用DES-ECB算法加密的报错
在自己电脑(Windows)开发测试代码都没问题,但一上生产环境就报错了。经过对比,本机和服务器的PHP版本和OpenSSL版本不一样,猜测可能是这个原因导致的。经过一番查找,找到了从代码上解决问题的办法,规避了调整生产服务器的风险。
报错的代码
/**
* 字符串加密(加密方法:DES-ECB)
* @param string $data 待加密字符串
* @param string $key 对称加密密钥
* @return string
*/
function encryptData(string $data, string $key)
{
// 获取密码iv长度
$length = openssl_cipher_iv_length('DES-ECB');
// 生成一个伪随机字节串
$iv = openssl_random_pseudo_bytes($length);
// 加密数据
$ciphertext = openssl_encrypt($data, 'DES-ECB', $key, OPENSSL_RAW_DATA, $iv);
// 把包含数据的二进制字符串转换为十六进制值,然后返回结果
return bin2hex($ciphertext);
}
报错信息
Fatal error: Uncaught Error: Length must be greater than 0 in frame.php:87
Stack trace:
#0 frame.php(87): openssl_random_pseudo_bytes()
#1 frame.php(60): encryptData()
#2 {main} thrown in frame.php on line 87
根据报错信息,主要问题是openssl_cipher_iv_length()
返回的长度为0
,而openssl_random_pseudo_bytes()
的参数的长度必须大于0
,所以就产生了报错。
但是openssl_cipher_iv_length()
为什么返回0
呢?难道是不支持DES-ECB
加密方法?
使用openssl_get_cipher_methods()
方法获取可用的加密算法的列表,发现DES-ECB
在列表内,那应该是支持的!
这时候,我就猜想是不是openssl低版本的BUG,因为以前也见过一些因为openssl版本过低导致的问题,于是继续Google一番。
经过查找,发现了两条有用的结果:
- https://stackoverflow.com/questions/64477558/how-to-fix-length-must-be-greater-than-0-in-openssl-random-pseudo-bytes0
- https://github.com/noprotocol/php-mysql-aes-crypt/issues/12
在第一条中得知ECB 加密模式是不安全的,因为它没有初始化矢量
,openssl_cipher_iv_length()
返回的长度为0
的原因就得知了。
但结论没有经过仔细论证。也不知道为什么java为什么要ECB。
在第二条中找到了调整代码的思路。
最终得到了以下没有报错的代码~
/**
* 字符串加密(加密方法:DES-ECB)
* @param string $data 待加密字符串
* @param string $key 对称加密密钥
* @return string
*/
function encryptData(string $data, string $key)
{
// 获取密码iv长度
$length = openssl_cipher_iv_length('DES-ECB');
// 生成一个伪随机字节串
if ($length > 0) {
$iv = openssl_random_pseudo_bytes($length);
} else {
$iv = '';
}
// 加密数据
$ciphertext = openssl_encrypt($data, 'DES-ECB', $key, OPENSSL_RAW_DATA, $iv);
// 把包含数据的二进制字符串转换为十六进制值,然后返回结果
return bin2hex($ciphertext);
}