Does anyone know whether it might be possible to use the javax.crypto.Cipher class to progressively fill a sequential chain of fixed size ByteBuffers (pooled buffers) with an arbitrary amount of encrypted data? Without allocating & filling any other intermediary buffer arrays, that is. The encryption for my particular case is AES in CBC mode.
I was hoping for something akin to CharsetEncoder’s encode(CharBuffer input, ByteBuffer output, boolean endOfInput) method, which allows a nice amount of control over writing the output including the ability to write up to a given limit and switch between output buffers as required.
At face value it would appear that this could be achieved using Cipher’s update(ByteBuffer input, ByteBuffer output) method where the limit of the input has been set prior to the call, ie. to avoid overrunning the capacity of the output buffer. If I’m understanding the API docs correctly though, a Cipher implementation may buffer any amount of encrypted data between calls to this method, so conceivably a single call to update() or doFinal() may require an output ByteBuffer with a larger capacity than that available from a fixed size buffer from a pool. In which case, according to the docs for both methods:
If output.remaining() bytes are insufficient to hold the result, a
ShortBufferException is thrown. In this case, repeat this call with a
larger output buffer. Use getOutputSize to determine how big the
output buffer should be.
Does anyone know if there might be a way around this? Perhaps some way of flushing the encrypted data between update() calls to prevent excessive buffer accumulation…? I haven’t yet gone down the path of trying CipherOutputStream. Again, the key goal is to directly populate a series of fixed size ByteBuffers with the encrypted data, without allocating/filling intermediary byte arrays.
For the benefit of anyone who ends up investigating similar needs:
I did end up using Cipher’s
update(ByteBuffer input, ByteBuffer output)method to populate a ByteBuffer, however it was using just the one output buffer rather than switching between a chain of fixed length ones. As the docs suggested (and as much as I tried to avoid this method), calls to getOutputSize() were used to get a ceiling on the output size of the next update() call, and a new buffer was allocated as necessary.There’s a limit to how much control you can have over the Cipher output, with or without using CipherOutputStream and its flush() method. It makes sense: if the first 15 bytes of input are submitted to a 128-bit (16 byte) block cipher such as AES, for example, output will always be withheld until further input is received. Flushing may prevent the cipher from buffering more than the block size of data but if you want to use
update(ByteBuffer input, ByteBuffer output)to neatly fill as much of the output as it can and then withhold the remaining output for the next call, then you’re out of luck – you would need to provide your own intermediary ‘overflow’ buffer to help dole out the bytes. It’s doable but a bit messy.