crypto-js 输出与节点加密不同

crypto-js output is different from node crypto

我们正在尝试匹配 crypto 和 crypto-js 输出。 我们的要求是在浏览器中加密字符串,但加密不支持浏览器端加密。因此,我们正在尝试使用 crypto-js 匹配输出。每次 crypto-js 产生不同的输出。

const crypto = require('crypto');
const CryptoJS = require('crypto-js');

const payload = {
    name: 'John Doe'
}

// Node Package
function encryptNode(text) {
    const cipher = crypto.createCipher('aes-256-cbc', 'devOps');
    return cipher.update(Buffer.from(JSON.stringify(text)), 'utf8', 'hex') + cipher.final('hex');
}
console.log(encryptNode(payload));


// Browser Package
function encryptBrowser(text) {
    const ciphertext = CryptoJS.AES.encrypt(JSON.stringify(text), 'devOps', { mode: CryptoJS.mode.CBC });
    return ciphertext.toString(CryptoJS.format.Hex);
}
console.log(encryptBrowser(payload));

输出:

加密(预期):dfe03c7e825e9943aa6ec61deb4a8a73fdba0016a13c59c628ce025f39d44c7c

crypto-js: 4e5453abe7bd53d67d88aa4f040356c649fe0101366d05ce4c7d625cfd052cdc

crypto.createCipher and CryptoJS.AES.encrypt utilize the functionality of the OpenSSL-function EVP_BytesToKey,需要密码、可选 8 字节盐、摘要和迭代计数,并从这些数据生成密钥和 IV .这两个函数都使用 MD5 摘要和迭代计数 1 作为固定参数。

crypto.createCipher 不使用盐,因此每次都会生成 same 密钥和 IV,因此生成相同的密文(假设相同的明文)。

相比之下,CryptoJS.AES.encrypt每次都会生成一个随机盐(here and here),这样每次都会生成一个不同的密钥和IV,并且因此是不同的密文(即使是相同的明文)。对于解密(除了密码),还需要加盐,这不是秘密,可以与密文一起传递(例如,在 CipherParams 对象中传递给 CryptoJS.AES.decrypt)。

发布的代码片段因此表现得符合预期:用crypto.createCipher创建的密文不会改变,用CryptoJS.AES.encrypt创建的密文每次都会改变.

EVP_BytesToKeyweak,出于安全原因不应使用(至少在使用 MD5 和迭代计数为 1 时)。这同样适用于 crypto.createCipher(由于缺少 salt 而更是如此),无论如何都已弃用,以及 CryptoJS.AES.encrypt 的重载变体,它使用 EVP_BytesToKey。这两个库都提供了额外的或重载的方法,可以将密钥和 IV 直接 传递给它们(如果需要,可以使用安全过程预先从密码短语派生)。