如何在nodejs应用程序中创建相当于php代码的openssl加密和解密

How to create openssl encryption and decryption equivalent of php code in nodejs application

我在 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), 0, 16); 
    $output = openssl_encrypt($data, $encrypt_method, $key, 0, $iv);
    $output = base64_encode($output);
    return $output;
}

function decryptString($data){
    $encrypt_method = "AES-256-CBC";
    $key = hash('sha256', OSSLENCKEY);    
    $iv = substr(hash('sha256', OSSLIVKEY), 0, 16);
    $output = openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv);     
    return $output;
}

echo encryptString("Hello World");
echo "<br>";
echo decryptString("MTZHaEoxb0JYV0dzNnptbEI2UXlPUT09");
?>

我有另一个在 nodejs 上运行的端点,我需要根据上述 php encrypt/decrypt 规则解密和加密值。 我已搜索但找不到解决方案。

我尝试使用库 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'), 0, 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', key, iv);
var decrypted = decipher.update(encrypted, 'base64', 'utf-8');
decrypted += decipher.final('utf-8');
console.log(decrypted); // Hello World

编辑:

如评论中所述,PHP的hash()方法returns 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), 0, 16); 

在 NodeJS 代码中:

const key = hashKey.digest();
const iv = hashIv.digest().slice(0, 16)

但是请注意,此版本与旧版本不兼容,即更改前的加密无法在更改后解密。所以旧数据必须迁移。