如何在JavaScript(浏览器)中使用块AES-CTR块解密大文件(1 GB)?
我们正在尝试在浏览器中解密大文件(1GB)。使用AES -CTR,应该可以通过块解密块 - 块必须是正确的尺寸,您还必须提供Nonce +计数器。 有人有任何例子或想法如何在JavaScript中执行此操作吗?
到目前为止,我们尝试了什么:
var length = value.byteLength;
var chunkSize = 128;
var index = 0;
let chunks = [];
let aesCounter = byteArrayToLong(subtleIv);
do {
let newCount = aesCounter + index / 16;
var decrypted = await window.crypto.subtle.decrypt({name: "AES-CTR", counter: Buffer.from(longToByteArray(newCount)), length: chunkSize}, subtleKey, value.slice(index, index+chunkSize));
chunks.push(Buffer.from(decrypted));
index += chunkSize;
} while(index < length);
let newCount = aesCounter + index / 16;
decrypted = await window.crypto.subtle.decrypt({name: "AES-CTR", counter: Buffer.from(longToByteArray(newCount)), length: chunkSize}, subtleKey, value.slice(index, index+chunkSize));
chunks.push(Buffer.from(decrypted));
let decryptedAll = Buffer.concat(chunks);
function longToByteArray(/*long*/long) {
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for ( var index = 0; index < byteArray.length; index ++ ) {
var byte = long & 0xff;
byteArray [ index ] = byte;
long = (long - byte) / 256 ;
}
return byteArray;
}
function byteArrayToLong(/*byte[]*/byteArray) {
var value = 0;
for ( var i = byteArray.length - 1; i >= 0; i--) {
value = (value * 256) + byteArray[i];
}
return value;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实现中唯一的缺陷实际上是整数和字节数组之间的转换。首先,在JavaScript中,最大整数为0x1fffffffffffffff,请参见在这里但是WebCrypto API应用了大型Endian订单。
作为修复程序的第一步,您可以使用 bigint JavaScript的实现和描述了
bigint
和arraybuffer
。之间的转换
由于此实现可用于
arrayBuffer
和uint8array
,因此需要进行串联的实现,例如,来自在这里。这稍微改变了您的实现,如下所示(键,IV和密文被导入编码):
成功地解密了密文。顺便说一句,Ciphertext用 cyberchef 。
与WebCrypto API不同,Cryptojs支持渐进的加密,因此使用CryptoJs可以更轻松地实现相同的逻辑:
第一个变体的缺点是
bigint
类实际上不应在密码学的上下文中使用,因为操作不是恒定的,这会导致易受时间攻击的脆弱性。因此,在这里,您必须在生产上应用一个更安全的JavaScript BigInteger实施。由于这种原因,使用已建立的库(而不是定制实施),例如Cryptojs,通常更安全(尽管最终在这里也不能排除漏洞)。
The only flaw in your implementation is actually the conversion between integer and byte array. Firstly, in JavaScript the maximum integer is 0x1FFFFFFFFFFFFF, see here, secondly, even with smaller numbers the little endian order is used, but the WebCrypto API applies the big endian order.
As a first step to a fix you could use e.g. the
BigInt
implementation of JavaScript and the here described conversion betweenBigInt
andArrayBuffer
.Since this implementation works with
ArrayBuffer
andUint8Array
respectively, an implementation for concatenation is needed, e.g. from here.This changes your implementation slightly as follows (key, IV and ciphertext are imported hex encoded):
which successfully decrypts the ciphertext. Btw, the ciphertext was generated with CyberChef.
Unlike the WebCrypto API, CryptoJS supports progressive encryption, so the same logic can be implemented significantly easier with CryptoJS:
A drawback of the first variant is that the
BigInt
class should actually not be used in the context of cryptography, since the operations are not constant-time, which results in a vulnerability to timing attacks. So, here you would have to apply a cryptographically more secure JavaScript BigInteger implementation for production.For such reasons, the use of an established library (as opposed to a custom implementation), such as CryptoJS, is generally more secure (although ultimately vulnerabilities cannot be ruled out here either).
你在路上很好;在C块中切割密文,然后根据块计数更新计数器块,这正是您应该做的。
但是,请注意,计数器块为128位:
longTobyTearray
应称为createcounterblock(nonce,counter)
。 nonce应包含在初始/最左(8?)字节中。该计数器在最终/最右键 8字节中被编码为未签名的大恩迪安,总计16个字节。然后,您可以作为静脉注册来解密块。当前,您的计数器放置在最左边的字节中,对于大多数CTR实现,这将失败;我不认为JS使用128位整数。正式地,未定义计数器块的内容,但通常使用一个大的Endian 128位值。
You are well on your way; cutting the ciphertext in chunks of C-blocks and then updating the counter block according to the block count is exactly what you should be doing.
However, note that the counter block is 128 bits: the
longToByteArray
should be calledcreateCounterBlock(nonce, counter)
. The nonce should be contained in the initial/leftmost (8?) bytes. The counter is encoded as unsigned big endian in the final/rightmost 8 bytes, making 16 bytes total. This you can then provide as an IV to decrypt the chunk.Currently your counter is placed in the leftmost bytes, which will fail for most CTR implementations; I don't think that JS uses 128 bit integers. Officially the contents of the counter block is not defined, but generally a big endian 128 bit value is used.