是否可以使用 nodejs 加密在随机位置破译?

Posted

技术标签:

【中文标题】是否可以使用 nodejs 加密在随机位置破译?【英文标题】:Is it possible to decipher at random position with nodejs crypto? 【发布时间】:2018-04-21 08:53:42 【问题描述】:

我的理解是,CTR 模式下的 AES 分组密码理论上允许解密大文件的任何位置,而无需读取整个文件。

但是,我看不到如何使用 nodejs 加密模块执行此操作。我可以使用虚拟块为Decipher.update 方法提供数据,直到我到达我感兴趣的部分,此时我将从文件中读取的实际数据提供给我,但这将是一种可怕的黑客行为,效率低下且脆弱,因为我需要注意块大小。

有没有办法使用加密模块,如果没有,我可以使用什么模块?

【问题讨论】:

是的,如果您可以计算存储在 Buffer 中的数字以推进 IV,您可以这样做。此外,如果您使用 AES,则块大小固定为 16 个字节。 @ArtjomB。您是指用虚拟数据重复调用Decipher.update 的“解决方案”吗?我真的不认为这是一个好方法。假设我有一个 1TB 的文件,我只想从中读取几 MB,接近尾声。我不想计算价值 1TB 的密码块只是为了最终破译几 MB... 【参考方案1】:

我可以为 Decipher.update 方法提供虚拟块,直到我到达我感兴趣的部分

正如@Artjom 已经评论的那样,假设使用 CTR 模式,您不需要提供文件的开头或任何虚拟块。您可以直接输入您感兴趣的密文。(使用AES启动128位的块大小)

参见CTR mode of operation,您只需将 IV 计数器设置为密文的起始块,只提供要解密的加密文件的一部分(如果需要,您可能需要提供起始块的虚拟字节)

例子:

您需要使用 AES 从位置 1048577 解密文件,它是块 65536 (1048577/16) 加上 1 个字节。所以你将IV设置为nonce|65536,解密虚拟1字节(移动到16 * 65536 + 1的位置)然后你可以从你感兴趣的文件部分输入你的密文

【讨论】:

好的,所以我不明白@ArtjomB 是什么。说:-/我想我现在更好地理解了他和你的意思。我仍然不确定 IV 格式,您的意思是将随机 IV 与计数器连接起来吗?每个字节有多少字节,计数器的字节顺序是什么,先到的等等? @youen 取决于 IV 的构造方式。 IV 是 128 位字节数组。***将 CTR IV 描述为 nonce 与 counter (64bit +64bit) 连接。最常见的是我看到 IV 随机初始化(没有人说计数器必须从 0 开始)然后你只需将块计数添加到初始 IV 值的二进制表示中 非常感谢,就像一个魅力。我并不是要窃取您的答案,而是发布了一个带有工作代码示例的答案,因为我认为它可以帮助其他人解决同样的问题,因为它更精确。【参考方案2】:

我找到了不同的方法来解决这个问题:

方法一:点击率模式

此答案基于@ArtjomB。和@gusto2 cmets and answer,这真的给了我解决方案。但是,这是一个带有工作代码示例的新答案,它还显示了实现细节(例如,IV 必须作为大端数递增)。

这个想法很简单:要从n 块的偏移量开始解密,只需将IV 增加n。每个块为 16 个字节。

import crypto = require('crypto');
let key = crypto.randomBytes(16);
let iv = crypto.randomBytes(16);

let message = 'Hello world! This is test message, designed to be encrypted and then decrypted';
let messageBytes = Buffer.from(message, 'utf8');
console.log('       clear text: ' + message);

let cipher = crypto.createCipheriv('aes-128-ctr', key, iv);
let cipherText = cipher.update(messageBytes);
cipherText = Buffer.concat([cipherText, cipher.final()]);

// this is the interesting part: we just increment the IV, as if it was a big 128bits unsigned integer. The IV is now valid for decrypting block n°2, which corresponds to byte offset 32
incrementIV(iv, 2); // set counter to 2

let decipher = crypto.createDecipheriv('aes-128-ctr', key, iv);
let decrypted = decipher.update(cipherText.slice(32)); // we slice the cipherText to start at byte 32
decrypted = Buffer.concat([decrypted, decipher.final()]);
let decryptedMessage = decrypted.toString('utf8');
console.log('decrypted message: ' + decryptedMessage);

这个程序将打印:

明文:世界你好!这是测试消息,旨在加密然后解密 解密消息:e,设计为先加密再解密

正如预期的那样,解密后的消息移动了 32 个字节。

最后,这里是 incrementIV 的实现:

function incrementIV(iv: Buffer, increment: number) 
    if(iv.length !== 16) throw new Error('Only implemented for 16 bytes IV');

    const MAX_UINT32 = 0xFFFFFFFF;
    let incrementBig = ~~(increment / MAX_UINT32);
    let incrementLittle = (increment % MAX_UINT32) - incrementBig;

    // split the 128bits IV in 4 numbers, 32bits each
    let overflow = 0;
    for(let idx = 0; idx < 4; ++idx) 
        let num = iv.readUInt32BE(12 - idx*4);

        let inc = overflow;
        if(idx == 0) inc += incrementLittle;
        if(idx == 1) inc += incrementBig;

        num += inc;

        let numBig = ~~(num / MAX_UINT32);
        let numLittle = (num % MAX_UINT32) - numBig;
        overflow = numBig;

        iv.writeUInt32BE(numLittle, 12 - idx*4);
    

方法二:CBC模式

由于 CBC 使用之前的 密文 块作为 IV,并且所有密文块在解密阶段都是已知的,因此您无需做任何特别的事情,您可以随时解密的流。唯一的问题是您解密的第一个块将是垃圾,但下一个块会很好。所以你只需要在你真正想要解密的部分之前开始一个块。

【讨论】:

nice.. 恕我直言,incrementIV 对我来说太复杂了(我正在使用 Java 女巫为此提供了本机工具),但似乎你让它工作了,干得好 @gusto2 当然,可以简化它,和/或使其更具可读性。但是,javascript 中没有原生功能来处理大于 32 位的数字(实际上,所有数字都是双精度浮点数,只能编码高达 50 位或类似的整数)。有诸如“bigint”和“bignum”之类的库,但我不喜欢添加太多的依赖项,当我需要的所有东西都适合 10 行代码时......我已经在各种情况下测试了这个函数(包括溢出和循环回到零),我认为这很好。

以上是关于是否可以使用 nodejs 加密在随机位置破译?的主要内容,如果未能解决你的问题,请参考以下文章

《白帽子讲WEB安全》学习笔记之第11章 加密算法与随机数

取模(取余)运算小结规律——用于数字加密以及破译

密码破译常识学习

加密解密及算法详解

Nodejs 随机免费 tcp 端口

井通商学院第九讲 I 非对称加密(上)