Node.JS AES解密截断初始结果



我正试图使用内置的加密库在node.js中复制一些python加密代码。为了测试,我使用现有的python脚本加密数据,然后尝试使用node.js.解密

除了一个问题外,我所有的东西都在工作,进行解密会导致截断的初始解密结果,除非我获取额外的数据,这会导致截断最终结果。

我对安全方面的事情很陌生,所以如果我的方言不正确,请提前道歉。

Python加密逻辑:

encryptor = AES.new(key, AES.MODE_CBC, IV)
<# Header logic, like including digest, salt, and IV #>
for rec in vect:
chunk = rec.pack()  # Just adds disparate pieces of data into a contiguous bytearray of length 176
encChunk = encryptor.encrypt(chunk)
outfile.write(encChunk)

节点解密逻辑:

let offset = 0;
let derivedKey = crypto.pbkdf2Sync(secret, salt, iterations, 32, 'sha256');
let decryptor = crypto.createDecipheriv('aes-256-cbc', derivedKey, iv);
let chunk = data.slice(offset, (offset + RECORD_LEN))
while(chunk.length > 0) {
let clearChunk = decryptor.update(chunk);
// unpack clearChunk and do something with that data
offset += RECORD_LEN;
chunk = data.slice(offset, offset + RECORD_LEN);
}

我希望我的初始结果能打印出这样的十六进制:

54722e34d8b2bf158db6b533e315764f87a07bbfbcf1bd6df0529e56b6a6ae0f123412341234123400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

当它接近时,预计它会切掉最后16个字节(在上面的例子中,最后32个"0"将丢失(。这将所有随后的解密移位这16个字节;0的";被添加到下一个解密块的前面。

如果我在初始块大小上添加16个字节(这意味着实际获取更多数据,而不仅仅是偏移(,那么这就解决了前端的所有问题,但会导致最终块丢失最后16个字节的数据。

有一件事在我看来很奇怪:初始块的长度为176,但解密后的结果的长度为160。所有其它块在解密之前和之后具有长度176。我假设我初始化解密器的方式有问题,这导致它一开始需要额外的16字节数据,但我一辈子都搞不清楚是什么。

我必须接近,因为解密的数据是正确的,减去神秘的变化,即使在读取大量数据时也是如此。只需要弄清楚最后一步。

基于更新代码的短版本:如果您绝对确定每个块都是176字节(即16的倍数(,那么您可以将cipher.setAutoPadding(false)添加到Node代码中。如果这不是真的,或者想了解更多原因,请继续阅读。


在解密结束时,您需要调用decryptor.final来获得最终块。

如果你把所有的数据放在一起,你可以在一个调用中解密:

let clearChunk = decryptor.update(chunk) + decryptor.final()

update()的存在使得您可以将数据以块的形式传递给解密器。例如,如果您有一个非常大的文件,您可能不希望在内存中同时有加密数据的完整副本和解密数据的完整拷贝。因此,您可以从文件中分块读取加密数据,将其传递给update(),然后分块写出解密的数据。

使用CBC模式的输入数据的长度必须是16字节的倍数。为了确保这一点,我们通常使用PKCS7填充。这将把你的输入数据填充到16的倍数。如果它已经是16的倍数,它将添加一个16字节的额外块。paddingvalue是填充值的数目。因此,如果您的块是12字节长,它将填充04040404。如果是16的倍数,则填充为0x10的16字节。这个填充系统允许解密器验证它是否删除了正确的填充量。这可能是导致您的176/160问题的原因。

这个填充问题就是final()调用的原因。系统需要知道哪个块是最后一个块,这样才能删除填充。因此,对update()的第一个调用返回的块总是比您传入的块少一个,因为它一直保持着它,直到知道它是否是最后一个块。


查看您的Python代码,我认为它根本没有填充(我熟悉的大多数Python库都不会自动填充(。只要输入一定是16的倍数,那没关系。但Node的默认值是期望填充。如果您知道您的大小总是16的倍数,那么您可以使用cipher.setAutoPadding(false)更改节点侧。如果您不确定输入大小总是是16的倍数,那么您需要在Python端为最后一个块添加一个pad()调用。

最新更新