javascript - AES 加密 PHP 到 NodeJS?
问题描述
我正在将一个小项目从 PHP 转移到 NodeJS,其中包括一小部分 AES 加密。
由于 PHP 代码运行良好,因此它是
function decysek($data, $app_key) {
$output = openssl_decrypt(base64_decode($data), 'AES-256-ECB', $app_key, OPENSSL_RAW_DATA);
return $output;
}
function decyGetBillData($rek , $data , $decrypted_sek){
$decrypted_rek = openssl_decrypt(base64_decode($rek), 'AES-256-ECB', $decrypted_sek, OPENSSL_RAW_DATA);
$decrypted_data = openssl_decrypt(base64_decode($data), 'AES-256-ECB', $decrypted_rek, OPENSSL_RAW_DATA);
return $decrypted_data;
}
$sekdec = decysek($request['sek'], $request['appKey']);
$data = decyGetBillData($response['rek'], $response['data'], $sekdec);
echo json_decode($data, true);
同理的NodeJS转换如下
var aes256 = require("aes256");
var js_base64_1 = require("js-base64");
function decysek(data, app_key) {
var cipher = aes256.createCipher(app_key);
var output = cipher.decrypt(js_base64_1.Base64.decode(data));
return output;
}
function decyGetBillData(rek, data, decrypted_sek) {
var cipher = aes256.createCipher(decrypted_sek);
var decrypted_rek = cipher.decrypt(js_base64_1.Base64.decode(rek));
var cipher2 = aes256.createCipher(decrypted_rek);
var decrypted_data = cipher2.decrypt(js_base64_1.Base64.decode(data));
return decrypted_data;
}
var sekdec = decysek(request["sek"], request["appKey"]);
var data = decyGetBillData(response["rek"], response["data"], sekdec);
console.log(data);
NodeJS 版本有一些问题,因为它没有给我输出,而是抛出一个错误。
提供的“加密”必须解密为非空字符串。
你能弄清楚这个问题吗?
解决方案
Node.jsaes256
模块不支持您的 PHP 加密算法 AES-256-ECB。它使用 AES-256-CTR 进行加密,使用 SHA256 作为密钥派生函数。IV 是随机生成的,并附加到密文中。
如果你想使用这个模块,你应该能够使用下面的函数在 PHP 中加密 - 解密你的数据。
/**
* Encrypts data with the supplied passphrase, using AES-256-CTR.
*
* @param string $plaintext the plaintext data.
* @param string $passphrase a passphrase/password.
* @return string|false encrypted data: iv + ciphertext or `false` on error.
*/
function encrypt($plaintext, $passphrase) {
$key = hash('SHA256', $passphrase, true);
$iv = openssl_random_pseudo_bytes(16);
$ct = openssl_encrypt($plaintext, 'AES-256-CTR', $key, 1, $iv);
return base64_encode($iv.$ct);
}
/**
* Decrypts data with the supplied passphrase, using AES-256-CTR.
*
* @param string $ciphertext encrypted data.
* @param string $passphrase a passphrase/password.
* @return string|false plaintext data or `false` on error.
*/
function decrypt($ciphertext, $passphrase) {
$data = base64_decode($ciphertext);
$ciphertext = substr($data, 16);
$key = hash('SHA256', $passphrase, true);
$iv = substr($data, 0, 16);
return openssl_decrypt($ciphertext, 'AES-256-CTR', $key, 1, $iv);
}
该aes256
模块在crypto
内部使用,它是一个内置模块,它支持 AES-256-ECB。所以你仍然可以将你的 PHP 代码移植到 JS,但我不建议这样做。AES-256-ECB 是一种非常弱的加密算法,它不提供身份验证。
PHP7 都crypto
支持经过身份验证的加密算法,因此您可以使用 GCM 为例。此外,最好使用 KDF,例如 PBKDF2(PHP 和 也支持 crypto
)来创建密钥。
使用 AES-256-GCM 的 PHP 加密,使用 SHA256 的 PBKDF2:
/**
* Encrypts data with the supplied passphrase, using AES-256-GCM and PBKDF2-SHA256.
*
* @param string $plaintext the plaintext data.
* @param string $passphrase a passphrase/password.
* @return string|false encrypted data: salt + nonce + ciphertext + tag or `false` on error.
*/
function encrypt(string $plaintext, string $passphrase) {
$salt = openssl_random_pseudo_bytes(16);
$nonce = openssl_random_pseudo_bytes(12);
$key = hash_pbkdf2("sha256", $passphrase, $salt, 40000, 32, true);
$ciphertext = openssl_encrypt($plaintext, 'aes-256-gcm', $key, 1, $nonce, $tag);
return base64_encode($salt.$nonce.$ciphertext.$tag);
}
/**
* Decrypts data with the supplied passphrase, using AES-256-GCM and PBKDF2-SHA256.
*
* @param string $ciphertext encrypted data.
* @param string $passphrase a passphrase/password.
* @return string|false plaintext data or `false` on error.
*/
function decrypt(string $ciphertext, string $passphrase) {
$input = base64_decode($ciphertext);
$salt = substr($input, 0, 16);
$nonce = substr($input, 16, 12);
$ciphertext = substr($input, 28, -16);
$tag = substr($input, -16);
$key = hash_pbkdf2("sha256", $passphrase, $salt, 40000, 32, true);
return openssl_decrypt($ciphertext, 'aes-256-gcm', $key, 1, $nonce, $tag);
}
使用 AES-256-GCM 的 JS 加密,使用 SHA256 的 PBKDF2:
const crypto = require('crypto');
/**
* Encrypts data with the supplied passphrase, using AES-256-GCM and PBKDF2-SHA256.
*
* @param {String} $plaintext the plaintext data.
* @param {String} $passphrase a passphrase/password.
* @return {String} encrypted data: salt + nonce + ciphertext + tag.
*/
function encrypt(plaintext, passphrase) {
var salt = crypto.randomBytes(16);
var nonce = crypto.randomBytes(12);
var key = crypto.pbkdf2Sync(passphrase, salt, 40000, 32, 'sha256');
var cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
var ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
var output = Buffer.concat([salt, nonce, ciphertext, cipher.getAuthTag()]);
return output.toString('base64');
}
/**
* Decrypts data with the supplied passphrase, using AES-256-GCM and PBKDF2-SHA256.
*
* @param {String} $ciphertext encrypted data.
* @param {String} $passphrase a passphrase/password.
* @return {String} plaintext data.
*/
function decrypt(ciphertext, passphrase) {
var input = new Buffer(ciphertext, 'base64');
var salt = input.slice(0, 16);
var nonce = input.slice(16, 28);
ciphertext = input.slice(28, -16);
var tag = input.slice(-16);
var key = crypto.pbkdf2Sync(passphrase, salt, 40000, 32, 'sha256');
var cipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
cipher.setAuthTag(tag);
var plaintext = Buffer.concat([cipher.update(ciphertext), cipher.final()]);
return plaintext.toString('utf-8');
}
这些函数产生兼容的结果,因此encrypt
在 PHP 中创建的密文可以decrypt
在 JS 中解密,反之亦然。当然,这只是一个基本示例,生产代码将具有更多功能、异常处理以及可能的密码和 KDF 设置。
推荐阅读
- html - 悬停伪类的CSS定位问题
- ruby-on-rails - 如何在字段 ActiveRecord 查询中识别随机空格空白
- swift - 当计时器发布者触发时如何调用 URLSession 发布者
- reactjs - Next.js 的服务器端组件
- c# - 在 ASP.net Core 中注入多个 ServiceBusClient
- python-3.x - selenium 4.0.0b4:“无法打开新标签 - 没有打开浏览器”
- python - 如何使用 python 和 pandas 计算数据框中的对
- spring-boot - 无法在 Intellij 中创建新模块(使用 Spring Boot)
- c# - 无法首先在数据库中创建用户角色
- bash - 如何从詹金斯获得完整的最后 3 天报告(不添加/修改插件)?