如何将PHP的openssl_seal函数移植到Node.js 18?
Node.js 适配 PHP
openssl_seal() 功能方案 问题背景
需要将PHP中可正常运行的openssl_seal()加密逻辑移植到Node.js,PHP后端通过openssl_open()解密,但自行编写的Node.js代码输出格式正确,却始终无法被后端解密成功。
PHP 参考代码
加密逻辑
$ivLength = openssl_cipher_iv_length('AES-256-CBC') ?: 16; $iv = openssl_random_pseudo_bytes($ivLength); openssl_seal( data: json_encode($data), sealed_data: $sealed, encrypted_keys: $encryptedKeys, public_key: [openssl_get_publickey("my-public-key")], cipher_algo: 'AES-256-CBC', iv: $iv ); return [ 'context' => base64_encode($sealed), 'envelope' => base64_encode($encryptedKeys[0]), 'vector' => base64_encode($iv), ];
解密逻辑
$privateKey = openssl_pkey_get_private("my-private-key"); openssl_open( data: base64_decode($data['context']), output: $context, encrypted_key: base64_decode($data['envelope']), private_key: $privateKey, cipher_algo: 'AES-256-CBC', iv: base64_decode($data['vector']) );
原Node.js代码问题
const algorithm = "aes-256-cbc"; const publicKey = loadPublicKey(); const key = Buffer.from(crypto.randomBytes(32), "binary"); const ivLength = crypto.randomBytes(16).length; const iv = crypto.randomBytes(ivLength); const cipher = crypto.createCipheriv(algorithm, key, iv); let sealedData = cipher.update(data, "utf8", "binary"); sealedData += cipher.final("binary"); const encryptedKeys = crypto.publicEncrypt(publicKey, iv); return { context: Buffer.from(sealedData).toString("base64"), envelope: encryptedKeys.toString("base64"), vector: iv.toString("base64"), };
存在两个核心错误:
- 加密对象错误:
openssl_seal()的核心逻辑是用公钥加密随机生成的AES密钥,而非IV。原代码错误地加密了IV,导致后端无法解密出正确的AES密钥。 - 冗余的密钥处理:
Buffer.from(crypto.randomBytes(32), "binary")完全多余,crypto.randomBytes(32)直接返回符合要求的Buffer类型密钥。
修正后的Node.js代码
const crypto = require('crypto'); const algorithm = "aes-256-cbc"; function sealData(data, publicKey) { // 生成AES-256所需的32字节随机密钥 const aesKey = crypto.randomBytes(32); // 生成AES-CBC标准的16字节IV const iv = crypto.randomBytes(16); // 加密原始数据(对齐PHP的json_encode+AES加密流程) const cipher = crypto.createCipheriv(algorithm, aesKey, iv); let sealedData = cipher.update(JSON.stringify(data), 'utf8'); sealedData = Buffer.concat([sealedData, cipher.final()]); // 用公钥加密AES密钥,显式指定PKCS1填充(和PHP默认逻辑一致) const encryptedAesKey = crypto.publicEncrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_PADDING }, aesKey); return { context: sealedData.toString('base64'), envelope: encryptedAesKey.toString('base64'), vector: iv.toString('base64') }; }
关键适配点
- 严格对齐
openssl_seal()的流程:生成随机AES密钥→用AES密钥+IV加密数据→用公钥加密AES密钥 - 显式指定RSA填充模式,避免环境差异导致的兼容问题
- 使用Buffer拼接加密结果,避免二进制编码转换错误
内容的提问来源于stack exchange,提问作者Levente Otta
相关产品推荐
相关产品推荐

