在 Forge.js 中解密 CryptoJS AES 数据(使用密码)

Decrypting CryptoJS AES data (with passphrase) in Forge.js

所以我有一段代码用密码加密字符串。它使用 CryptoJS AES 加密函数 (CryptoJS.AES.encrypt) 并且看起来像这样...

CryptoJS.AES.encrypt(data, password).toString();

展望未来,我不想使用 CryptoJS,因为它是官方 deprecated/not 维护的,我想使用 Forge.js。我试图通读 GitHub 上的 Forge.js 文档以找到解决方案,但未能找到任何使用密码而不是手动创建密钥和 IV 的内容。

我查看了 https://code.google.com/archive/p/crypto-js/ 上的 CryptoJS 存档,似乎如果 encrypt 函数传递一个字符串作为第二个参数(密钥),它被用作密码导出密钥和 IV。但它没有详细说明它是如何做到这一点的。

似乎 base64 解码结果给出了一个以 Salted__ 开头的字符串,然后是一个逗号,然后是二进制文本的加密 blob,我什至不确定我将如何传递 "salt" 到 Forge。

我将如何仅使用 Forge.js 来解密这个数据块?

CryptoJS 支持 OpenSSL 的 EVP_BytesToKey 函数,该函数通过一轮 MD5 从新生成的盐和密码派生密钥和 IV。 forge documentation page:

上有一个例子

Using forge in node.js to match openssl's "enc" command line tool (Note: OpenSSL "enc" uses a non-standard file format with a custom key derivation function and a fixed iteration count of 1, which some consider less secure than alternatives such as OpenPGP/GnuPG):

var forge = require('node-forge');
var fs = require('fs');

// openssl enc -des3 -in input.txt -out input.enc
function encrypt(password) {
  var input = fs.readFileSync('input.txt', {encoding: 'binary'});

  // 3DES key and IV sizes
  var keySize = 24;
  var ivSize = 8;

  // get derived bytes
  // Notes:
  // 1. If using an alternative hash (eg: "-md sha1") pass
  //   "forge.md.sha1.create()" as the final parameter.
  // 2. If using "-nosalt", set salt to null.
  var salt = forge.random.getBytesSync(8);
  // var md = forge.md.sha1.create(); // "-md sha1"
  var derivedBytes = forge.pbe.opensslDeriveBytes(
    password, salt, keySize + ivSize/*, md*/);
  var buffer = forge.util.createBuffer(derivedBytes);
  var key = buffer.getBytes(keySize);
  var iv = buffer.getBytes(ivSize);

  var cipher = forge.cipher.createCipher('3DES-CBC', key);
  cipher.start({iv: iv});
  cipher.update(forge.util.createBuffer(input, 'binary'));
  cipher.finish();

  var output = forge.util.createBuffer();

  // if using a salt, prepend this to the output:
  if(salt !== null) {
    output.putBytes('Salted__'); // (add to match openssl tool output)
    output.putBytes(salt);
  }
  output.putBuffer(cipher.output);

  fs.writeFileSync('input.enc', output.getBytes(), {encoding: 'binary'});
}

// openssl enc -d -des3 -in input.enc -out input.dec.txt
function decrypt(password) {
  var input = fs.readFileSync('input.enc', {encoding: 'binary'});

  // parse salt from input
  input = forge.util.createBuffer(input, 'binary');
  // skip "Salted__" (if known to be present)
  input.getBytes('Salted__'.length);
  // read 8-byte salt
  var salt = input.getBytes(8);

  // Note: if using "-nosalt", skip above parsing and use
  // var salt = null;

  // 3DES key and IV sizes
  var keySize = 24;
  var ivSize = 8;

  var derivedBytes = forge.pbe.opensslDeriveBytes(
    password, salt, keySize + ivSize);
  var buffer = forge.util.createBuffer(derivedBytes);
  var key = buffer.getBytes(keySize);
  var iv = buffer.getBytes(ivSize);

  var decipher = forge.cipher.createDecipher('3DES-CBC', key);
  decipher.start({iv: iv});
  decipher.update(input);
  var result = decipher.finish(); // check 'result' for true/false

  fs.writeFileSync(
    'input.dec.txt', decipher.output.getBytes(), {encoding: 'binary'});
}

此示例显示的是三重 DES,但它以与 AES 相同的方式工作。您只需将 ivSize 更改为 16.