在 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.
所以我有一段代码用密码加密字符串。它使用 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.