[英]Seeking in AES-CTR-encrypted input
As AES in CTR mode is great for random access, lets say I have a data source created with a CipherOutputStream
in AES-CTR mode.由于 CTR 模式下的 AES 非常适合随机访问,假设我有一个使用 AES-CTR 模式下的CipherOutputStream
创建的数据源。 The library underneath—which is not mine—uses a RandomAccessFile
that allows to seek to a specific byte offset in the file.下面的库(不是我的)使用RandomAccessFile
允许查找文件中的特定字节偏移量。
My initial thought would be to use a CipherInputStream
with a Cipher
initialized with the right parameters, but the API for that doesn't do seeking and states to not support mark
and reset
.我最初的想法是将CipherInputStream
与用正确参数初始化的Cipher
一起使用,但该 API不进行搜索并声明不支持mark
和reset
。
Is there a part of the API that I've missed that can do this for me, should I look into the configuration of CTR's IV/block counter and recreate that with a custom input stream (which sounds like shotgun aimed at self
to me) or take some other approach I've missed?是否有我错过的 API 的一部分可以为我做到这一点,我是否应该查看 CTR 的 IV/块计数器的配置并使用自定义输入流重新创建它(这听起来像是针对我self
霰弹枪)或者采取一些我错过的其他方法?
I ended up looking up exactly how the IV is updated in CTR mode.我最终确切地查找了 IV 在 CTR 模式下的更新方式。 This turns out to do a simple +1 for each AES block it processes.事实证明,这对其处理的每个 AES 块都进行了简单的 +1。 I implemented reading along the following lines.我按照以下几行实现了阅读。
Given a class that implements a read
-like method that would read the next byte in a byte sequence that is encrypted and needs to support seeking in that sequence and the following variables:给定一个实现类似read
方法的类,该方法将读取加密的字节序列中的下一个字节,并需要支持在该序列中查找和以下变量:
BLOCK_SIZE
: fixed at 16 (128 bits, AES block size); BLOCK_SIZE
:固定为 16(128 位,AES 块大小);cipher
: an instance of javax.crypto.Cipher
, initialized to deal with AES; cipher
: javax.crypto.Cipher
一个实例,初始化以处理 AES;delegate
: a java.io.InputStream
that wraps an encrypted resource that allows random access; delegate
:一个java.io.InputStream
,它包装了一个允许随机访问的加密资源;input
: a javax.crypto.CipherInputStream
we'll be serving reads from (the stream will take care of the decryption). input
:一个javax.crypto.CipherInputStream
我们将提供读取服务(流将负责解密)。 The seek
method is implemented as such: seek
方法是这样实现的:
void seek(long pos) {
// calculate the block number that contains the byte we need to seek to
long block = pos / BLOCK_SIZE;
// allocate a 16-byte buffer
ByteBuffer buffer = ByteBuffer.allocate(BLOCK_SIZE);
// fill the first 12 bytes with the original IV (the iv minus the actual counter value)
buffer.put(cipher.getIV(), 0, BLOCK_SIZE - 4);
// set the counter of the IV to the calculated block index + 1 (counter starts at 1)
buffer.putInt(block + 1);
IvParameterSpec iv = new IvParameterSpec(buffer.array());
// re-init the Cipher instance with the new IV
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// seek the delegate wrapper (like seek() in a RandomAccessFile and
// recreate the delegate stream to read from the new location)
// recreate the input stream we're serving reads from
input = new CipherInputStream(delegate, cipher);
// next read will be at the block boundary, need to skip some bytes to arrive at pos
int toSkip = (int) (pos % BLOCK_SIZE);
byte[] garbage = new byte[toSkip];
// read bytes into a garbage array for as long as we need (should be max BLOCK_SIZE
// bytes
int skipped = input.read(garbage, 0, toSkip);
while (skipped < toSkip) {
skipped += input.read(garbage, 0, toSkip - skipped);
}
// at this point, the CipherStream is positioned at pos, next read will serve the
// plain byte at pos
}
Note that seeking the delegate resource is omitted here, as this depends on what is underneath the delegate InputStream
.请注意,此处省略了寻找委托资源,因为这取决于委托InputStream
。 Also note that the initial IV is required to be started at counter 1 (the last 4 bytes).另请注意,初始 IV 需要从计数器 1(最后 4 个字节)开始。
Unittests show that this approach works (performance benchmarks will be done at some point in the future :)).单元测试表明这种方法有效(性能基准测试将在未来的某个时候完成:))。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.