使用 CTR 模式问题对 crypto-js 进行加密和对节点 crypto 进行解密
Encryption on crypto-js and decryption on node crypto using CTR mode issue
我正在尝试使用 crypto-js javascript 库加密数据,并尝试使用节点加密库在 nodejs 端解密相同的加密文本。我正在使用 AES 256 加密算法和 CTR 模式,没有填充。我能够正确加密,但 nodejs 加密模块上的描述未生成相同的纯文本。
如果我尝试使用相同的 crypto-js 和节点加密库进行加密或解密,它工作正常,但 crypto-js 上的加密和 crypto 上的描述没有按预期工作。我试图确认我是否在同一个库中加密和解密而不是它是否有效并且它工作得很好。有人可以检查我在这里犯了什么错误吗?
请在下面找到代码示例。
加密:
var key = CryptoJS.enc.Hex.parse('F29BA22B55F9B229CC9C250E11FD4384');
var iv = CryptoJS.enc.Hex.parse('C160C947CD9FC273');
function encrypt(plainText) {
return CryptoJS.AES.encrypt(
plainText,
key,
{
iv: iv,
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.CTR
}
);
}
使用 NodeJS crypo 模块解密:
var algorithm = 'aes-256-ctr';
var key = 'F29BA22B55F9B229CC9C250E11FD4384';
var iv = 'C160C947CD9FC273';
var outputEncoding = 'hex';
var inputEncoding = 'hex';
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update('8df5e11f521cf492437a95', inputEncoding, 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
正如我上面提到的,如果我使用相同的库进行加密和解密,我的 JavaScript crypo-js 和 NodeJS crypo 模块会话工作正常,但在其他情况下不起作用。请检查下面的工作代码。
JavaScript: http://jsfiddle.net/usr_r/2qwt8jsh/2/
与 NodeJS 代码 (Crypto
) 相比,JavaScript 代码 (CryptoJS
) 将键和 IV 解释为十六进制字符串。因此,在 JavaScript-Code 中使用 AES-128,在 NodeJS-Code 中使用 AES-256。要解决这个问题,两个代码必须使用相同的加密。
选项 1:将 JavaScript-code 更改为 AES-256:替换 JavaScript-code
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB162');
来自
var key = CryptoJS.enc.Utf8.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Utf8.parse('D959B836CD9FB162');
选项 2:将 NodeJS 代码更改为 AES-128:替换为 NodeJS 代码
var algorithm = 'aes-256-ctr';
var key = 'F18AB33A57F9B229CC9C250D00FC3273';
var iv = 'D959B836CD9FB162';
来自
var algorithm = 'aes-128-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');
对于两个更改中的每一个更改,两个链接的代码都会产生相同的结果。
如果应使用 AES-256 并且密钥和 IV 应指定为十六进制字符串,必须使用相应的大密钥和 IV,例如在 JavaScript 一侧:
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB16200000000000000');
在 NodeJS 端:
var algorithm = 'aes-256-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');
我认为您的 CryptoJS 代码没有使用 AES-256,因为密钥和 IV 太短,因此隐式使用 AES-128。如果你从 CryptoJS.AES
对象中得到 blockSize
,它对我说 4
。那就是说我不太了解 CryptoJS,这可能并不意味着“4 个词”。
为了绕过这种实施的不确定性,最好有一个 "gold standard" 来复制。 NIST 提供了大量测试向量,其中一些适用于您的 CTR mode AES-256。首先,我从该文档中提取了一组(十六进制编码的)测试向量:
const key = (
'603deb1015ca71be2b73aef0857d7781' +
'1f352c073b6108d72d9810a30914dff4'
)
const ctr = 'f0f1f2f3f4f5f6f7f8f9fafbfcfdff00'
const output = '5a6e699d536119065433863c8f657b94'
const cipher = 'f443e3ca4d62b59aca84e990cacaf5c5'
const plain = 'ae2d8a571e03ac9c9eb76fac45af8e51'
接下来我尝试从 Node's crypto
module:
恢复这些
const crypto = require('crypto')
function node_crypto(text) {
const dec = crypto.createDecipheriv(
'aes-256-ctr',
Buffer.from(key, 'hex'),
Buffer.from(ctr, 'hex')
);
const out = dec.update(Buffer.from(text, 'hex'))
return out.toString('hex')
}
现在我可以编写一个简单的测试工具来测试上述内容并将其与该功能一起使用:
const zero = '00'.repeat(16);
function test_crypto(fn) {
return {
'zero => output': fn(zero) == output,
'cipher => plain': fn(cipher) == plain,
'plain => cipher': fn(plain) == cipher,
}
}
console.log(test_crypto(node_crypto))
这给了我所有测试的 true
。
最后,CryptoJS 的等效代码是:
const CryptoJS = require("crypto-js");
function cryptojs(text) {
const out = CryptoJS.AES.encrypt(
CryptoJS.enc.Latin1.parse(Buffer.from(text, 'hex').toString('binary')),
CryptoJS.enc.Hex.parse(key),
{
iv: CryptoJS.enc.Hex.parse(ctr),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.NoPadding,
}
);
return out.ciphertext.toString();
}
console.log(test_crypto(cryptojs))
这对我也适用。
重要的是要注意 CryptoJS 只是默默地接受 arbitrarily sized keys,文档说:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
我正在尝试使用 crypto-js javascript 库加密数据,并尝试使用节点加密库在 nodejs 端解密相同的加密文本。我正在使用 AES 256 加密算法和 CTR 模式,没有填充。我能够正确加密,但 nodejs 加密模块上的描述未生成相同的纯文本。
如果我尝试使用相同的 crypto-js 和节点加密库进行加密或解密,它工作正常,但 crypto-js 上的加密和 crypto 上的描述没有按预期工作。我试图确认我是否在同一个库中加密和解密而不是它是否有效并且它工作得很好。有人可以检查我在这里犯了什么错误吗?
请在下面找到代码示例。
加密:
var key = CryptoJS.enc.Hex.parse('F29BA22B55F9B229CC9C250E11FD4384');
var iv = CryptoJS.enc.Hex.parse('C160C947CD9FC273');
function encrypt(plainText) {
return CryptoJS.AES.encrypt(
plainText,
key,
{
iv: iv,
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.CTR
}
);
}
使用 NodeJS crypo 模块解密:
var algorithm = 'aes-256-ctr';
var key = 'F29BA22B55F9B229CC9C250E11FD4384';
var iv = 'C160C947CD9FC273';
var outputEncoding = 'hex';
var inputEncoding = 'hex';
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update('8df5e11f521cf492437a95', inputEncoding, 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
正如我上面提到的,如果我使用相同的库进行加密和解密,我的 JavaScript crypo-js 和 NodeJS crypo 模块会话工作正常,但在其他情况下不起作用。请检查下面的工作代码。
JavaScript: http://jsfiddle.net/usr_r/2qwt8jsh/2/
与 NodeJS 代码 (Crypto
) 相比,JavaScript 代码 (CryptoJS
) 将键和 IV 解释为十六进制字符串。因此,在 JavaScript-Code 中使用 AES-128,在 NodeJS-Code 中使用 AES-256。要解决这个问题,两个代码必须使用相同的加密。
选项 1:将 JavaScript-code 更改为 AES-256:替换 JavaScript-code
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB162');
来自
var key = CryptoJS.enc.Utf8.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Utf8.parse('D959B836CD9FB162');
选项 2:将 NodeJS 代码更改为 AES-128:替换为 NodeJS 代码
var algorithm = 'aes-256-ctr';
var key = 'F18AB33A57F9B229CC9C250D00FC3273';
var iv = 'D959B836CD9FB162';
来自
var algorithm = 'aes-128-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');
对于两个更改中的每一个更改,两个链接的代码都会产生相同的结果。
如果应使用 AES-256 并且密钥和 IV 应指定为十六进制字符串,必须使用相应的大密钥和 IV,例如在 JavaScript 一侧:
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB16200000000000000');
在 NodeJS 端:
var algorithm = 'aes-256-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');
我认为您的 CryptoJS 代码没有使用 AES-256,因为密钥和 IV 太短,因此隐式使用 AES-128。如果你从 CryptoJS.AES
对象中得到 blockSize
,它对我说 4
。那就是说我不太了解 CryptoJS,这可能并不意味着“4 个词”。
为了绕过这种实施的不确定性,最好有一个 "gold standard" 来复制。 NIST 提供了大量测试向量,其中一些适用于您的 CTR mode AES-256。首先,我从该文档中提取了一组(十六进制编码的)测试向量:
const key = (
'603deb1015ca71be2b73aef0857d7781' +
'1f352c073b6108d72d9810a30914dff4'
)
const ctr = 'f0f1f2f3f4f5f6f7f8f9fafbfcfdff00'
const output = '5a6e699d536119065433863c8f657b94'
const cipher = 'f443e3ca4d62b59aca84e990cacaf5c5'
const plain = 'ae2d8a571e03ac9c9eb76fac45af8e51'
接下来我尝试从 Node's crypto
module:
const crypto = require('crypto')
function node_crypto(text) {
const dec = crypto.createDecipheriv(
'aes-256-ctr',
Buffer.from(key, 'hex'),
Buffer.from(ctr, 'hex')
);
const out = dec.update(Buffer.from(text, 'hex'))
return out.toString('hex')
}
现在我可以编写一个简单的测试工具来测试上述内容并将其与该功能一起使用:
const zero = '00'.repeat(16);
function test_crypto(fn) {
return {
'zero => output': fn(zero) == output,
'cipher => plain': fn(cipher) == plain,
'plain => cipher': fn(plain) == cipher,
}
}
console.log(test_crypto(node_crypto))
这给了我所有测试的 true
。
最后,CryptoJS 的等效代码是:
const CryptoJS = require("crypto-js");
function cryptojs(text) {
const out = CryptoJS.AES.encrypt(
CryptoJS.enc.Latin1.parse(Buffer.from(text, 'hex').toString('binary')),
CryptoJS.enc.Hex.parse(key),
{
iv: CryptoJS.enc.Hex.parse(ctr),
mode: CryptoJS.mode.CTR,
padding: CryptoJS.pad.NoPadding,
}
);
return out.ciphertext.toString();
}
console.log(test_crypto(cryptojs))
这对我也适用。
重要的是要注意 CryptoJS 只是默默地接受 arbitrarily sized keys,文档说:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.