【问题标题】:Node.js AES-256-CBC encryption issueNode.js AES-256-CBC 加密问题
【发布时间】:2021-04-22 18:01:46
【问题描述】:

我正在尝试根据 PHP 中给出的一个示例代码创建一个加密字符串,我的主要问题是 Node.js crypto 模块不接受长度超过 32 个字节的密钥,但 PHP openssl_encrypt确实如此,看来这就是我收到Invalid key size error 的原因。

这是我的js代码:

    let iv = sha1(await HelpersService.makeRandomNumber(null, null, 16));
    iv = iv.substr(0, 16);
    const text = bundledData;
    const password = sha1(this.credentials.appSecret);
    let salt = sha1(await HelpersService.makeRandomNumber(null, null, 4));
    salt = salt.substr(0, 4);
    const key = crypto.createHash('sha256').update(password + salt).digest('hex');
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    let encoded = cipher.update(text, 'utf8', 'hex');
    encoded += cipher.final('hex');

这是 PHP 示例:

   function generateCashOutAPIHashKey($app_secret ){
      //remove plus(+) sign from gsm number.
      $data = 'text';
      $iv = substr(sha1(mt_rand()), 0, 16);
      $password = sha1($app_secret);
      $salt = substr(sha1(mt_rand()), 0, 4);
      $saltWithPassword = hash('sha256', $password . $salt);
      $encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv    );
      return $encrypted;
    }

【问题讨论】:

  • 可能无关紧要的小点 当像openssl_encrypt("$data", 这样作为参数传递时,您不需要将变量放在引号中,一个简单的openssl_encrypt($data, 就足够了
  • PHP 根据指定的算法隐式截断密钥长度,即 AES-256 为 32 字节。 NodeJS 代码的 crypto 模块不会自动执行此操作,因此必须使用substr(0, 32) 显式截断密钥。此外,PHP 代码返回 Base64 编码的密文,即在 NodeJS 代码中,输出编码必须在 update()final() 调用中从 'hex' 更改为 'base64'
  • @Topaco 非常感谢!现在好了。可以发一下吗,我会把它标记为答案
  • 不客气。我将其发布为答案。

标签: php node.js openssl cryptography


【解决方案1】:

有人对以下有问题吗?

crypto.createHash('sha256').update(密码+ salt).digest('hex').substr(0, 32);

我理解需要由 sha256 生成的 32 字节值的意图,但是如果您进行十六进制编码,然后获取字符串的前 32 个字符,您将显着降低此值。您现在有一个 32 个字符长的字符串,每个字符只有 16 个可能的值 (0-9a-f)。你真的想要一个 32 字节的值,每个字节有 256 个可能的值。您已将加密密钥的密钥安全性从预期的 256^32 (1.1E77) 更改为 16^32 (3.4E38)。

crypto.createHash('sha256').update('').digest('hex');
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
crypto.createHash('sha256').update('').digest('hex').substr(0,32);
'e3b0c44298fc1c149afbf4c8996fb924'

找到一种方法来使用 SHA256 生成的字节值。

【讨论】:

  • @Gray 非常感谢,实际上我明白了你的意思,但我只是第三方服务的消费者,他们打算创建这样的密钥,我只是在模拟代码但是如果我以后想进行同样的加密,我肯定会考虑你的观点。
【解决方案2】:

在 PHP 和 NodeJS 代码中生成的密钥长度为 64 字节。使用的加密 AES-256 需要一个 32 字节的密钥。在 PHP 代码中,openssl_encrypt() 隐式 将 64 字节的密钥缩短为 32 字节。在 NodeJS 代码中,这必须显式地完成

const key = crypto.createHash('sha256').update(password + salt).digest('hex').substr(0, 32);

另外,openssl_encrypt() 返回默认编码的密文 Base64。在 NodeJS 代码中,结果以十六进制编码返回。在这里,您必须在update()final() 调用中将输出编码从'hex' 更改为'base64'

let encoded = cipher.update(text, 'utf8', 'base64');  
encoded += cipher.final('base64');                    

请注意,PHP 参考代码存在多个漏洞:

  • mt_rand() 不是加密安全的伪随机数生成器 (CSPRNG)。 PHP 提供了用于派生随机 IV / salt 的加密安全方法,例如random_bytes()random_int()
  • 使用 SHA-256 推断密钥。使用可靠的密钥派生函数如PBKDF2 更安全。 4 字节的salt 通常太小了。
  • SHA1 在大多数情况下被认为是不安全的,不得再使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 2020-01-14
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    相关资源
    最近更新 更多