Class MultiRecipientDataSourceBuilder
- All Implemented Interfaces:
AutoCloseable, Destroyable, DataContentBuilder<MultiRecipientContent>
This builder is responsible only for the multi-recipient envelope: generating a content-encryption key (CEK), constructing recipient entries that can recover the same CEK, and scanning those entries on decrypt to recover it. Once the CEK is ready, the builder delegates the actual payload encryption or decryption to an injected symmetric data-content builder (AES or ChaCha), which is responsible for its own header and ciphertext framing.
Envelope layout. The stream begins with a recipient table owned by this builder:
- pack7 recipientCount
- for each recipient: UTF-8 entryId, then length-prefixed entryBlob
- then follows the symmetric builder's header and payload stream
-
Method Summary
Modifier and TypeMethodDescriptionaddOpener(RecipientOpener opener) Adds a custom recipient opener used during decryption when scanning recipient entries.addPasswordRecipient(char[] password, int iterations, int saltLen, int kekBytes) Adds a password-based recipient that derives a KEK via PBKDF2(HMAC-SHA-256) and AES-GCM-wraps the CEK.addPasswordRecipientDecoy(char[] password, int iterations, int saltLen, int kekBytes) Adds a password-based decoy recipient that derives a KEK via PBKDF2(HMAC-SHA-256) and AES-GCM-wraps the CEK.Adds a universal recipient backed by a caller-suppliedEncryptionContext.addRecipient(KemContext kem, int kekBytes, int saltLen) Adds a universal recipient backed by a caller-suppliedKemContext.Adds a universal decoy recipient backed by a caller-suppliedEncryptionContext.addRecipientDecoy(KemContext kem, int kekBytes, int saltLen) Adds a universal decoy recipient backed by a caller-suppliedKemContext.build(boolean encrypt) Builds either an encrypting or a decrypting content wrapper depending on theencryptflag.builder(ZeroEchoSession session) Creates a new builder for constructing a multi-recipient data source.voidclose()Releases recipient and opener resources not transferred bybuild(boolean).voiddestroy()Destroys recipient secrets still owned by this builder.headerLimits(int maxRecipients, int maxEntryLen) Applies defensive limits for parsing the recipient table.booleanReports whether this builder has released its owned recipient secrets.payloadKeyBytes(int keyBytes) Sets the CEK length in bytes that will be generated on encrypt and expected on decrypt.voidshuffle()Randomly permutes the list of recipients using a default source of randomness.unlockWith(UnlockMaterial material) Supplies the unlocking material for decryption, either a private key or a password.withAes(AesDataContentBuilder builder) Selects AES payload processing by installing an AES data content builder.withChaCha(ChaChaDataContentBuilder builder) Selects ChaCha20-Poly1305 payload processing by installing a ChaCha data content builder.
-
Method Details
-
builder
Creates a new builder for constructing a multi-recipient data source.The returned builder is initialized with safe defaults:
- Payload key length: 32 bytes (suitable for AES-256).
- Maximum recipients: 64.
Callers can customize these parameters before building the final data source.
- Parameters:
session- active session used to create recipient cryptographic contexts- Returns:
- a fresh
MultiRecipientDataSourceBuilderinstance with default settings
-
withAes
Selects AES payload processing by installing an AES data content builder.The AES builder owns its parameters and header. This method clears any previously installed ChaCha builder.
- Parameters:
builder- configured AES data content builder- Returns:
- this builder
- Throws:
NullPointerException- ifbuilderisnull
-
shuffle
public void shuffle()Randomly permutes the list of recipients using a default source of randomness. All permutations occur with approximately equal likelihood. -
withChaCha
Selects ChaCha20-Poly1305 payload processing by installing a ChaCha data content builder.The ChaCha builder owns its parameters and header. This method clears any previously installed AES builder.
- Parameters:
builder- configured ChaCha data content builder- Returns:
- this builder
- Throws:
NullPointerException- ifbuilderisnull
-
payloadKeyBytes
Sets the CEK length in bytes that will be generated on encrypt and expected on decrypt.Typical values: 16 or 32 for AES, 32 for ChaCha20-Poly1305.
- Parameters:
keyBytes- CEK length in bytes- Returns:
- this builder
- Throws:
IllegalArgumentException- ifkeyBytesis not positive
-
addPasswordRecipient
public MultiRecipientDataSourceBuilder addPasswordRecipient(char[] password, int iterations, int saltLen, int kekBytes) Adds a password-based recipient that derives a KEK via PBKDF2(HMAC-SHA-256) and AES-GCM-wraps the CEK.- Parameters:
password- the password; the caller should clear the array after useiterations- PBKDF2 iteration count; must be at least 10000saltLen- salt length in byteskekBytes- derived KEK length; exactly 16 or 32 bytes- Returns:
- this builder
- Throws:
NullPointerException- ifpasswordisnullIllegalArgumentException- ifiterationsis below 10000 orkekBytesis not exactly 16 or 32
-
addRecipient
Adds a universal recipient backed by a caller-suppliedKemContext.The context is used once to
KemContext.encapsulate()and then closed. The entry format and key wrapping are:kemCt || hkdfSalt || wrapIv || gcmWrappedCEK. The entry id is"KEM:<kemAlgorithmId>:GCM-WRAP"so that default openers still match.- Parameters:
kem- configured context forKeyUsage.ENCAPSULATEkekBytes- derived KEK length; exactly 16 or 32 bytessaltLen- HKDF salt length in bytes- Returns:
- this builder
- Throws:
NullPointerException- ifkemisnullIllegalArgumentException- ifkekBytesis not exactly 16 or 32
-
addRecipient
Adds a universal recipient backed by a caller-suppliedEncryptionContext.The context must be configured for
KeyUsage.ENCRYPTwith the appropriate asymmetric algorithm (e.g., RSA-OAEP, ElGamal, ECIES). The CEK is encrypted by streaming it throughEncryptionContext.attach(java.io.InputStream). The entry id is"ENC:" + ctx.algorithm().id().The context is consumed once and then closed by the builder.
- Parameters:
ctx- the encryption context configured for ENCRYPT- Returns:
- this builder
- Throws:
NullPointerException- ifctxisnull
-
addPasswordRecipientDecoy
public MultiRecipientDataSourceBuilder addPasswordRecipientDecoy(char[] password, int iterations, int saltLen, int kekBytes) Adds a password-based decoy recipient that derives a KEK via PBKDF2(HMAC-SHA-256) and AES-GCM-wraps the CEK.- Parameters:
password- the password; the caller should clear the array after useiterations- PBKDF2 iteration count; must be at least 10000saltLen- salt length in byteskekBytes- derived KEK length; exactly 16 or 32 bytes- Returns:
- this builder
- Throws:
NullPointerException- ifpasswordisnullIllegalArgumentException- ifiterationsis below 10000 orkekBytesis not exactly 16 or 32
-
addRecipientDecoy
Adds a universal decoy recipient backed by a caller-suppliedKemContext.The context is used once to
KemContext.encapsulate()and then closed. The entry format and key wrapping are:kemCt || hkdfSalt || wrapIv || gcmWrappedCEK. The entry id is"KEM:<kemAlgorithmId>:GCM-WRAP"so that default openers still match.- Parameters:
kem- configured context forKeyUsage.ENCAPSULATEkekBytes- derived KEK length; exactly 16 or 32 bytessaltLen- HKDF salt length in bytes- Returns:
- this builder
- Throws:
NullPointerException- ifkemisnullIllegalArgumentException- ifkekBytesis not exactly 16 or 32
-
addRecipientDecoy
Adds a universal decoy recipient backed by a caller-suppliedEncryptionContext.The context must be configured for
KeyUsage.ENCRYPTwith the appropriate asymmetric algorithm (e.g., RSA-OAEP, ElGamal, ECIES). The CEK is encrypted by streaming it throughEncryptionContext.attach(java.io.InputStream). The entry id is"ENC:" + ctx.algorithm().id().The context is consumed once and then closed by the builder.
- Parameters:
ctx- the encryption context configured for ENCRYPT- Returns:
- this builder
- Throws:
NullPointerException- ifctxisnull
-
unlockWith
Supplies the unlocking material for decryption, either a private key or a password.Only a single unlocking material is used. During decryption each opener attempts to recover the CEK from the recipient entries using this material. The builder borrows the object for the lifetime of the built decrypting content and never destroys it. The caller retains ownership and must destroy password material after the returned content and stream are no longer used.
- Parameters:
material- caller-owned unlocking material borrowed until decryption completes- Returns:
- this builder
- Throws:
NullPointerException- ifmaterialisnull
-
addOpener
Adds a custom recipient opener used during decryption when scanning recipient entries.If no openers are added explicitly, the default set is installed during
build(boolean)and covers KEM, RSA-OAEP, ElGamal, and PBKDF2 password recipients.The builder takes ownership of the opener. The opener must be reusable across all recipient entries and is closed after scanning or when the built content is abandoned.
- Parameters:
opener- reusable opener to add- Returns:
- this builder
- Throws:
NullPointerException- ifopenerisnull
-
headerLimits
Applies defensive limits for parsing the recipient table.All values are clamped to at least 1. These limits guard against resource exhaustion from malformed input.
- Parameters:
maxRecipients- maximum number of recipient entries allowedmaxEntryLen- maximum size in bytes for a single recipient entry blob- Returns:
- this builder
-
build
Builds either an encrypting or a decrypting content wrapper depending on theencryptflag.When
encryptistrue, the wrapper emits the recipient table followed by the symmetric builder's header and ciphertext. Whenencryptisfalse, the wrapper unlocks the CEK from the first matching recipient entry and streams the symmetric plaintext.Default openers are installed automatically if none were added explicitly. They support KEM, RSA-OAEP, ElGamal with PKCS1 padding, and, when password unlock material is selected, PBKDF2 password recipients. Password decryption requires explicit session PBKDF2 limits.
- Specified by:
buildin interfaceDataContentBuilder<MultiRecipientContent>- Parameters:
encrypt-trueto build an encrypting content source,falseto build a decrypting one- Returns:
- an encrypting or decrypting one-shot content wrapper; callers must close an abandoned result
- Throws:
IllegalStateException- if no symmetric builder is selected, if encryption is requested with no recipients, or if decryption is requested without unlock material
-
destroy
Destroys recipient secrets still owned by this builder.Recipients transferred to a successfully built encrypting content object are owned and destroyed by that object instead.
- Specified by:
destroyin interfaceDestroyable- Throws:
DestroyFailedException- if recipient cleanup fails
-
isDestroyed
public boolean isDestroyed()Reports whether this builder has released its owned recipient secrets.- Specified by:
isDestroyedin interfaceDestroyable- Returns:
trueafterclose()ordestroy()
-
close
Releases recipient and opener resources not transferred bybuild(boolean).- Specified by:
closein interfaceAutoCloseable- Throws:
IOException- if recipient cleanup fails
-