问题描述
我有一个在 PHP 上运行的应用程序,它有一些使用 openssl encrption 加密的值,使用下面的代码
<?PHP
define('OSSLENCKEY','14E2E2D1582A36172AE401CB826003C1');
define('OSSLIVKEY','747E314D23DBC624E971EE59A0BA6D28');
function encryptString($data) {
$encrypt_method = "AES-256-CBC";
$key = hash('sha256',OSSLENCKEY);
$iv = substr(hash('sha256',OSSLIVKEY),16);
$output = openssl_encrypt($data,$encrypt_method,$key,$iv);
$output = base64_encode($output);
return $output;
}
function decryptString($data){
$encrypt_method = "AES-256-CBC";
$key = hash('sha256',16);
$output = openssl_decrypt(base64_decode($data),$iv);
return $output;
}
echo encryptString("Hello World");
echo "<br>";
echo decryptString("MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09");
?>
我有另一个在 nodejs 上运行的端点,我需要根据上述 PHP 加密/解密规则解密和加密值。 我已经搜索过,但找不到解决方案。
我尝试使用图书馆 crypto
但最终出现错误 Reference
我尝试过的 nodejs 代码如下
message = 'MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09';
const cypher = Buffer.from(message,"base64");
const key = crypto.createHash('sha256').update('14E2E2D1582A36172AE401CB826003C1');//.digest('hex');
// $iv = substr(hash('sha256','747E314D23DBC624E971EE59A0BA6D28'),16); from PHP returns '0ed9c2aa27a31693' need nodejs equivalent
const iv = '0ed9c2aa27a31693';
const decipher = crypto.createDecipheriv("aes-256-cbc",key,iv);
console.log( decipher.update(contents) + decipher.final());
有人请帮我找到一个用于openssl加密和解密的nodejs代码
提前致谢
解决方法
代码中存在以下问题:
- 密钥在 PHP 代码中以十六进制编码返回,因此在 AES-256 的 NodeJS 代码中,只需考虑密钥的前 32 个字节(PHP 会自动执行此操作)。
- PHP 代码 Base64 隐式编码密文,因此由于显式 Base64 编码,密文被 Base64 编码两次(这是不必要的)。因此,NodeJS 代码中也需要双 Base64 编码。
另外,请注意使用静态 IV 是不安全的(但您可能只是为了测试目的而这样做)。
以下 NodeJS 代码产生与 PHP 代码相同的密文:
const crypto = require('crypto');
const plain = 'Hello World';
const hashKey = crypto.createHash('sha256');
hashKey.update('14E2E2D1582A36172AE401CB826003C1');
const key = hashKey.digest('hex').substring(0,32);
const hashIv = crypto.createHash('sha256');
hashIv.update('747E314D23DBC624E971EE59A0BA6D28');
const iv = hashIv.digest('hex').substring(0,16);
const cipher = crypto.createCipheriv('aes-256-cbc',key,iv);
var encrypted = cipher.update(plain,'utf-8','base64');
encrypted += cipher.final('base64');
encrypted = Buffer.from(encrypted,'utf-8').toString('base64');
console.log(encrypted); // MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09
encrypted = Buffer.from(encrypted,'base64').toString('utf-8');
const decipher = crypto.createDecipheriv('aes-256-cbc',iv);
var decrypted = decipher.update(encrypted,'base64','utf-8');
decrypted += decipher.final('utf-8');
console.log(decrypted); // Hello World
编辑:
如注释中所述,PHP 的 hash()
方法默认以十六进制字符串形式返回哈希值(除非第三个参数明确设置为 true
,参考代码中并非如此) .这使长度加倍,因为在这种编码中,散列的每个字节由两个十六进制数字 (hexits) 表示,即 2 个字节。
因此,有必要缩短 NodeJS 代码中的密钥(请参阅我原始答案的第一点)。这种缩短在 PHP 代码中不是必需的,因为 PHP 会隐式这样做(这实际上是一个设计缺陷,因为这样用户不会注意到密钥可能存在的问题)。
使用十六进制字符串有两个缺点:
- 对于十六进制编码的字符串,每个字节由 16 个可能的值 (0-15) 组成,而不是一个字节的 256 个可能的值 (0-255)。这将安全性从 256 位降低到 128 位(在算术上等同于 AES-128),请参阅 here。
- 根据平台的不同,十六进制 a-f 可以表示为小写或大写字母,这可能会导致不同的键和 IV(对两种情况之一没有明确的约定)。
出于这些原因,使用散列的原始二进制数据而不是十六进制编码的字符串更安全、更可靠。如果要这样做,则需要进行以下更改。
在 PHP 代码中:
$key = hash('sha256',OSSLENCKEY,true);
$iv = substr(hash('sha256',OSSLIVKEY,true),16);
在 NodeJS 代码中:
const key = hashKey.digest();
const iv = hashIv.digest().slice(0,16)
但是请注意,此版本与旧版本不兼容,即此更改之前的加密无法在更改后解密。因此必须迁移旧数据。