【问题标题】:AES Encryption PHP to NodeJS?AES 加密 PHP 到 NodeJS?
【发布时间】:2019-02-14 00:51:02
【问题描述】:

我正在将一个小项目从 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 版本有些问题,因为它无法给我输出, 而是引发错误。

提供的“加密”必须解密为非空字符串。

你能找出问题所在吗?

【问题讨论】:

    标签: javascript node.js encryption aes php-7


    【解决方案1】:

    Node.js aes256 模块不支持您的 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');
    }
    

    这些函数产生兼容的结果,因此在 PHP 中使用 encrypt 创建的密文可以在 JS 中使用 decrypt 解密,反之亦然。当然,这只是一个基本示例,生产代码将具有更多功能、异常处理以及可能的密码和 KDF 设置。

    【讨论】:

    • 你能说出这里的密码是什么
    • @TusharKale passphrase 只是一个字符串,一个密码。我添加了一些评论以使其更加清晰。
    • 反之亦然加密不起作用。前任。在节点 js 中加密,但密文不适用于 PHP 中的解密。在同一平台上工作。 @t.m.adam
    • @KishanPatel 您是否使用相同的密码和密文?因为我在创建的 repls 中没有遇到这个问题 - Js,replit.com/@tasos_py/MutedHotpinkClasses#index.js 和 PHP,replit.com/@tasos_py/PassionateCheapTag#main.php(请随意使用它们进行测试,顺便说一句)。
    • 谢谢@t.m.adam。这是 PHP 密码短语的拼写错误。再次感谢您的演示......:+1
    猜你喜欢
    • 2019-12-20
    • 2019-11-04
    • 2021-09-24
    • 2016-09-22
    • 1970-01-01
    • 1970-01-01
    • 2014-04-24
    • 2011-10-19
    • 1970-01-01
    相关资源
    最近更新 更多