关于javascript:使用CTR模式发行的crypto-js加密和节点加密的解密

Encryption on crypto-js and decryption on node crypto using CTR mode issue

我正在尝试使用crypto-js javascript库对数据进行加密,并尝试使用node crypto库在nodejs端解密相同的加密文本。我正在CTR模式下使用无填充的AES 256加密算法。我能够正确加密,但是对nodejs crypto模块的描述未生成相同的纯文本。

如果我尝试使用相同的crypto-js和节点crypto库进行加密或解密,则可以正常工作,但是对crypto-js进行加密和对crypto的描述无法正常工作。我试图确认是否在同一个库中对它进行加密和解密,而不是正常工作,并且效果很好。有人可以检查我在这里犯什么错误吗?

请找到以下代码示例。

加密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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模块解密:

1
2
3
4
5
6
7
8
9
10
11
12
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:https://repl.it/repls/AchingRegalPhp


我认为您的CryptoJS代码未使用AES-256,因为密钥和IV太短,因此隐式使用了AES-128。如果从CryptoJS.AES对象获得blockSize,它对我说4。那说我不太了解CryptoJS,这可能并不意味着" 4个单词"。

为了绕过这种实现的不确定性,最好复制一个"黄金标准"。 NIST提供了许多测试矢量,其中一些适用于CTR模式AES-256。首先,我从该文档中提取了一组(十六进制编码的)测试向量:

1
2
3
4
5
6
7
8
9
const key = (
  '603deb1015ca71be2b73aef0857d7781' +
  '1f352c073b6108d72d9810a30914dff4'
)
const ctr = 'f0f1f2f3f4f5f6f7f8f9fafbfcfdff00'

const output = '5a6e699d536119065433863c8f657b94'
const cipher = 'f443e3ca4d62b59aca84e990cacaf5c5'
const plain = 'ae2d8a571e03ac9c9eb76fac45af8e51'

接下来,我尝试从Node的Crypto模块中恢复它们:

1
2
3
4
5
6
7
8
9
10
11
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')
}

现在,我可以编写一个简单的测试工具来测试上述内容,并将其与该功能配合使用:

1
2
3
4
5
6
7
8
9
10
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的等效代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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只是默默地接受任意大小的密钥,而文档说:

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.


与NodeJS代码(Crypto)相比,JavaScript代码(CryptoJS)将键和IV解释为十六进制字符串。因此,在JavaScript代码AES-128和NodeJS代码AES-256中使用。要解决此问题,两个代码必须使用相同的加密。

选项1:将JavaScript代码更改为AES-256:替换为JavaScript代码

1
2
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB162');

通过

1
2
var key = CryptoJS.enc.Utf8.parse('F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Utf8.parse('D959B836CD9FB162');

选项2:将NodeJS代码更改为AES-128:替换为NodeJS代码

1
2
3
var algorithm = 'aes-256-ctr';    
var key = 'F18AB33A57F9B229CC9C250D00FC3273';
var iv = 'D959B836CD9FB162';

通过

1
2
3
var algorithm = 'aes-128-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');

使用两个更改中的每个更改,两个链接的代码将产生相同的结果。

如果应使用AES-256并且密钥和IV应指定为十六进制字符串,则必须使用相应的较大密钥和IV,例如在JavaScript方面:

1
2
var key = CryptoJS.enc.Hex.parse('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273');
var iv = CryptoJS.enc.Hex.parse('D959B836CD9FB16200000000000000');

在NodeJS端:

1
2
3
var algorithm = 'aes-256-ctr';
var key = Buffer.from('F18AB33A57F9B229CC9C250D00FC3273F18AB33A57F9B229CC9C250D00FC3273', 'hex');
var iv = Buffer.from('D959B836CD9FB1620000000000000000', 'hex');