Node.js AES-256-CBC 加密问题

Node.js AES-256-CBC encryption issue

我正在尝试根据 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;
    }

PHP和NodeJS代码生成的key长度为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() returns 默认使用Base64编码的密文。在 NodeJS 代码中,结果以十六进制编码返回。在这里,您必须在 update()final() 调用中将输出编码从 'hex' 更改为 'base64'

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

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

  • mt_rand() is not a cryptographically secure pseudorandom number generator (CSPRNG). PHP provides cryptographically secure methods for deriving a random IV / salt, e.g. random_bytes() or random_int().
  • 密钥是使用 SHA-256 推断的。使用可靠的密钥推导函数更安全,例如 PBKDF2. A 4 bytes salt 一般太小。
  • SHA1 在大多数情况下被认为是不安全的,不能再使用。

有人对以下问题有疑问吗?

crypto.createHash('sha256').update(password + 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 生成的字节值的方法。