我曾经通过 execSync 调用 openssl 来加密 Nodejs 中的文本,但这是一个不好的做法,因为它可能会将密码泄漏到系统日志中。现在我想使用
crypto
库,但得到的结果不一致。
这是要重现的最小代码:
const crypto = require('crypto');
const { execSync } = require('child_process');
const encrypt_text = async (plaintext, pass, salt = null) => {
return new Promise((resolve, reject) => {
try {
let command = `echo "${plaintext}" | openssl enc -aes-256-cbc -a -md sha1 -pbkdf2 -pass pass:${pass}`;
if (salt) {
const saltHex = salt.toString('hex');
command = `echo "${plaintext}" | openssl enc -aes-256-cbc -a -md sha1 -pbkdf2 -pass pass:${pass} -S ${saltHex}`;
}
const encrypted_text = execSync(command).toString().trim();
resolve(encrypted_text);
} catch (error) {
console.error('Error encrypting text.');
reject(error);
}
});
}
const encrypt_text_new = async (plaintext, pass, salt = null) => {
return new Promise((resolve, reject) => {
try {
if (!salt) {
salt = crypto.randomBytes(8); // 8 bytes salt
}
const salted = Buffer.concat([Buffer.from('Salted__'), salt]); // OpenSSL salt format
// OpenSSL key derivation
const keyIv = crypto.pbkdf2Sync(pass, salt, 1, 48, 'sha1'); // 48 bytes for key and IV
const key = keyIv.slice(0, 32); // first 32 bytes for key
const iv = keyIv.slice(32); // remaining 16 bytes for IV
// Create cipher
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(plaintext, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
// Combine all parts
const result = Buffer.concat([salted, encrypted]);
const encrypted_text = result.toString('base64');
resolve(encrypted_text);
} catch (error) {
console.error('Error encrypting text.');
reject(error);
}
});
}
const check_encryption = async () => {
// Sample data for testing
const plaintext = 'This is a test.';
const password = 'mysecretpassword';
// Generate a random salt for consistency in both functions
const salt = Buffer.from("12345678", 'utf8');
(async () => {
try {
const encrypted1 = await encrypt_text(plaintext, password, salt);
const encrypted2 = await encrypt_text_new(plaintext, password, salt);
console.log('Encrypted with OpenSSL:', encrypted1);
console.log('Encrypted with crypto:', encrypted2);
if (encrypted1 === encrypted2) {
console.log('The encrypted results are the same.');
} else {
console.log('The encrypted results are different.');
}
} catch (error) {
console.error('Error during encryption comparison:', error);
}
})();
}
check_encryption();
将输出:
Encrypted with OpenSSL: yY0R+o0ikHugU/cFa8O6LnLZA4WWAojrY0VhqA+vY3I=
Encrypted with crypto: U2FsdGVkX18xMjM0NTY3OLl80vSUI4464+tcG/S4C9M=
The encrypted results are different.
如何让nodejs加密与openssl一致?
需要进行以下更改,以便 crypto 代码生成与 OpenSSL 语句相同的密文:
-pbkdf2
使用 default迭代计数 10,000(可以使用
-iter
更改该值);在 crypto 代码中,使用的迭代计数为 1。因此必须将该值更改为 10,000:
const keyIv = crypto.pbkdf2Sync(pass, salt, 10000, 48, 'sha1');
\n
)。为了在 crypto 代码中获得相同的结果,必须将 \n
附加到明文中:
let encrypted = cipher.update(plaintext + '\n', 'utf8');
如果不想附加换行符,可以使用 echo -n
来避免(那么当然不需要修改 crypto 代码)。Salted__|<salt>
(当指定 -S
时,显然更现代的 OpenSSL 版本就是这种情况)。因此,在 crypto 代码中必须省略串联:
const result = encrypted; // Buffer.concat([salted, encrypted]);
通过这些更改,crypto代码返回与 OpenSSL 语句相同的密文。