Class CipherTransformInputStreamBuilder
InputStreams backed by a
Cipher.
This builder configures and creates an InputStream that reads from an
upstream source and applies a block-based transformation using a provided
Cipher. Three stream variants are available:
- Independent block stream - invokes
Cipher.doFinal(byte[], int, int, byte[], int)for each full input block; a final partial block (if any) is processed by a singledoFinal. This mode is restricted to RSA and ElGamal. - Left-padded independent block stream - like the independent block
stream, but left-pads each transformed output block with zeros up to
outChunkSize. Final blocks must be complete; otherwise anIllegalStateExceptionis thrown. - Continuous stream - uses
Cipher.update(...)for bulk bytes and a singledoFinal()at end of stream. This is suitable for CTR/CFB/OFB/GCM and padding modes.
Block sizing
The builder exposes inChunkSize and outChunkSize. For AES in
ECB/CBC/CTR-like modes these are typically 16 bytes, but the stream can use
larger logical chunks for I/O efficiency. The implementation buffers up to
bufferedBlocks chunks per fill to balance throughput and memory.
Finalization headroom
Some ciphers or modes produce extra bytes at end-of-stream (for example, a
padding block for CBC/PKCS5 or an authentication tag for GCM). To ensure the
final doFinal(...) fits even when the steady-state output area is
full, you can reserve additional output capacity via
withFinalizationOutputChunks(int). This increases only the
output-side buffer, leaving the input buffer size unchanged.
Thread-safety
Instances produced by this builder are not thread-safe. Do not share a single stream across threads without external synchronization.Examples
// AES/ECB/NOPADDING: aligned input, no extra finalization output
Cipher c1 = Cipher.getInstance("AES/ECB/NOPADDING");
c1.init(Cipher.ENCRYPT_MODE, key);
InputStream s1 = CipherTransformInputStreamBuilder.builder()
.withUpstream(in)
.withCipher(c1)
.withUpdateStreaming(true) // allowed; final len must be multiple of 16
.withInputBlockSize(16)
.withOutputBlockSize(16)
.withBufferedBlocks(200)
.withFinalizationOutputChunks(0)
.build();
// AES/CBC/PKCS5PADDING: one final padding block
Cipher c2 = Cipher.getInstance("AES/CBC/PKCS5PADDING");
c2.init(Cipher.ENCRYPT_MODE, key, ivSpec);
InputStream s2 = CipherTransformInputStreamBuilder.builder()
.withUpstream(in)
.withCipher(c2)
.withUpdateStreaming(true)
.withInputBlockSize(16)
.withOutputBlockSize(16)
.withBufferedBlocks(200)
.withFinalizationOutputChunks(1)
.build();
// AES/GCM/NOPADDING (16-byte tag): tail + tag -> reserve two chunks
Cipher c3 = Cipher.getInstance("AES/GCM/NOPADDING");
c3.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
InputStream s3 = CipherTransformInputStreamBuilder.builder()
.withUpstream(in)
.withCipher(c3)
.withUpdateStreaming(true)
.withInputBlockSize(16).withOutputBlockSize(16)
.withBufferedBlocks(200)
.withFinalizationOutputChunks(2)
.build();
// ElGamal (asymmetric; non-streaming semantics): no extra finalization output
Cipher c4 = Cipher.getInstance("ElGamal/None/NOPADDING"); // provider-specific
c4.init(Cipher.ENCRYPT_MODE, publicKey, params);
InputStream s4 = CipherTransformInputStreamBuilder.builder()
.withUpstream(in)
.withCipher(c4)
.withIndependentBlocks()
.withInputBlockSize(elgIn)
.withOutputBlockSize(elgOut)
.withBufferedBlocks(200)
.withFinalizationOutputChunks(0)
.build();
- Since:
- 1.0
-
Method Summary
Modifier and TypeMethodDescriptionbuild()Builds a chunk-transformingInputStreamusing the configured options.builder()Returns a new builder instance with default settings.withBufferedBlocks(int bufferedBlocks) Sets the number of blocks buffered per internal fill cycle.withCipher(Cipher cipher) Sets theCipherused to transform blocks.withFinalizationOutputChunks(int chunks) Sets the number of extra output chunks reserved for the finalization step.Selects independent-block processing, in which every logical input block is passed to a separateCipher.doFinal(byte[], int, int, byte[], int)invocation.withInputBlockSize(int inSize) Sets the size of a single input block fed to the cipher.withLeftZeroPadding(boolean padding) Configures whether left zero padding is applied to each output block.withOutputBlockSize(int outSize) Sets the size of a single output block expected from the cipher.Enables update-driven streaming (Cipher.update during transform, single doFinal at EOF).withUpdateStreaming(boolean enable) Controls update-driven streaming explicitly.withUpstream(InputStream upstream) Sets the upstream source from which bytes will be read and transformed.
-
Method Details
-
builder
Returns a new builder instance with default settings.Defaults:
inChunkSize=256,outChunkSize=256,bufferedBlocks=100, padding disabled.- Returns:
- a new builder
-
withUpstream
Sets the upstream source from which bytes will be read and transformed.- Parameters:
upstream- the input stream to wrap; must not be null- Returns:
- this builder for chaining
-
withCipher
-
withLeftZeroPadding
Configures whether left zero padding is applied to each output block.If set to
true, the builder will produce a padded stream variant where each block is left-filled with zeros up tooutChunkSize. Iffalse, the plain non-padded variant is used.- Parameters:
padding-trueto enable left zero padding,falseto disable it- Returns:
- this builder for chaining
-
withInputBlockSize
Sets the size of a single input block fed to the cipher.- Parameters:
inSize- input chunk size in bytes; must be > 1- Returns:
- this builder for chaining
-
withOutputBlockSize
Sets the size of a single output block expected from the cipher.- Parameters:
outSize- output chunk size in bytes; must be > 0- Returns:
- this builder for chaining
-
withBufferedBlocks
Sets the number of blocks buffered per internal fill cycle.The internal buffers will be sized to
inChunkSize * bufferedBlocksandoutChunkSize * bufferedBlocks.- Parameters:
bufferedBlocks- number of blocks to buffer; must be > 0- Returns:
- this builder for chaining
-
withFinalizationOutputChunks
Sets the number of extra output chunks reserved for the finalization step.Some modes may emit additional bytes at the very end (padding block for CBC/PKCS5, authentication tag for GCM/CCM/EAX, provider flush). The output buffer is allocated with
bufferedBlocks + finalizationOutputChunkschunks to guarantee that the finaldoFinal(...)fits even when the steady-state area is full.Recommended values
- AES/CBC/PKCS5PADDING:
1 - AES/GCM (16-byte tag):
2 - AES/CTR, CFB, OFB:
1(covers a tail up to 15 bytes) - AES/CBC/NOPADDING:
0(total length must be a multiple of block size) - ElGamal/RSA:
0(non-streaming; no extra finalization block)
Notes
- For GCM with a different tag size, use
ceil((15 + tagLen)/outChunkSize). With 12–16 byte tags andoutChunkSize=16,2is still safe. - This setting enlarges only the output buffer capacity; the input
buffer remains
inChunkSize * bufferedBlocks.
- Parameters:
chunks- extra output chunks reserved for finalization; must be>= 0- Returns:
- this builder for chaining
- AES/CBC/PKCS5PADDING:
-
withUpdateStreaming
Enables update-driven streaming (Cipher.update during transform, single doFinal at EOF). Suitable for CTR/CFB/OFB/GCM and padding modes.- Returns:
- this builder for chaining
-
withUpdateStreaming
Controls update-driven streaming explicitly.- Parameters:
enable- true to use update-driven streaming, false for per-block doFinal variant- Returns:
- this builder
-
withIndependentBlocks
Selects independent-block processing, in which every logical input block is passed to a separateCipher.doFinal(byte[], int, int, byte[], int)invocation.This mode is supported only for RSA and ElGamal transformations. Stateful symmetric modes, including AES-GCM and AES-CBC, must use
withUpdateStreaming().- Returns:
- this builder
-
build
Builds a chunk-transformingInputStreamusing the configured options.The returned stream pulls from
upstreamand applies the configuredcipher. The output buffer is allocated tooutChunkSize * (bufferedBlocks + finalizationOutputChunks)bytes so that the finaldoFinal(...)can emit padding or tags even when the steady-state output window is full.- Returns:
- a new InputStream that transforms bytes on the fly
- Throws:
NullPointerException- ifupstreamorcipheris nullIllegalArgumentException- if independent-block processing is selected for an unsupported algorithm or buffer geometry is invalid
-