首页 > 解决方案 > 如何使用具有可变消息大小的 CryptoJS CTR 模式(随机数计数器和偏移量)

问题描述

当使用 cryptoJS 在 AES(CTR 模式)中加密和解密消息时,AES 算法依赖于 nonce(Number Once)值来混淆您的流块并防止重放攻击。在 cryptoJS 中,设置你的随机数:

  const cli_verify = CryptoJS.AES.encrypt(CryptoJS.enc.Latin1.parse(originalString), key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
  });

同样,您可以在解密消息时使用相同的方法:

  const decryptedString = CryptoJS.AES.decrypt(encryptedStringB64, key, {
  mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
  }).toString(CryptoJS.enc.Latin1); 

但是:如果你这样做,你需要自己跟踪随机数。如果您开始处理可变大小的消息(即不是 16 字节的倍数),这 a) 很痛苦,b) 真的很困难。

我已经拆开了另一边正在与之交谈的 AES 库,并弄清楚了 nonce 计数器和偏移量是如何工作的。基本上,如果您开始发送非常小的消息,例如 4 个字节,则 4 个字节将使用流的前 4 个字节进行解密,并且偏移量将增加到 4 以跟踪这一点。如果随后加密了 6 个字节,则使用流的下 6 个字节(不增加 nonce),并将偏移量更新为 10... 如果下一次传输是 143 个字节,您可以看到这开始变得复杂自己实施。使用当前流的最后 6 个字节,增加随机数等等。

我的问题是:如何让 CryptoJS 在会话期间为您处理这个问题。我尝试在第一次调用中设置 IV、填充和模式,然后在下一次将其留空,但它不起作用。它是一个很棒的图书馆,我相信它就在那里。只是不清楚如何使用它。

标签: javascriptcryptographyaescryptojsnonce

解决方案


您可以改用渐进式密码 API,这确实会正确更新随机数/计数器。

var key = CryptoJS.enc.Hex.parse("000102030405060708090a0b0c0d0e0f");
var iv = CryptoJS.enc.Hex.parse("101112131415161718191a1b1c1d1e1f");
    
// encrypt
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
});
var ciphertextPart1 = aesEncryptor.process("Message Part 1");
var ciphertextPart2 = aesEncryptor.process("Message Part 2");
var ciphertextPart3 = aesEncryptor.process("Message Part 3");
var ciphertextPart4 = aesEncryptor.finalize();
    ​
// decrypt
var aesDecryptor = CryptoJS.algo.AES.createDecryptor(key, {
    mode: CryptoJS.mode.CTR,
    iv: iv,
    padding: CryptoJS.pad.NoPadding
});    ​

var plaintextPart1 = aesDecryptor.process(ciphertextPart1);
var plaintextPart2 = aesDecryptor.process(ciphertextPart2);
var plaintextPart3 = aesDecryptor.process(ciphertextPart3);
var plaintextPart4 = aesDecryptor.process(ciphertextPart4);
var plaintextPart5 = aesDecryptor.finalize();

根据https://cryptojs.gitbook.io/docs/上面的代码片段最初来自哪里。

如果您处理与块大小不匹配的输入(如您的情况),您将不会获得所有输出,直到您调用 finalize。

您可能会发现您需要调用 finalize 从与块大小不匹配但仍希望继续使用加密器/解密器的输入中获取输出。这个用例是需要在客户端和服务器之间同步随机数的地方。如果在输入和块大小不匹配时在 finalize 之后调用 process,则 api 不起作用。克隆 api 在块大小和输入不匹配的情况下也无法正常工作(在这种情况下,这实际上是您想要做的)。

但是,如果您跟踪会话期间发送和接收的所有输入,您可以在调用 finalize 后重新构建会话。这可以通过在调用 finalize 以获得最终输出之前调用 process 将先前的输入重播到新的加密器/解密器中来完成。在这种情况下,在调用 finalize 之后,您需要从输出中获取与输入中相同的字节数,因为加密器/解密器可能包含一些先前的输出,就像输入块大小不匹配的情况一样。


推荐阅读