使用PHP和nodejs进行通讯时候遇到双方加解密结果不一致的问题。
注意到crypto.createCipher(algorithm, password[, options])
方法有如下的提示。
text
The implementation of crypto.createCipher() derives keys using the OpenSSL
function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
createCipher
方法只接受key
,不接受iv
参数,iv
参数是使用openssl
的EVP_BytesToKey
方法对key
进行扩展得来的,salt
为空,hash
算法为MD5
。
可以使用新的createCipheriv
方法代替,但原来的NodeJs代码不能修改,只能从php方面入手修改。
简易实现-使用php对key进行扩展,再进行加解密。
php
function EVP_BytesToKey($salt, $password) {
$bytes = '';
$last = '';
while(strlen($bytes) < 48) {
$last = hash('md5', $last . $password . $salt, true);
$bytes.= $last;
}
return $bytes;
}
//从扩展结果中分离真正用于通讯的key和iv
$key = '12345678901234561234567890123456';
$devd = EVP_BytesToKey('', $key);
$key = substr($devd, 0, 32);
$iv = substr($devd, 32, 16);
@openssl_encrypt('test-data', "aes-256-cbc", $key, 1, $iv);
nodejs直接使用key进行加解密
js
const key = '12345678901234561234567890123456';
crypto.createCipher("aes-256-cbc", key)
EVP_BytesToKey的完整实现
php
/**
* @param string $password
* @param int $nkey 需要生成的密钥长度
* @param int $niv 需要生成的iv长度
* @param string $hash HASH算法,默认MD5
* @param string $salt salt。默认为空
* @param int $round HASH运行轮数
* @return array
*/
function EVP_BytesToKey(string $password, int $nkey = 32, int $niv = 16, string $hash = 'md5', string $salt = '', int $round = 1): array
{
$bytes = '';
$last = '';
$total = $nkey + $niv;
while (strlen($bytes) < $total) {
$last = hash($hash, $last . $password . $salt, true);
for ($i = 1; $i < $round; $i++) {
$last = hash($hash, $last, true);
}
$bytes .= $last;
}
return [
'key' => substr($bytes, 0, $nkey),
'iv' => substr($bytes, $nkey, $niv),
];
}