如何在nodejs中通过AES-256-CBC加密以与openssl保持一致

问题描述 投票:0回答:1

我曾经通过 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一致?

node.js encryption openssl cryptography
1个回答
0
投票

需要进行以下更改,以便 crypto 代码生成与 OpenSSL 语句相同的密文:

  • OpenSSL 语句对 -pbkdf2 使用
    default
    迭代计数 10,000(可以使用
    -iter
    更改该值);在 crypto 代码中,使用的迭代计数为 1。因此必须将该值更改为 10,000:
    const keyIv = crypto.pbkdf2Sync(pass, salt, 10000, 48, 'sha1'); 
    
  • 在 OpenSSL 语句中,末尾会自动添加换行符 (
    \n
    )。为了在 crypto 代码中获得相同的结果,必须将
    \n
    附加到明文中:
    let encrypted = cipher.update(plaintext + '\n', 'utf8');
    
    如果不想附加换行符,可以使用
    echo -n
    来避免(那么当然不需要修改 crypto 代码)。
  • 在 OpenSSL 语句中,不应用前缀
    Salted__|<salt>
    (当指定
    -S
    时,显然更现代的 OpenSSL 版本就是这种情况)。因此,在 crypto 代码中必须省略串联:
    const result = encrypted; // Buffer.concat([salted, encrypted]);
    

通过这些更改,crypto代码返回与 OpenSSL 语句相同的密文。

© www.soinside.com 2019 - 2024. All rights reserved.