diff --git a/api/src/main/java/io/jsonwebtoken/Jwts.java b/api/src/main/java/io/jsonwebtoken/Jwts.java
index eed9bca1b..5d87653be 100644
--- a/api/src/main/java/io/jsonwebtoken/Jwts.java
+++ b/api/src/main/java/io/jsonwebtoken/Jwts.java
@@ -1068,7 +1068,7 @@ public static JwtBuilder builder() {
/**
* Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser}.
*
- * @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser}.
+ * @return a new {@link JwtParserBuilder} instance that can be configured create an immutable/thread-safe {@link JwtParser}.
*/
public static JwtParserBuilder parser() {
return JWT_PARSER_BUILDER_SUPPLIER.get();
diff --git a/api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java b/api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java
index 7e82d85f9..721f53a2a 100644
--- a/api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java
+++ b/api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java
@@ -78,6 +78,12 @@ public interface AeadAlgorithm extends Identifiable, KeyLengthSupplier, KeyBuild
*/
void encrypt(AeadRequest req, AeadResult res) throws SecurityException;
+ default AeadResult encrypt(AeadRequest req, OutputStream out) throws SecurityException {
+ AeadResult result = AeadResult.with(out);
+ encrypt(req, result);
+ return result;
+ }
+
/**
* Decrypts ciphertext and authenticates any {@link DecryptAeadRequest#getAssociatedData() associated data},
* writing the decrypted plaintext to the provided {@code out}put stream.
diff --git a/api/src/main/java/io/jsonwebtoken/security/AeadRequest.java b/api/src/main/java/io/jsonwebtoken/security/AeadRequest.java
index 828786982..5dc1aa542 100644
--- a/api/src/main/java/io/jsonwebtoken/security/AeadRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/AeadRequest.java
@@ -17,6 +17,7 @@
import javax.crypto.SecretKey;
import java.io.InputStream;
+import java.io.OutputStream;
/**
* A request to an {@link AeadAlgorithm} to perform authenticated encryption with a supplied symmetric
@@ -27,4 +28,46 @@
* @since 0.12.0
*/
public interface AeadRequest extends SecureRequest, AssociatedDataSupplier {
+
+ /**
+ * Named parameters (setters) used to configure an {@link AeadRequest AeadRequest} instance.
+ *
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends SecureRequest.Params {
+
+ /**
+ * Sets any "associated data" that must be integrity protected (but not encrypted) when performing
+ * AEAD encryption or decryption.
+ *
+ * @param aad the {@code InputStream} containing any associated data that must be integrity protected or
+ * verified during AEAD encryption or decryption.
+ * @return the instance for method chaining.
+ * @see AeadAlgorithm#encrypt(AeadRequest, AeadResult)
+ * @see AeadAlgorithm#decrypt(DecryptAeadRequest, OutputStream)
+ */
+ M associatedData(InputStream aad);
+ }
+
+ /**
+ * A builder for creating new immutable {@link AeadRequest} instances used for AEAD encryption via
+ * {@link AeadAlgorithm#encrypt(AeadRequest, AeadResult)}.
+ *
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params, io.jsonwebtoken.lang.Builder {
+ }
+
+ /**
+ * Returns a new {@link AeadRequest.Builder} for creating immutable {@link AeadRequest}s used for AEAD encryption
+ * via {@link AeadAlgorithm#encrypt(AeadRequest, AeadResult)}.
+ *
+ * @return a new {@link AeadRequest.Builder} for creating immutable {@link AeadRequest}s used for AEAD encryption
+ * via {@link AeadAlgorithm#encrypt(AeadRequest, AeadResult)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ static AeadRequest.Builder builder() {
+ return Suppliers.AEAD_REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/AeadResult.java b/api/src/main/java/io/jsonwebtoken/security/AeadResult.java
index c8734b5a3..51e9164b1 100644
--- a/api/src/main/java/io/jsonwebtoken/security/AeadResult.java
+++ b/api/src/main/java/io/jsonwebtoken/security/AeadResult.java
@@ -24,7 +24,7 @@
*
* @since 0.12.0
*/
-public interface AeadResult {
+public interface AeadResult extends DigestSupplier, IvSupplier {
/**
* Returns the {@code OutputStream} the AeadAlgorithm will use to write the resulting ciphertext during
@@ -50,4 +50,18 @@ public interface AeadResult {
* @return the AeadResult for method chaining.
*/
AeadResult setIv(byte[] iv);
+
+ /**
+ * Returns a new {@link AeadResult} with the specified {@link OutputStream} that will be used to write the
+ * resulting ciphertext during encryption or plaintext during decryption.
+ *
+ * @param out the {@link OutputStream} that will be used to write the resulting ciphertext during encryption
+ * @return a new {@link AeadResult} with the specified {@link OutputStream} that will be used to write the
+ * resulting ciphertext during encryption.
+ * @since JJWT_RELEASE_VERSION
+ */
+ static AeadResult with(OutputStream out) {
+ return Suppliers.AEAD_RESULT_FACTORY.apply(out);
+ }
+
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/DecryptAeadRequest.java b/api/src/main/java/io/jsonwebtoken/security/DecryptAeadRequest.java
index 5faf1f6a5..b49d35a5f 100644
--- a/api/src/main/java/io/jsonwebtoken/security/DecryptAeadRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/DecryptAeadRequest.java
@@ -16,13 +16,61 @@
package io.jsonwebtoken.security;
import javax.crypto.SecretKey;
+import java.io.OutputStream;
/**
- * A request to an {@link AeadAlgorithm} to decrypt ciphertext and perform integrity-protection with a supplied
+ * A request to an {@link AeadAlgorithm} to decrypt ciphertext with integrity verification with a supplied
* decryption {@link SecretKey}. Extends both {@link IvSupplier} and {@link DigestSupplier} to
* ensure the respective required IV and AAD tag returned from an {@link AeadResult} are available for decryption.
*
* @since 0.12.0
*/
public interface DecryptAeadRequest extends AeadRequest, IvSupplier, DigestSupplier {
+
+ /**
+ * Named parameters (setters) used to configure an {@link AeadRequest AeadRequest} instance.
+ *
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends AeadRequest.Params {
+
+ /**
+ * Sets the required initialization vector used during AEAD decryption.
+ *
+ * @param iv the required initialization vector used during AEAD decryption.
+ * @return the instance for method chaining.
+ */
+ M iv(byte[] iv);
+
+ /**
+ * Sets the required AEAD Authentication Tag used to verify message authenticity during AEAD decryption.
+ *
+ * @param digest the required AEAD Authentication Tag used to verify message authenticity during AEAD decryption.
+ * @return the instance for method chaining.
+ */
+ M digest(byte[] digest);
+ }
+
+ /**
+ * A builder for creating new immutable {@link DecryptAeadRequest}s used for AEAD decryption via
+ * {@link AeadAlgorithm#decrypt(DecryptAeadRequest, OutputStream)}.
+ *
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends io.jsonwebtoken.lang.Builder, Params {
+ }
+
+ /**
+ * Returns a new {@link DecryptAeadRequest.Builder} for creating immutable {@link DecryptAeadRequest}s used for
+ * AEAD decryption via {@link AeadAlgorithm#decrypt(DecryptAeadRequest, OutputStream)}.
+ *
+ * @return a new {@link DecryptAeadRequest.Builder} for creating immutable {@link DecryptAeadRequest}s used for
+ * AEAD decryption via {@link AeadAlgorithm#decrypt(DecryptAeadRequest, OutputStream)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ static DecryptAeadRequest.Builder builder() {
+ return Suppliers.DECRYPT_AEAD_REQUEST_BUILDER.get();
+ }
+
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/DecryptionKeyRequest.java b/api/src/main/java/io/jsonwebtoken/security/DecryptionKeyRequest.java
index 893cad946..ab6677851 100644
--- a/api/src/main/java/io/jsonwebtoken/security/DecryptionKeyRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/DecryptionKeyRequest.java
@@ -35,8 +35,46 @@
* {@code JWE Encrypted Key} (such as an initialization vector, authentication tag, ephemeral key, etc) is expected
* to be available in the JWE protected header, accessible via {@link #getHeader()}.
*
- * @param the type of {@link Key} used during the request to obtain the resulting decryption key.
+ * @param the type of key used by the {@link KeyAlgorithm} to obtain the JWE Content Encryption Key (CEK).
+ * @see KeyAlgorithm#getDecryptionKey(DecryptionKeyRequest)
* @since 0.12.0
*/
public interface DecryptionKeyRequest extends SecureRequest, KeyRequest {
+
+ /**
+ * Named parameters (setters) used to configure a {@link DecryptionKeyRequest DecryptionKeyRequest}
+ * instance.
+ *
+ * @param the type of key used by the {@link KeyAlgorithm} to obtain the JWE Content Encryption Key (CEK).
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends KeyRequest.Params,
+ SecureRequest.Params {
+ }
+
+ /**
+ * A builder for creating new immutable {@link DecryptionKeyRequest} instances used to get a JWE
+ * decryption key via {@link KeyAlgorithm#getDecryptionKey(DecryptionKeyRequest)}.
+ *
+ * @param the type of key used by the {@link KeyAlgorithm} to obtain the JWE Content Encryption Key (CEK).
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params>, io.jsonwebtoken.lang.Builder> {
+ }
+
+ /**
+ * Returns a new {@link DecryptionKeyRequest.Builder} for creating immutable {@link DecryptionKeyRequest}s used to
+ * get a JWE decryption key via {@link KeyAlgorithm#getDecryptionKey(DecryptionKeyRequest)}.
+ *
+ * @param the type of key used to obtain the JWE decryption key via
+ * {@link KeyAlgorithm#getDecryptionKey(DecryptionKeyRequest)}.
+ * @return a new {@link DecryptionKeyRequest.Builder} for creating immutable {@link DecryptionKeyRequest}s used to
+ * get a JWE decryption key via {@link KeyAlgorithm#getDecryptionKey(DecryptionKeyRequest)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ @SuppressWarnings("unchecked")
+ static DecryptionKeyRequest.Builder builder() {
+ return (DecryptionKeyRequest.Builder) Suppliers.DECRYPTION_KEY_REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/HashAlgorithm.java b/api/src/main/java/io/jsonwebtoken/security/HashAlgorithm.java
index 3bc4ec449..6adf21912 100644
--- a/api/src/main/java/io/jsonwebtoken/security/HashAlgorithm.java
+++ b/api/src/main/java/io/jsonwebtoken/security/HashAlgorithm.java
@@ -16,8 +16,10 @@
package io.jsonwebtoken.security;
import io.jsonwebtoken.Identifiable;
+import io.jsonwebtoken.lang.Assert;
import java.io.InputStream;
+import java.util.function.Consumer;
/**
* A {@link DigestAlgorithm} that computes and verifies digests without the use of a cryptographic key, such as for
@@ -42,4 +44,82 @@
* @since 0.12.0
*/
public interface HashAlgorithm extends DigestAlgorithm, VerifyDigestRequest> {
+
+ /**
+ * Computes a digest of a request {@link Request#getPayload() payload} {@code InputStream} using configured
+ * parameters. This is a lambda-style method to execute the request in-line instead of requiring the caller to
+ * first use a {@link Request.Builder} to construct the request.
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the request
+ * payload stream if necessary after calling this method.
+ *
+ * @param c consumer supporting lambda-style specification of digest {@link Request.Params}.
+ * @return the computed digest of the request {@link Request#getPayload() payload}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default byte[] digest(Consumer> c) {
+ Assert.notNull(c, "Consumer cannot be null");
+ Request.Builder b = Request.builder();
+ c.accept(b);
+ Request r = b.build();
+ return digest(r);
+ }
+
+ /**
+ * Computes a digest of the specified {@code is} input stream.
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the payload
+ * stream if necessary after calling this method.
+ *
+ * @param is the {@code InputStream} that will be consumed to compute the digest. Callers are expected to
+ * {@link InputStream#close() close} or {@link InputStream#reset() reset} the payload stream if necessary
+ * after calling this method.
+ * @return the computed digest of the specified {@code is} input stream.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default byte[] digest(InputStream is) {
+ return digest(c -> c.payload(is));
+ }
+
+ /**
+ * Returns {@code true} if the request's specified {@link VerifyDigestRequest#getDigest() digest} matches (equals)
+ * the algorithm's computed digest of the request {@link VerifyDigestRequest#getPayload() payload}, {@code false}
+ * otherwise. This is a lambda-style method to execute the request in-line instead of requiring the caller to first
+ * use a {@link VerifyDigestRequest.Builder} to construct the request.
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the request
+ * payload stream if necessary after calling this method.
+ *
+ * @param c consumer supporting lambda-style specification of {@link VerifyDigestRequest.Params}.
+ * @return {@code true} if the request's specified {@link VerifyDigestRequest#getDigest() digest} matches (equals)
+ * the algorithm's computed digest of the request {@link VerifyDigestRequest#getPayload() payload}, {@code false}
+ * otherwise.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default boolean verify(Consumer> c) {
+ Assert.notNull(c, "Consumer cannot be null");
+ VerifyDigestRequest.Builder b = VerifyDigestRequest.builder();
+ c.accept(b);
+ VerifyDigestRequest r = b.build();
+ return verify(r);
+ }
+
+ /**
+ * Returns {@code true} if the specified {@code digest} matches (equals) the algorithm's computed digest of the
+ * specified {@code is} input stream, {@code false} otherwise.
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the payload
+ * stream if necessary after calling this method.
+ *
+ * @param is the {@code InputStream} that will be consumed to compute the digest. Callers are expected to
+ * {@link InputStream#close() close} or {@link InputStream#reset() reset} the payload stream if
+ * necessary after calling this method.
+ * @param digest the previously-computed digest to compare with the algorithm's computed digest of {@code is}.
+ * @return {@code true} if the specified {@code digest} matches (equals) the algorithm's computed digest of the
+ * specified {@code is} input stream, {@code false} otherwise.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default boolean verify(InputStream is, byte[] digest) {
+ return verify(c -> c.payload(is).digest(digest));
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/KeyAlgorithm.java b/api/src/main/java/io/jsonwebtoken/security/KeyAlgorithm.java
index e3cd13c65..3c5494f26 100644
--- a/api/src/main/java/io/jsonwebtoken/security/KeyAlgorithm.java
+++ b/api/src/main/java/io/jsonwebtoken/security/KeyAlgorithm.java
@@ -16,10 +16,13 @@
package io.jsonwebtoken.security;
import io.jsonwebtoken.Identifiable;
+import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.lang.Assert;
import javax.crypto.SecretKey;
import java.security.Key;
+import java.util.function.Consumer;
/**
* A {@code KeyAlgorithm} produces the {@link SecretKey} used to encrypt or decrypt a JWE. The {@code KeyAlgorithm}
@@ -65,6 +68,17 @@ public interface KeyAlgorithm extends Identifiable
*/
KeyResult getEncryptionKey(KeyRequest request) throws SecurityException;
+ default KeyResult getEncryptionKey(Consumer> p) throws SecurityException {
+ Assert.notNull(p, "Consumer cannot be null");
+ KeyRequest.Builder builder = KeyRequest.builder();
+ p.accept(builder);
+ return getEncryptionKey(builder.build());
+ }
+
+ default KeyResult getEncryptionKey(E key, JweHeader header, AeadAlgorithm enc) throws SecurityException {
+ return getEncryptionKey(p -> p.payload(key).header(header).encryptionAlgorithm(enc));
+ }
+
/**
* Return the {@link SecretKey} that should be used to decrypt a JWE via the request's specified
* {@link DecryptionKeyRequest#getEncryptionAlgorithm() AeadAlgorithm}.
@@ -81,4 +95,15 @@ public interface KeyAlgorithm extends Identifiable
* @throws SecurityException if there is a problem obtaining or decrypting the AEAD {@code SecretKey}.
*/
SecretKey getDecryptionKey(DecryptionKeyRequest request) throws SecurityException;
+
+ default SecretKey getDecryptionKey(Consumer> p) throws SecurityException {
+ Assert.notNull(p, "Consumer cannot be null");
+ DecryptionKeyRequest.Builder builder = DecryptionKeyRequest.builder();
+ p.accept(builder);
+ return getDecryptionKey(builder.build());
+ }
+
+ default SecretKey getDecryptionKey(byte[] cekCiphertext, D decryptionKey, JweHeader header, AeadAlgorithm enc) throws SecurityException {
+ return getDecryptionKey(p -> p.payload(cekCiphertext).key(decryptionKey).header(header).encryptionAlgorithm(enc));
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/KeyRequest.java b/api/src/main/java/io/jsonwebtoken/security/KeyRequest.java
index ffe22061e..1e1adf6f0 100644
--- a/api/src/main/java/io/jsonwebtoken/security/KeyRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/KeyRequest.java
@@ -17,6 +17,8 @@
import io.jsonwebtoken.JweHeader;
+import java.security.Key;
+
/**
* A request to a {@link KeyAlgorithm} to obtain the key necessary for AEAD encryption or decryption. The exact
* {@link AeadAlgorithm} that will be used is accessible via {@link #getEncryptionAlgorithm()}.
@@ -74,4 +76,73 @@ public interface KeyRequest extends Request {
* reading or writing any {@link KeyAlgorithm}-specific information.
*/
JweHeader getHeader();
+
+ /**
+ * Named parameters (setters) used to configure a {@link KeyRequest KeyRequest} instance.
+ *
+ * @param the type of request payload. For an encryption key request, this will be the
+ * key used to obtain the encryption key. For a decryption key request, this will be the encrypted CEK
+ * (Content Encryption Key) ciphertext byte array.
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends Request.Params {
+
+ /**
+ * Sets the {@link JweHeader} that will be used to construct the final JWE header, available for
+ * reading or writing any {@link KeyAlgorithm}-specific information.
+ *
+ * For an encryption key request, any public information specific to the called {@code KeyAlgorithm}
+ * implementation that is required to be transmitted in the JWE (such as an initialization vector,
+ * authentication tag or ephemeral key, etc) is expected to be added to this header. Although the header is
+ * checked for authenticity and integrity, it itself is not encrypted, so
+ * {@link KeyAlgorithm}s should never place any secret or private information in the header.
+ *
+ * For a decryption request, any public information necessary by the called {@link KeyAlgorithm}
+ * (such as an initialization vector, authentication tag, ephemeral key, etc) is expected to be available in
+ * this header.
+ *
+ * @param header the {@link JweHeader} that will be used to construct the final JWE header, available for
+ * reading or writing any {@link KeyAlgorithm}-specific information.
+ * @return the instance for method chaining.
+ */
+ M header(JweHeader header);
+
+ /**
+ * Sets the {@link AeadAlgorithm} that will be called for encryption or decryption after processing the
+ * {@code KeyRequest}. {@link KeyAlgorithm} implementations that generate an ephemeral {@code SecretKey} to use
+ * as what the JWE specification calls a
+ * "Content Encryption Key (CEK)" should call the {@code AeadAlgorithm}'s
+ * {@link AeadAlgorithm#key() key()} builder to create a key suitable for that exact {@code AeadAlgorithm}.
+ *
+ * @param alg the {@link AeadAlgorithm} that will be called for encryption or decryption after processing the
+ * {@code KeyRequest}.
+ * @return the instance for method chaining.
+ */
+ M encryptionAlgorithm(AeadAlgorithm alg);
+ }
+
+ /**
+ * A builder for creating {@link KeyRequest}s used to get a JWE encryption key via
+ * {@link KeyAlgorithm#getEncryptionKey(KeyRequest)}.
+ *
+ * @param the type of {@link java.security.Key Key} used to obtain the encryption key.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params>, io.jsonwebtoken.lang.Builder> {
+ }
+
+ /**
+ * Returns a new {@link KeyRequest.Builder} for creating immutable {@link KeyRequest}s used to get a JWE
+ * encryption key via {@link KeyAlgorithm#getEncryptionKey(KeyRequest)}.
+ *
+ * @param the type of {@link java.security.Key Key} used to obtain the JWE content encryption key.
+ * @return a new {@link KeyRequest.Builder} for creating immutable {@link KeyRequest}s used to get a JWE
+ * encryption key via {@link KeyAlgorithm#getEncryptionKey(KeyRequest)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ @SuppressWarnings("unchecked")
+ static KeyRequest.Builder builder() {
+ return (KeyRequest.Builder) Suppliers.KEY_REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/Request.java b/api/src/main/java/io/jsonwebtoken/security/Request.java
index 77e0d32f9..c6b8a6ee1 100644
--- a/api/src/main/java/io/jsonwebtoken/security/Request.java
+++ b/api/src/main/java/io/jsonwebtoken/security/Request.java
@@ -54,4 +54,63 @@ public interface Request extends Message {
* {@code null} if a default {@link SecureRandom} should be used.
*/
SecureRandom getSecureRandom();
+
+ /**
+ * Named parameters (setters) used to configure a {@link Request Request} instance.
+ *
+ * @param the type of payload in the request.
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> {
+
+ /**
+ * Sets the JCA provider that should be used for cryptographic operations during the request. A {@code null}
+ * value indicates that the JCA subsystem preferred provider should be used by default.
+ *
+ * @param provider the JCA provider that should be used for cryptographic operations during the request, or
+ * {@code null} to use the JCA subsystem preferred provider.
+ * @return the instance for method chaining.
+ */
+ M provider(Provider provider);
+
+ /**
+ * Sets the {@code SecureRandom} to use when performing cryptographic operations during the request. A
+ * {@code null} value ensures a default {@link SecureRandom} should be used.
+ *
+ * @param random the {@code SecureRandom} to use when performing cryptographic operations during the request,
+ * or {@code null} if a default {@link SecureRandom} should be used.
+ * @return the instance for method chaining.
+ */
+ M random(SecureRandom random);
+
+ /**
+ * Sets the request payload.
+ *
+ * @param payload the request payload.
+ * @return the instance for method chaining.
+ */
+ M payload(T payload);
+ }
+
+ /**
+ * A builder for creating new immutable {@link Request} instances.
+ *
+ * @param the type of payload in the request.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends io.jsonwebtoken.lang.Builder>, Params> {
+ }
+
+ /**
+ * Returns a new {@link Request.Builder} for creating immutable {@link Request}s.
+ *
+ * @param the type of payload in the request.
+ * @return a new {@link Request.Builder} for creating immutable {@link Request}s.
+ * @since JJWT_RELEASE_VERSION
+ */
+ @SuppressWarnings("unchecked")
+ static Builder builder() {
+ return (Builder) Suppliers.REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/SecureDigestAlgorithm.java b/api/src/main/java/io/jsonwebtoken/security/SecureDigestAlgorithm.java
index fbe671d72..fd56e4792 100644
--- a/api/src/main/java/io/jsonwebtoken/security/SecureDigestAlgorithm.java
+++ b/api/src/main/java/io/jsonwebtoken/security/SecureDigestAlgorithm.java
@@ -16,9 +16,11 @@
package io.jsonwebtoken.security;
import io.jsonwebtoken.Identifiable;
+import io.jsonwebtoken.lang.Assert;
import java.io.InputStream;
import java.security.Key;
+import java.util.function.Consumer;
/**
* A {@link DigestAlgorithm} that requires a {@link Key} to compute and verify the authenticity of digests using either
@@ -52,4 +54,99 @@
*/
public interface SecureDigestAlgorithm
extends DigestAlgorithm, VerifySecureDigestRequest> {
+
+ /**
+ * Computes a mac or signature of an {@link InputStream} using named parameters. At least the
+ * {@link SecureRequest#getPayload() payload} and mac or signing {@link SecureRequest#getKey() key} parameters
+ * must be specified. For example:
+ *
+ *
+ * alg.digest(r -> r.{@link SecureRequest.Params#payload(Object) payload}(is).{@link SecureRequest.Params#key(Key) key}(key));
+ *
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the
+ * payload {@code InputStream} if necessary after calling this method.
+ *
+ * @param c consumer supporting lambda-style specification of named digest {@link SecureRequest.Params}.
+ * @return the computed mac or signature for the request {@link SecureRequest#getPayload() payload}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default byte[] digest(Consumer> c) {
+ Assert.notNull(c, "Consumer cannot be null");
+ SecureRequest.Builder b = SecureRequest.builder();
+ c.accept(b);
+ SecureRequest r = b.build();
+ return digest(r);
+ }
+
+ /**
+ * Computes a mac or signature of the specified {@code is} input stream using the specified {@code key}. This is
+ * a convenience method equivalent to:
+ *
+ *
+ * {@link #digest(Request) digest}(r -> r.{@link SecureRequest.Params#payload(Object) payload}(is).{@link SecureRequest.Params#key(Key) key}(key));
+ *
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the
+ * {@code is} input stream if necessary after calling this method.
+ *
+ * @param key the key used to compute the mac or signature
+ * @param is the {@code InputStream} that will be consumed to compute the mac or signature. Callers are expected to
+ * {@link InputStream#close() close} or {@link InputStream#reset() reset} the {@code is} input stream if
+ * necessary after calling this method.
+ * @return the computed mac or signature of the specified {@code is} input stream.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default byte[] digest(S key, InputStream is) {
+ return digest(c -> c.payload(is).key(key));
+ }
+
+ /**
+ * Returns {@code true} if a given mac or signature for an {@link InputStream} is authentic, {@code false}
+ * otherwise. At least the {@link VerifySecureDigestRequest#getPayload() payload} stream, verification
+ * {@link VerifySecureDigestRequest#getKey() key}, and {@link VerifySecureDigestRequest#getDigest() digest}
+ * parameters must be specified. For example:
+ *
+ *
+ * alg.verify(r -> r.{@link VerifySecureDigestRequest.Params#key(Key) key}(key).{@link VerifySecureDigestRequest.Params#payload(Object) payload}(is).{@link VerifySecureDigestRequest.Params#digest(byte[]) digest}(macOrSignature));
+ *
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the request
+ * payload stream if necessary after calling this method.
+ *
+ * @param c consumer supporting lambda-style specification of named {@link VerifySecureDigestRequest.Params}.
+ * @return {@code true} if a given mac or signature for an {@code InputStream} is authentic, {@code false} otherwise.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default boolean verify(Consumer> c) {
+ Assert.notNull(c, "Consumer cannot be null");
+ VerifySecureDigestRequest.Builder b = VerifySecureDigestRequest.builder();
+ c.accept(b);
+ VerifySecureDigestRequest r = b.build();
+ return verify(r);
+ }
+
+ /**
+ * Returns {@code true} if the given {@code macOrSignature} for the {@code is} input stream is
+ * authentic, {@code false} otherwise. This is a convenience method equivalent to:
+ *
+ *
+ * {@link #verify(VerifyDigestRequest) verify}(r -> r.{@link VerifySecureDigestRequest.Params#key(Key) key}(key).{@link VerifySecureDigestRequest.Params#payload(Object) payload}(is).{@link VerifySecureDigestRequest.Params#digest(byte[]) digest}(macOrSignature));
+ *
+ *
+ * Callers are expected to {@link InputStream#close() close} or {@link InputStream#reset() reset} the
+ * {@code is} input stream if necessary after calling this method.
+ *
+ * @param key the key used to verify the mac or signature
+ * @param is the data claimed as authentic by {@code macOrSignature}, which will be consumed to
+ * verify authenticity. Callers are expected to {@link InputStream#close() close} or
+ * {@link InputStream#reset() reset} this stream if necessary after calling this method.
+ * @param macOrSignature the mac or signature claimed to authenticate the {@code is} input stream.
+ * @return {@code true} if the given {@code macOrSignature} for the {@code is} input stream is authentic,
+ * {@code false} otherwise.
+ * @since JJWT_RELEASE_VERSION
+ */
+ default boolean verify(V key, InputStream is, byte[] macOrSignature) {
+ return verify(c -> c.payload(is).key(key).digest(macOrSignature));
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/SecureRequest.java b/api/src/main/java/io/jsonwebtoken/security/SecureRequest.java
index 4e65c3076..52e6a48ad 100644
--- a/api/src/main/java/io/jsonwebtoken/security/SecureRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/SecureRequest.java
@@ -25,4 +25,49 @@
* @since 0.12.0
*/
public interface SecureRequest extends Request, KeySupplier {
+
+ /**
+ * Named parameters (setters) used to configure a {@link SecureRequest SecureRequest} instance.
+ *
+ * @param the type of payload in the request.
+ * @param the type of key used by the algorithm during the request.
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends Request.Params {
+
+ /**
+ * Sets the key used by the algorithm during the request, must be compatible with the target algorithm.
+ *
+ * @param key the algorithm key to use during the request.
+ * @return the instance for method chaining.
+ */
+ M key(K key);
+ }
+
+ /**
+ * A builder for creating {@link SecureRequest}s used to compute a mac or signature via
+ * {@link SecureDigestAlgorithm#digest(Request)}.
+ *
+ * @param the type of payload in the request.
+ * @param the type of key used by the algorithm during the request.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params>, io.jsonwebtoken.lang.Builder> {
+ }
+
+ /**
+ * Returns a new {@link SecureRequest.Builder} for creating {@link SecureRequest}s used to compute a mac or
+ * signature via {@link SecureDigestAlgorithm#digest(Request)}.
+ *
+ * @param the type of payload in the request.
+ * @param the type of key used by the algorithm to compute the digest.
+ * @return a new {@link SecureRequest.Builder} for creating {@link SecureRequest}s used to compute a mac or
+ * signature via {@link SecureDigestAlgorithm#digest(Request)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ @SuppressWarnings("unchecked")
+ static SecureRequest.Builder builder() {
+ return (SecureRequest.Builder) Suppliers.SECURE_REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/Suppliers.java b/api/src/main/java/io/jsonwebtoken/security/Suppliers.java
new file mode 100644
index 000000000..cf145b633
--- /dev/null
+++ b/api/src/main/java/io/jsonwebtoken/security/Suppliers.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2025 jsonwebtoken.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jsonwebtoken.security;
+
+import io.jsonwebtoken.lang.Classes;
+
+import java.io.OutputStream;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Package-private on purpose - this is an internal utility use only.
+ *
+ * @since JJWT_RELEASE_VERSION
+ */
+final class Suppliers {
+
+ private Suppliers() { // for coverage
+ }
+
+ static final Supplier> REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultRequest$Builder$Supplier");
+
+ static final Supplier VERIFY_DIGEST_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultVerifyDigestRequest$Builder$Supplier");
+
+ static final Supplier> SECURE_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultSecureRequest$Builder$Supplier");
+
+ static final Supplier> VERIFY_SECURE_DIGEST_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultVerifySecureDigestRequest$Builder$Supplier");
+
+ static final Supplier> KEY_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultKeyRequest$Builder$Supplier");
+
+ static final Supplier> DECRYPTION_KEY_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultDecryptionKeyRequest$Builder$Supplier");
+
+ static final Supplier AEAD_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultAeadRequest$Builder$Supplier");
+
+ static final Supplier DECRYPT_AEAD_REQUEST_BUILDER =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultDecryptAeadRequest$Builder$Supplier");
+
+ static final Function AEAD_RESULT_FACTORY =
+ Classes.newInstance("io.jsonwebtoken.impl.security.DefaultAeadResult$Factory");
+}
diff --git a/api/src/main/java/io/jsonwebtoken/security/VerifyDigestRequest.java b/api/src/main/java/io/jsonwebtoken/security/VerifyDigestRequest.java
index 34fbf16c7..80df532d3 100644
--- a/api/src/main/java/io/jsonwebtoken/security/VerifyDigestRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/VerifyDigestRequest.java
@@ -30,4 +30,41 @@
* @since 0.12.0
*/
public interface VerifyDigestRequest extends Request, DigestSupplier {
+
+ /**
+ * Named parameters (setters) used to configure a {@link VerifyDigestRequest VerifyDigestRequest} instance.
+ *
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends Request.Params {
+
+ /**
+ * The digest to verify against the one computed for the given {@link #getPayload() payload}.
+ *
+ * @param digest the digest to verify against the one computed for the given {@link #getPayload() payload}.
+ * @return the instance for method chaining.
+ */
+ M digest(byte[] digest);
+ }
+
+ /**
+ * A builder for creating new immutable {@link VerifyDigestRequest} instances.
+ *
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params, io.jsonwebtoken.lang.Builder {
+ }
+
+ /**
+ * Returns a new {@link VerifyDigestRequest.Builder} for creating {@link VerifyDigestRequest}s to verify a
+ * digest via {@link HashAlgorithm#verify(VerifyDigestRequest)}.
+ *
+ * @return a new {@link VerifyDigestRequest.Builder} for creating {@link VerifyDigestRequest}s to verify a
+ * digest via {@link HashAlgorithm#verify(VerifyDigestRequest)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ static VerifyDigestRequest.Builder builder() {
+ return Suppliers.VERIFY_DIGEST_REQUEST_BUILDER.get();
+ }
}
diff --git a/api/src/main/java/io/jsonwebtoken/security/VerifySecureDigestRequest.java b/api/src/main/java/io/jsonwebtoken/security/VerifySecureDigestRequest.java
index a1ddbd571..4ca832990 100644
--- a/api/src/main/java/io/jsonwebtoken/security/VerifySecureDigestRequest.java
+++ b/api/src/main/java/io/jsonwebtoken/security/VerifySecureDigestRequest.java
@@ -31,4 +31,40 @@
* @since 0.12.0
*/
public interface VerifySecureDigestRequest extends SecureRequest, VerifyDigestRequest {
+
+ /**
+ * Named parameters (setters) used to configure a {@link VerifySecureDigestRequest VerifySecureDigestRequest}
+ * instance.
+ *
+ * @param type of key to use to verify the digest.
+ * @param the instance type returned for method chaining.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Params> extends SecureRequest.Params,
+ VerifyDigestRequest.Params {
+ }
+
+ /**
+ * A builder for creating {@link VerifySecureDigestRequest}s used to verify a mac or signature via
+ * {@link SecureDigestAlgorithm#verify(VerifyDigestRequest)}.
+ *
+ * @param type of key used to verify the digest.
+ * @since JJWT_RELEASE_VERSION
+ */
+ interface Builder extends Params>, io.jsonwebtoken.lang.Builder> {
+ }
+
+ /**
+ * Returns a new {@link VerifySecureDigestRequest.Builder} for creating {@link VerifySecureDigestRequest}s used
+ * to verify a mac or signature via {@link SecureDigestAlgorithm#verify(VerifyDigestRequest)}.
+ *
+ * @param type of key used to verify the digest.
+ * @return a new {@link VerifySecureDigestRequest.Builder} for creating {@link VerifySecureDigestRequest}s used
+ * to verify a mac or signature via {@link SecureDigestAlgorithm#verify(VerifyDigestRequest)}.
+ * @since JJWT_RELEASE_VERSION
+ */
+ @SuppressWarnings("unchecked")
+ static VerifySecureDigestRequest.Builder builder() {
+ return (VerifySecureDigestRequest.Builder) Suppliers.VERIFY_SECURE_DIGEST_REQUEST_BUILDER.get();
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java b/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java
index 56706ba30..de01e466c 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java
@@ -32,10 +32,6 @@
import io.jsonwebtoken.impl.lang.Functions;
import io.jsonwebtoken.impl.lang.Parameter;
import io.jsonwebtoken.impl.lang.Services;
-import io.jsonwebtoken.impl.security.DefaultAeadRequest;
-import io.jsonwebtoken.impl.security.DefaultAeadResult;
-import io.jsonwebtoken.impl.security.DefaultKeyRequest;
-import io.jsonwebtoken.impl.security.DefaultSecureRequest;
import io.jsonwebtoken.impl.security.Pbes2HsAkwAlgorithm;
import io.jsonwebtoken.impl.security.ProviderKey;
import io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms;
@@ -601,7 +597,9 @@ private String sign(final Payload payload, final Key key, final Provider provide
byte[] signature;
try {
- SecureRequest request = new DefaultSecureRequest<>(signingInput, provider, secureRandom, key);
+ SecureRequest request =
+ SecureRequest.builder().payload(signingInput).key(key)
+ .provider(provider).random(secureRandom).build();
signature = signFunction.apply(request);
// now that we've calculated the signature, if using the b64 extension, and the payload is
@@ -679,7 +677,10 @@ private String encrypt(final Payload content, final Key key, final Provider keyP
//only expose (mutable) JweHeader functionality to KeyAlgorithm instances, not the full headerBuilder
// (which exposes this JwtBuilder and shouldn't be referenced by KeyAlgorithms):
JweHeader delegate = new DefaultMutableJweHeader(this.headerBuilder);
- KeyRequest keyRequest = new DefaultKeyRequest<>(key, keyProvider, this.secureRandom, delegate, enc);
+ KeyRequest keyRequest = KeyRequest.builder()
+ .provider(keyProvider).random(this.secureRandom)
+ .payload(key).header(delegate).encryptionAlgorithm(enc)
+ .build();
KeyResult keyResult = keyAlgFunction.apply(keyRequest);
Assert.stateNotNull(keyResult, "KeyAlgorithm must return a KeyResult.");
@@ -705,11 +706,12 @@ private String encrypt(final Payload content, final Key key, final Provider keyP
// During encryption, the configured Provider applies to the KeyAlgorithm, not the AeadAlgorithm, mostly
// because all JVMs support the standard AeadAlgorithms (especially with BouncyCastle in the classpath).
- // As such, the provider here is intentionally omitted (null):
+ // As such, the provider here is intentionally omitted when building the AeadRequest:
// TODO: add encProvider(Provider) builder method that applies to this request only?
ByteArrayOutputStream ciphertextOut = new ByteArrayOutputStream(8192);
- AeadRequest req = new DefaultAeadRequest(plaintext, null, secureRandom, cek, aad);
- DefaultAeadResult res = new DefaultAeadResult(ciphertextOut);
+ AeadRequest req = AeadRequest.builder().random(secureRandom) // no .provider call, see message above
+ .payload(plaintext).key(cek).associatedData(aad).build();
+ AeadResult res = AeadResult.with(ciphertextOut);
encrypt(req, res);
byte[] iv = Assert.notEmpty(res.getIv(), "Encryption result must have a non-empty initialization vector.");
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java b/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java
index b646f7d05..7b03ab93c 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java
@@ -46,9 +46,6 @@
import io.jsonwebtoken.impl.io.UncloseableInputStream;
import io.jsonwebtoken.impl.lang.Bytes;
import io.jsonwebtoken.impl.lang.RedactedConfidentialValue;
-import io.jsonwebtoken.impl.security.DefaultDecryptAeadRequest;
-import io.jsonwebtoken.impl.security.DefaultDecryptionKeyRequest;
-import io.jsonwebtoken.impl.security.DefaultVerifySecureDigestRequest;
import io.jsonwebtoken.impl.security.LocatingKeyResolver;
import io.jsonwebtoken.impl.security.ProviderKey;
import io.jsonwebtoken.io.CompressionAlgorithm;
@@ -332,8 +329,9 @@ private byte[] verifySignature(final TokenizedJwt tokenized, final JwsHeader jws
}
try {
- VerifySecureDigestRequest request =
- new DefaultVerifySecureDigestRequest<>(verificationInput, provider, null, key, signature);
+ VerifySecureDigestRequest request = VerifySecureDigestRequest.builder()
+ .key(key).payload(verificationInput).digest(signature)
+ .provider(provider).build();
if (!algorithm.verify(request)) {
String msg = "JWT signature does not match locally computed signature. JWT validity cannot be " +
"asserted and should not be trusted.";
@@ -548,8 +546,9 @@ private byte[] verifySignature(final TokenizedJwt tokenized, final JwsHeader jws
// extract key-specific provider if necessary;
Provider provider = ProviderKey.getProvider(key, this.provider);
key = ProviderKey.getKey(key); // this must be called after ProviderKey.getProvider
- DecryptionKeyRequest request =
- new DefaultDecryptionKeyRequest<>(cekBytes, provider, null, jweHeader, encAlg, key);
+ DecryptionKeyRequest request = DecryptionKeyRequest.builder().provider(provider)
+ .payload(cekBytes).header(jweHeader).encryptionAlgorithm(encAlg)
+ .key(key).build();
final SecretKey cek = keyAlg.getDecryptionKey(request);
if (cek == null) {
String msg = "The '" + keyAlg.getId() + "' JWE key algorithm did not return a decryption key. " +
@@ -563,7 +562,9 @@ private byte[] verifySignature(final TokenizedJwt tokenized, final JwsHeader jws
// TODO: add encProvider(Provider) builder method that applies to this request only?
InputStream ciphertext = payload.toInputStream();
ByteArrayOutputStream plaintext = new ByteArrayOutputStream(8192);
- DecryptAeadRequest dreq = new DefaultDecryptAeadRequest(ciphertext, cek, aad, iv, digest);
+ DecryptAeadRequest dreq = DecryptAeadRequest.builder()
+ .payload(ciphertext).key(cek).associatedData(aad).iv(iv).digest(digest)
+ .build();
encAlg.decrypt(dreq, plaintext);
payload = new Payload(plaintext.toByteArray(), header.getContentType());
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/lang/BiConsumer.java b/impl/src/main/java/io/jsonwebtoken/impl/lang/BiConsumer.java
deleted file mode 100644
index 6ea0f31f0..000000000
--- a/impl/src/main/java/io/jsonwebtoken/impl/lang/BiConsumer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright © 2023 jsonwebtoken.io
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.jsonwebtoken.impl.lang;
-
-public interface BiConsumer {
-
- void accept(T t, U u);
-}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwk.java b/impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwk.java
index 1fc849044..597f4fef6 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwk.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwk.java
@@ -32,7 +32,6 @@
import io.jsonwebtoken.security.Jwks;
import io.jsonwebtoken.security.KeyOperation;
-import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.PrivateKey;
@@ -152,8 +151,10 @@ public JwkThumbprint thumbprint(final HashAlgorithm alg) {
String json = toThumbprintJson();
Assert.hasText(json, "Canonical JWK Thumbprint JSON cannot be null or empty.");
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // https://www.rfc-editor.org/rfc/rfc7638#section-3 #2
- InputStream in = Streams.of(bytes);
- byte[] digest = alg.digest(new DefaultRequest<>(in, this.context.getProvider(), this.context.getRandom()));
+ byte[] digest = alg.digest(r ->
+ r.provider(this.context.getProvider())
+ .random(this.context.getRandom())
+ .payload(Streams.of(bytes)));
return new DefaultJwkThumbprint(digest, alg);
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithm.java
index 2a98a07f8..1aa73dd74 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithm.java
@@ -18,7 +18,6 @@
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.impl.DefaultJweHeader;
import io.jsonwebtoken.impl.lang.Bytes;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.impl.lang.ParameterReadable;
import io.jsonwebtoken.impl.lang.RequiredParameterReader;
import io.jsonwebtoken.io.Encoders;
@@ -55,12 +54,9 @@ public KeyResult getEncryptionKey(final KeyRequest request) throws Se
final byte[] iv = ensureInitializationVector(request);
final AlgorithmParameterSpec ivSpec = getIvSpec(iv);
- byte[] taggedCiphertext = jca(request).withCipher(new CheckedFunction() {
- @Override
- public byte[] apply(Cipher cipher) throws Exception {
- cipher.init(Cipher.WRAP_MODE, kek, ivSpec);
- return cipher.wrap(cek);
- }
+ byte[] taggedCiphertext = jca(request).withCipher(cipher -> {
+ cipher.init(Cipher.WRAP_MODE, kek, ivSpec);
+ return cipher.wrap(cek);
});
int tagByteLength = this.tagBitLength / Byte.SIZE;
@@ -94,14 +90,11 @@ public SecretKey getDecryptionKey(DecryptionKeyRequest request) throw
//for tagged GCM, the JCA spec requires that the tag be appended to the end of the ciphertext byte array:
final byte[] taggedCiphertext = Bytes.concat(cekBytes, tag);
- return jca(request).withCipher(new CheckedFunction() {
- @Override
- public SecretKey apply(Cipher cipher) throws Exception {
- cipher.init(Cipher.UNWRAP_MODE, kek, ivSpec);
- Key key = cipher.unwrap(taggedCiphertext, KEY_ALG_NAME, Cipher.SECRET_KEY);
- Assert.state(key instanceof SecretKey, "cipher.unwrap must produce a SecretKey instance.");
- return (SecretKey) key;
- }
+ return jca(request).withCipher(cipher -> {
+ cipher.init(Cipher.UNWRAP_MODE, kek, ivSpec);
+ Key key = cipher.unwrap(taggedCiphertext, KEY_ALG_NAME, Cipher.SECRET_KEY);
+ Assert.state(key instanceof SecretKey, "cipher.unwrap must produce a SecretKey instance.");
+ return (SecretKey) key;
});
}
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/AesWrapKeyAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/AesWrapKeyAlgorithm.java
index 05b5a65dd..caaf6ae8d 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/AesWrapKeyAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/AesWrapKeyAlgorithm.java
@@ -15,7 +15,6 @@
*/
package io.jsonwebtoken.impl.security;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.security.DecryptionKeyRequest;
import io.jsonwebtoken.security.KeyRequest;
@@ -44,12 +43,9 @@ public KeyResult getEncryptionKey(final KeyRequest request) throws Se
final SecretKey kek = assertKey(request.getPayload());
final SecretKey cek = generateCek(request);
- byte[] ciphertext = jca(request).withCipher(new CheckedFunction() {
- @Override
- public byte[] apply(Cipher cipher) throws Exception {
- cipher.init(Cipher.WRAP_MODE, kek);
- return cipher.wrap(cek);
- }
+ byte[] ciphertext = jca(request).withCipher(cipher -> {
+ cipher.init(Cipher.WRAP_MODE, kek);
+ return cipher.wrap(cek);
});
return new DefaultKeyResult(cek, ciphertext);
@@ -61,14 +57,11 @@ public SecretKey getDecryptionKey(DecryptionKeyRequest request) throw
final SecretKey kek = assertKey(request.getKey());
final byte[] cekBytes = Assert.notEmpty(request.getPayload(), "Request content (encrypted key) cannot be null or empty.");
- return jca(request).withCipher(new CheckedFunction() {
- @Override
- public SecretKey apply(Cipher cipher) throws Exception {
- cipher.init(Cipher.UNWRAP_MODE, kek);
- Key key = cipher.unwrap(cekBytes, KEY_ALG_NAME, Cipher.SECRET_KEY);
- Assert.state(key instanceof SecretKey, "Cipher unwrap must return a SecretKey instance.");
- return (SecretKey) key;
- }
+ return jca(request).withCipher(cipher -> {
+ cipher.init(Cipher.UNWRAP_MODE, kek);
+ Key key = cipher.unwrap(cekBytes, KEY_ALG_NAME, Cipher.SECRET_KEY);
+ Assert.state(key instanceof SecretKey, "Cipher unwrap must return a SecretKey instance.");
+ return (SecretKey) key;
});
}
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadRequest.java
index 592ded197..d8cf399dc 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadRequest.java
@@ -40,7 +40,7 @@ public class DefaultAeadRequest extends DefaultSecureRequest>
+ extends AbstractSecureRequestParams
+ implements AeadRequest.Params {
+
+ protected InputStream aad;
+
+ @Override
+ public M associatedData(InputStream aad) {
+ this.aad = aad;
+ return self();
+ }
+ }
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractAeadRequestParams
+ implements AeadRequest.Builder {
+
+ @Override
+ public AeadRequest build() {
+ return new DefaultAeadRequest(this.payload, this.provider, this.random, this.key, this.aad);
+ }
+
+ public static class Supplier implements java.util.function.Supplier {
+ @Override
+ public AeadRequest.Builder get() {
+ return new Builder();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadResult.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadResult.java
index 96bae63ba..f5819807c 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadResult.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadResult.java
@@ -21,14 +21,16 @@
import io.jsonwebtoken.security.IvSupplier;
import java.io.OutputStream;
+import java.util.function.Function;
+@SuppressWarnings("unused") //used via reflection as io.jsonwebtoken.security.Suppliers.AEAD_RESULT_FACTORY
public class DefaultAeadResult implements AeadResult, DigestSupplier, IvSupplier {
private final OutputStream out;
private byte[] tag;
private byte[] iv;
- public DefaultAeadResult(OutputStream out) {
+ private DefaultAeadResult(OutputStream out) {
this.out = Assert.notNull(out, "OutputStream cannot be null.");
}
@@ -58,4 +60,12 @@ public AeadResult setIv(byte[] iv) {
public byte[] getIv() {
return this.iv;
}
+
+ @SuppressWarnings("unused") // instantiated via reflection as io.jsonwebtoken.security.Suppliers.AEAD_RESULT_FACTORY
+ public static class Factory implements Function {
+ @Override
+ public AeadResult apply(OutputStream out) {
+ return new DefaultAeadResult(out);
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptAeadRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptAeadRequest.java
index f030721e6..a8dd0eed2 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptAeadRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptAeadRequest.java
@@ -38,4 +38,36 @@ public DefaultDecryptAeadRequest(InputStream payload, SecretKey key, InputStream
public byte[] getDigest() {
return this.TAG;
}
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractAeadRequestParams
+ implements DecryptAeadRequest.Builder {
+
+ private byte[] iv;
+ private byte[] tag;
+
+ @Override
+ public DecryptAeadRequest.Builder iv(byte[] iv) {
+ this.iv = iv;
+ return self();
+ }
+
+ @Override
+ public DecryptAeadRequest.Builder digest(byte[] digest) {
+ this.tag = digest;
+ return self();
+ }
+
+ @Override
+ public DecryptAeadRequest build() {
+ return new DefaultDecryptAeadRequest(this.payload, this.key, this.aad, this.iv, this.tag);
+ }
+
+ public static class Supplier implements java.util.function.Supplier {
+ @Override
+ public DecryptAeadRequest.Builder get() {
+ return new Builder();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptionKeyRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptionKeyRequest.java
index fde6cb4d5..bb8c758ca 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptionKeyRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptionKeyRequest.java
@@ -43,4 +43,29 @@ protected void assertBytePayload(byte[] payload) {
public K getKey() {
return this.decryptionKey;
}
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractKeyRequestParams>
+ implements DecryptionKeyRequest.Builder {
+
+ private K decryptionKey;
+
+ @Override
+ public DecryptionKeyRequest.Builder key(K decryptionKey) {
+ this.decryptionKey = decryptionKey;
+ return self();
+ }
+
+ @Override
+ public DecryptionKeyRequest build() {
+ return new DefaultDecryptionKeyRequest<>(this.payload, this.provider, this.random, this.header, this.aeadAlg, this.decryptionKey);
+ }
+
+ public static class Supplier implements java.util.function.Supplier> {
+ @Override
+ public DecryptionKeyRequest.Builder get() {
+ return new Builder<>();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultHashAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultHashAlgorithm.java
index 18e847124..516330afa 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultHashAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultHashAlgorithm.java
@@ -15,13 +15,11 @@
*/
package io.jsonwebtoken.impl.security;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.security.HashAlgorithm;
import io.jsonwebtoken.security.Request;
import io.jsonwebtoken.security.VerifyDigestRequest;
-import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.Locale;
@@ -38,17 +36,14 @@ public final class DefaultHashAlgorithm extends CryptoAlgorithm implements HashA
public byte[] digest(final Request request) {
Assert.notNull(request, "Request cannot be null.");
final InputStream payload = Assert.notNull(request.getPayload(), "Request payload cannot be null.");
- return jca(request).withMessageDigest(new CheckedFunction() {
- @Override
- public byte[] apply(MessageDigest md) throws IOException {
- byte[] buf = new byte[1024];
- int len = 0;
- while (len != -1) {
- len = payload.read(buf);
- if (len > 0) md.update(buf, 0, len);
- }
- return md.digest();
+ return jca(request).withMessageDigest(md -> {
+ byte[] buf = new byte[1024];
+ int len = 0;
+ while (len != -1) {
+ len = payload.read(buf);
+ if (len > 0) md.update(buf, 0, len);
}
+ return md.digest();
});
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyRequest.java
index a3699994f..a54adec7f 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyRequest.java
@@ -20,6 +20,7 @@
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.KeyRequest;
+import java.security.Key;
import java.security.Provider;
import java.security.SecureRandom;
@@ -43,4 +44,40 @@ public JweHeader getHeader() {
public AeadAlgorithm getEncryptionAlgorithm() {
return this.encryptionAlgorithm;
}
+
+ static abstract class AbstractKeyRequestParams>
+ extends AbstractRequestParams implements KeyRequest.Params {
+
+ protected AeadAlgorithm aeadAlg;
+ protected JweHeader header;
+
+ @Override
+ public M encryptionAlgorithm(AeadAlgorithm aeadAlg) {
+ this.aeadAlg = aeadAlg;
+ return self();
+ }
+
+ @Override
+ public M header(JweHeader header) {
+ this.header = header;
+ return self();
+ }
+ }
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractKeyRequestParams>
+ implements KeyRequest.Builder {
+
+ @Override
+ public KeyRequest build() {
+ return new DefaultKeyRequest<>(this.payload, this.provider, this.random, this.header, this.aeadAlg);
+ }
+
+ public static class Supplier implements java.util.function.Supplier> {
+ @Override
+ public KeyRequest.Builder get() {
+ return new Builder<>();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRequest.java
index 4c7c07bb7..74bd694dc 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRequest.java
@@ -25,7 +25,7 @@ public class DefaultRequest extends DefaultMessage implements Request {
private final Provider provider;
private final SecureRandom secureRandom;
- public DefaultRequest(T payload, Provider provider, SecureRandom secureRandom) {
+ DefaultRequest(T payload, Provider provider, SecureRandom secureRandom) {
super(payload);
this.provider = provider;
this.secureRandom = secureRandom;
@@ -40,4 +40,51 @@ public Provider getProvider() {
public SecureRandom getSecureRandom() {
return this.secureRandom;
}
+
+ static abstract class AbstractRequestParams>
+ implements Params {
+
+ protected Provider provider;
+ protected SecureRandom random;
+ protected T payload;
+
+ @SuppressWarnings("unchecked")
+ protected final M self() {
+ return (M) this;
+ }
+
+ @Override
+ public M payload(T payload) {
+ this.payload = payload;
+ return self();
+ }
+
+ @Override
+ public M provider(Provider provider) {
+ this.provider = provider;
+ return self();
+ }
+
+ @Override
+ public M random(SecureRandom random) {
+ this.random = random;
+ return self();
+ }
+ }
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractRequestParams> implements Request.Builder {
+
+ @Override
+ public Request build() {
+ return new DefaultRequest<>(this.payload, this.provider, this.random);
+ }
+
+ public static class Supplier implements java.util.function.Supplier> {
+ @Override
+ public Builder get() {
+ return new Builder<>();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithm.java
index 7a09b8141..52741e9b8 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithm.java
@@ -15,7 +15,6 @@
*/
package io.jsonwebtoken.impl.security;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.security.DecryptionKeyRequest;
import io.jsonwebtoken.security.InvalidKeyException;
@@ -88,16 +87,13 @@ public KeyResult getEncryptionKey(final KeyRequest request) throws Se
validate(kek, true);
final SecretKey cek = generateCek(request);
- byte[] ciphertext = jca(request).withCipher(new CheckedFunction() {
- @Override
- public byte[] apply(Cipher cipher) throws Exception {
- if (SPEC == null) {
- cipher.init(Cipher.WRAP_MODE, kek, ensureSecureRandom(request));
- } else {
- cipher.init(Cipher.WRAP_MODE, kek, SPEC, ensureSecureRandom(request));
- }
- return cipher.wrap(cek);
+ byte[] ciphertext = jca(request).withCipher(cipher -> {
+ if (SPEC == null) {
+ cipher.init(Cipher.WRAP_MODE, kek, ensureSecureRandom(request));
+ } else {
+ cipher.init(Cipher.WRAP_MODE, kek, SPEC, ensureSecureRandom(request));
}
+ return cipher.wrap(cek);
});
return new DefaultKeyResult(cek, ciphertext);
@@ -110,17 +106,14 @@ public SecretKey getDecryptionKey(DecryptionKeyRequest request) thro
validate(kek, false);
final byte[] cekBytes = Assert.notEmpty(request.getPayload(), "Request content (encrypted key) cannot be null or empty.");
- return jca(request).withCipher(new CheckedFunction() {
- @Override
- public SecretKey apply(Cipher cipher) throws Exception {
- if (SPEC == null) {
- cipher.init(Cipher.UNWRAP_MODE, kek);
- } else {
- cipher.init(Cipher.UNWRAP_MODE, kek, SPEC);
- }
- Key key = cipher.unwrap(cekBytes, AesAlgorithm.KEY_ALG_NAME, Cipher.SECRET_KEY);
- return Assert.isInstanceOf(SecretKey.class, key, "Cipher unwrap must return a SecretKey instance.");
+ return jca(request).withCipher(cipher -> {
+ if (SPEC == null) {
+ cipher.init(Cipher.UNWRAP_MODE, kek);
+ } else {
+ cipher.init(Cipher.UNWRAP_MODE, kek, SPEC);
}
+ Key key = cipher.unwrap(cekBytes, AesAlgorithm.KEY_ALG_NAME, Cipher.SECRET_KEY);
+ return Assert.isInstanceOf(SecretKey.class, key, "Cipher unwrap must return a SecretKey instance.");
});
}
}
\ No newline at end of file
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecureRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecureRequest.java
index 776dc8ade..405a8529b 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecureRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecureRequest.java
@@ -35,4 +35,33 @@ public DefaultSecureRequest(T payload, Provider provider, SecureRandom secureRan
public K getKey() {
return this.KEY;
}
+
+ static abstract class AbstractSecureRequestParams>
+ extends AbstractRequestParams implements SecureRequest.Params {
+
+ protected K key;
+
+ @Override
+ public M key(K key) {
+ this.key = key;
+ return self();
+ }
+ }
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractSecureRequestParams>
+ implements SecureRequest.Builder {
+
+ @Override
+ public SecureRequest build() {
+ return new DefaultSecureRequest<>(this.payload, this.provider, this.random, this.key);
+ }
+
+ public static class Supplier implements java.util.function.Supplier> {
+ @Override
+ public Builder get() {
+ return new Builder<>();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifyDigestRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifyDigestRequest.java
index 1d89bbcdb..1fbddc7ea 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifyDigestRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifyDigestRequest.java
@@ -22,6 +22,7 @@
import java.security.Provider;
import java.security.SecureRandom;
+@SuppressWarnings("unused")
public class DefaultVerifyDigestRequest extends DefaultRequest implements VerifyDigestRequest {
private final byte[] digest;
@@ -35,4 +36,29 @@ public DefaultVerifyDigestRequest(InputStream payload, Provider provider, Secure
public byte[] getDigest() {
return this.digest;
}
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractRequestParams
+ implements VerifyDigestRequest.Builder {
+
+ private byte[] digest;
+
+ @Override
+ public VerifyDigestRequest.Builder digest(byte[] digest) {
+ this.digest = digest;
+ return self();
+ }
+
+ @Override
+ public VerifyDigestRequest build() {
+ return new DefaultVerifyDigestRequest(this.payload, this.provider, this.random, this.digest);
+ }
+
+ public static class Supplier implements java.util.function.Supplier {
+ @Override
+ public Builder get() {
+ return new Builder();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifySecureDigestRequest.java b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifySecureDigestRequest.java
index 7be699174..31394fa45 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifySecureDigestRequest.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifySecureDigestRequest.java
@@ -36,4 +36,29 @@ public DefaultVerifySecureDigestRequest(InputStream payload, Provider provider,
public byte[] getDigest() {
return this.digest;
}
+
+ @SuppressWarnings("unused") // instantiated via reflection in io.jsonwebtoken.security.Suppliers
+ public static class Builder extends AbstractSecureRequestParams>
+ implements VerifySecureDigestRequest.Builder {
+
+ private byte[] digest;
+
+ @Override
+ public VerifySecureDigestRequest.Builder digest(byte[] digest) {
+ this.digest = digest;
+ return self();
+ }
+
+ @Override
+ public VerifySecureDigestRequest build() {
+ return new DefaultVerifySecureDigestRequest<>(this.payload, this.provider, this.random, this.key, this.digest);
+ }
+
+ public static class Supplier implements java.util.function.Supplier> {
+ @Override
+ public VerifySecureDigestRequest.Builder get() {
+ return new Builder<>();
+ }
+ }
+ }
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java
index 114cccd4e..917ffafef 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java
@@ -18,7 +18,6 @@
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.impl.DefaultJweHeader;
import io.jsonwebtoken.impl.lang.Bytes;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.impl.lang.ParameterReadable;
import io.jsonwebtoken.impl.lang.RequiredParameterReader;
import io.jsonwebtoken.lang.Arrays;
@@ -40,7 +39,6 @@
import io.jsonwebtoken.security.SecureRequest;
import io.jsonwebtoken.security.SecurityException;
-import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.Key;
@@ -90,13 +88,10 @@ protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom r
}
protected byte[] generateZ(final KeyRequest> request, final PublicKey pub, final PrivateKey priv) {
- return jca(request).withKeyAgreement(new CheckedFunction() {
- @Override
- public byte[] apply(KeyAgreement keyAgreement) throws Exception {
- keyAgreement.init(KeysBridge.root(priv), ensureSecureRandom(request));
- keyAgreement.doPhase(pub, true);
- return keyAgreement.generateSecret();
- }
+ return jca(request).withKeyAgreement(keyAgreement -> {
+ keyAgreement.init(KeysBridge.root(priv), ensureSecureRandom(request));
+ keyAgreement.doPhase(pub, true);
+ return keyAgreement.generateSecret();
});
}
@@ -194,8 +189,10 @@ public KeyResult getEncryptionKey(KeyRequest request) throws Security
final SecretKey derived = deriveKey(request, publicKey, pair.getPrivate());
- KeyRequest wrapReq = new DefaultKeyRequest<>(derived, request.getProvider(),
- request.getSecureRandom(), request.getHeader(), request.getEncryptionAlgorithm());
+ KeyRequest wrapReq = KeyRequest.builder()
+ .provider(request.getProvider()).random(request.getSecureRandom())
+ .payload(derived).header(request.getHeader()).encryptionAlgorithm(request.getEncryptionAlgorithm())
+ .build();
KeyResult result = WRAP_ALG.getEncryptionKey(wrapReq);
header.put(DefaultJweHeader.EPK.getId(), jwk);
@@ -228,8 +225,11 @@ public SecretKey getDecryptionKey(DecryptionKeyRequest request) thro
final SecretKey derived = deriveKey(request, epk.toKey(), privateKey);
- DecryptionKeyRequest unwrapReq = new DefaultDecryptionKeyRequest<>(request.getPayload(),
- null, request.getSecureRandom(), header, request.getEncryptionAlgorithm(), derived);
+ DecryptionKeyRequest unwrapReq = DecryptionKeyRequest.builder()
+ .random(request.getSecureRandom())
+ .payload(request.getPayload()).key(derived)
+ .header(header).encryptionAlgorithm(request.getEncryptionAlgorithm())
+ .build();
return WRAP_ALG.getDecryptionKey(unwrapReq);
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithm.java
index 970785f66..b9880a18e 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithm.java
@@ -150,11 +150,8 @@ private byte[] sign(byte[] aad, byte[] iv, InputStream ciphertext, byte[] macKey
streams.add(ciphertext);
streams.add(Streams.of(AL));
InputStream in = new SequenceInputStream(Collections.enumeration(streams));
-
SecretKey key = new SecretKeySpec(macKeyBytes, SIGALG.getJcaName());
- SecureRequest request =
- new DefaultSecureRequest<>(in, null, null, key);
- byte[] digest = SIGALG.digest(request);
+ byte[] digest = SIGALG.digest(key, in);
// https://tools.ietf.org/html/rfc7518#section-5.2.2.1 #5 requires truncating the signature
// to be the same length as the macKey/encKey:
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java b/impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java
index 796c78c8e..302968cfd 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java
@@ -19,7 +19,6 @@
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.DefaultJweHeader;
import io.jsonwebtoken.impl.lang.Bytes;
-import io.jsonwebtoken.impl.lang.CheckedFunction;
import io.jsonwebtoken.impl.lang.Parameter;
import io.jsonwebtoken.impl.lang.ParameterReadable;
import io.jsonwebtoken.impl.lang.RequiredParameterReader;
@@ -133,12 +132,7 @@ protected SecretKey deriveKey(SecretKeyFactory factory, final char[] password, f
private SecretKey deriveKey(final KeyRequest> request, final char[] password, final byte[] salt, final int iterations) {
try {
Assert.notEmpty(password, "Key password character array cannot be null or empty.");
- return jca(request).withSecretKeyFactory(new CheckedFunction() {
- @Override
- public SecretKey apply(SecretKeyFactory factory) throws Exception {
- return deriveKey(factory, password, salt, iterations);
- }
- });
+ return jca(request).withSecretKeyFactory(factory -> deriveKey(factory, password, salt, iterations));
} finally {
java.util.Arrays.fill(password, '\u0000');
}
@@ -173,8 +167,10 @@ public KeyResult getEncryptionKey(final KeyRequest request) throws Sec
final SecretKey derivedKek = deriveKey(request, password, rfcSalt, iterations);
// now get a new CEK that is encrypted ('wrapped') with the PBE-derived key:
- KeyRequest wrapReq = new DefaultKeyRequest<>(derivedKek, request.getProvider(),
- request.getSecureRandom(), request.getHeader(), request.getEncryptionAlgorithm());
+ KeyRequest wrapReq = KeyRequest.builder()
+ .provider(request.getProvider()).random(request.getSecureRandom())
+ .payload(derivedKek).header(request.getHeader()).encryptionAlgorithm(request.getEncryptionAlgorithm())
+ .build();
KeyResult result = wrapAlg.getEncryptionKey(wrapReq);
request.getHeader().put(DefaultJweHeader.P2S.getId(), inputSalt); //retain for recipients
@@ -203,9 +199,11 @@ public SecretKey getDecryptionKey(DecryptionKeyRequest request) throws
final char[] password = key.toCharArray(); // password will be safely cleaned/zeroed in deriveKey next:
final SecretKey derivedKek = deriveKey(request, password, rfcSalt, iterations);
- DecryptionKeyRequest unwrapReq =
- new DefaultDecryptionKeyRequest<>(request.getPayload(), request.getProvider(),
- request.getSecureRandom(), header, request.getEncryptionAlgorithm(), derivedKek);
+ DecryptionKeyRequest unwrapReq = DecryptionKeyRequest.builder()
+ .provider(request.getProvider()).random(request.getSecureRandom())
+ .key(derivedKek).payload(request.getPayload())
+ .header(header).encryptionAlgorithm(request.getEncryptionAlgorithm())
+ .build();
return wrapAlg.getDecryptionKey(unwrapReq);
}
diff --git a/impl/src/main/java/io/jsonwebtoken/impl/security/X509BuilderSupport.java b/impl/src/main/java/io/jsonwebtoken/impl/security/X509BuilderSupport.java
index 4ab1a821c..ffa8283ab 100644
--- a/impl/src/main/java/io/jsonwebtoken/impl/security/X509BuilderSupport.java
+++ b/impl/src/main/java/io/jsonwebtoken/impl/security/X509BuilderSupport.java
@@ -23,7 +23,6 @@
import io.jsonwebtoken.lang.Objects;
import io.jsonwebtoken.security.HashAlgorithm;
import io.jsonwebtoken.security.Jwks;
-import io.jsonwebtoken.security.Request;
import io.jsonwebtoken.security.X509Builder;
import java.io.InputStream;
@@ -95,8 +94,7 @@ public X509BuilderSupport x509Sha256Thumbprint(boolean enable) {
private byte[] computeThumbprint(final X509Certificate cert, HashAlgorithm alg) {
byte[] encoded = GET_X509_BYTES.apply(cert);
InputStream in = Streams.of(encoded);
- Request request = new DefaultRequest<>(in, null, null);
- return alg.digest(request);
+ return alg.digest(in);
}
public void apply() {
diff --git a/impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy
index c232a1ade..e0ee721e3 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy
@@ -141,8 +141,7 @@ class JwtsTest {
def c = base64Url('{"sub":"joe","exp":"-42-"}')
def data = Strings.utf8(("$h.$c" as String))
def payload = Streams.of(data)
- def request = new DefaultSecureRequest<>(payload, null, null, key)
- def result = Jwts.SIG.HS256.digest(request)
+ def result = Jwts.SIG.HS256.digest(key, payload)
def sig = Encoders.BASE64URL.encode(result)
def compact = "$h.$c.$sig" as String
try {
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest.groovy
index 049f56447..2ebedf584 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest.groovy
@@ -22,7 +22,6 @@ import io.jsonwebtoken.ProtectedHeader
import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.impl.security.DefaultHashAlgorithm
-import io.jsonwebtoken.impl.security.DefaultRequest
import io.jsonwebtoken.impl.security.TestKeys
import io.jsonwebtoken.io.Encoders
import io.jsonwebtoken.lang.Collections
@@ -362,8 +361,7 @@ class DefaultJwtHeaderBuilderTest {
@Test
void testX509CertificateSha1Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5t = DefaultHashAlgorithm.SHA1.digest(request)
+ def x5t = DefaultHashAlgorithm.SHA1.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5t)
header = jws().x509Sha1Thumbprint(x5t).build() as JwsHeader
@@ -375,8 +373,7 @@ class DefaultJwtHeaderBuilderTest {
void testX509CertificateSha1ThumbprintEnabled() {
def chain = TestKeys.RS256.chain
def payload = Streams.of(chain[0].getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5t = DefaultHashAlgorithm.SHA1.digest(request)
+ def x5t = DefaultHashAlgorithm.SHA1.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5t)
header = jws().x509Chain(chain).x509Sha1Thumbprint(true).build() as JwsHeader
assertArrayEquals x5t, header.getX509Sha1Thumbprint()
@@ -390,8 +387,7 @@ class DefaultJwtHeaderBuilderTest {
@Test
void testX509CertificateSha256Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5tS256 = Jwks.HASH.@SHA256.digest(request)
+ def x5tS256 = Jwks.HASH.@SHA256.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5tS256)
header = jws().x509Sha256Thumbprint(x5tS256).build() as JwsHeader
assertArrayEquals x5tS256, header.getX509Sha256Thumbprint()
@@ -402,8 +398,7 @@ class DefaultJwtHeaderBuilderTest {
void testX509CertificateSha256ThumbprintEnabled() {
def chain = TestKeys.RS256.chain
def payload = Streams.of(chain[0].getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5tS256 = Jwks.HASH.SHA256.digest(request)
+ def x5tS256 = Jwks.HASH.SHA256.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5tS256)
header = jws().x509Chain(chain).x509Sha256Thumbprint(true).build() as JwsHeader
assertArrayEquals x5tS256, header.getX509Sha256Thumbprint()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultMutableJweHeaderTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultMutableJweHeaderTest.groovy
index 76b647341..4ec9899a3 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultMutableJweHeaderTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/DefaultMutableJweHeaderTest.groovy
@@ -19,7 +19,6 @@ import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.impl.security.DefaultHashAlgorithm
-import io.jsonwebtoken.impl.security.DefaultRequest
import io.jsonwebtoken.impl.security.TestKeys
import io.jsonwebtoken.io.Encoders
import io.jsonwebtoken.lang.Strings
@@ -271,8 +270,7 @@ class DefaultMutableJweHeaderTest {
@Test
void testX509Sha1Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5t = DefaultHashAlgorithm.SHA1.digest(request)
+ def x5t = DefaultHashAlgorithm.SHA1.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5t)
header.x509Sha1Thumbprint(x5t)
@@ -287,8 +285,7 @@ class DefaultMutableJweHeaderTest {
@Test
void testX509Sha256Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- def request = new DefaultRequest(payload, null, null)
- def x5tS256 = Jwks.HASH.@SHA256.digest(request)
+ def x5tS256 = Jwks.HASH.@SHA256.digest(payload)
String encoded = Encoders.BASE64URL.encode(x5tS256)
header.x509Sha256Thumbprint(x5tS256)
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilderTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilderTest.groovy
index 514c9ccd7..0e3357fb0 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilderTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilderTest.groovy
@@ -17,7 +17,10 @@ package io.jsonwebtoken.impl.security
import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.io.Encoders
-import io.jsonwebtoken.security.*
+import io.jsonwebtoken.security.EcPrivateJwk
+import io.jsonwebtoken.security.EcPublicJwk
+import io.jsonwebtoken.security.Jwks
+import io.jsonwebtoken.security.RsaPublicJwkBuilder
import org.junit.Test
import java.security.cert.X509Certificate
@@ -66,8 +69,7 @@ class AbstractAsymmetricJwkBuilderTest {
@Test
void testX509CertificateSha1Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- Request request = new DefaultRequest(payload, null, null)
- def x5t = DefaultHashAlgorithm.SHA1.digest(request)
+ def x5t = DefaultHashAlgorithm.SHA1.digest(payload)
def encoded = Encoders.BASE64URL.encode(x5t)
def jwk = builder().x509Sha1Thumbprint(x5t).build()
assertArrayEquals x5t, jwk.getX509Sha1Thumbprint()
@@ -77,8 +79,7 @@ class AbstractAsymmetricJwkBuilderTest {
@Test
void testX509CertificateSha1ThumbprintEnabled() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- Request request = new DefaultRequest(payload, null, null)
- def x5t = DefaultHashAlgorithm.SHA1.digest(request)
+ def x5t = DefaultHashAlgorithm.SHA1.digest(payload)
def encoded = Encoders.BASE64URL.encode(x5t)
def jwk = builder().x509Chain(CHAIN).x509Sha1Thumbprint(true).build()
assertArrayEquals x5t, jwk.getX509Sha1Thumbprint()
@@ -88,8 +89,7 @@ class AbstractAsymmetricJwkBuilderTest {
@Test
void testX509CertificateSha256Thumbprint() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- Request request = new DefaultRequest(payload, null, null)
- def x5tS256 = Jwks.HASH.SHA256.digest(request)
+ def x5tS256 = Jwks.HASH.SHA256.digest(payload)
def encoded = Encoders.BASE64URL.encode(x5tS256)
def jwk = builder().x509Sha256Thumbprint(x5tS256).build()
assertArrayEquals x5tS256, jwk.getX509Sha256Thumbprint()
@@ -99,8 +99,7 @@ class AbstractAsymmetricJwkBuilderTest {
@Test
void testX509CertificateSha256ThumbprintEnabled() {
def payload = Streams.of(TestKeys.RS256.cert.getEncoded())
- Request request = new DefaultRequest(payload, null, null)
- def x5tS256 = Jwks.HASH.SHA256.digest(request)
+ def x5tS256 = Jwks.HASH.SHA256.digest(payload)
def encoded = Encoders.BASE64URL.encode(x5tS256)
def jwk = builder().x509Chain(CHAIN).x509Sha256Thumbprint(true).build()
assertArrayEquals x5tS256, jwk.getX509Sha256Thumbprint()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithmTest.groovy
index 166762c4d..42e86f38f 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithmTest.groovy
@@ -39,7 +39,7 @@ class AbstractSecureDigestAlgorithmTest {
def pair = Jwts.SIG.RS256.keyPair().build()
byte[] data = Strings.utf8('foo')
def payload = Streams.of(data)
- byte[] signature = Jwts.SIG.RS256.digest(new DefaultSecureRequest<>(payload, provider, null, pair.getPrivate()))
+ byte[] signature = Jwts.SIG.RS256.digest( r -> r.provider(provider).payload(payload).key(pair.getPrivate()))
payload.reset()
assertTrue Jwts.SIG.RS256.verify(new DefaultVerifySecureDigestRequest(payload, provider, null, pair.getPublic(), signature))
}
@@ -56,7 +56,7 @@ class AbstractSecureDigestAlgorithmTest {
}
try {
def payload = Streams.of(Strings.utf8('foo'))
- alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
+ alg.digest(pair.getPrivate(), payload)
} catch (SignatureException e) {
assertTrue e.getMessage().startsWith('Unable to compute test signature with JCA algorithm \'test\' using key {')
assertTrue e.getMessage().endsWith('}: foo')
@@ -77,9 +77,9 @@ class AbstractSecureDigestAlgorithmTest {
def data = Strings.utf8('foo')
def payload = Streams.of(data)
try {
- byte[] signature = alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))
+ byte[] signature = alg.digest(pair.getPrivate(), payload)
payload.reset()
- alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, pair.getPublic(), signature))
+ alg.verify(pair.getPublic(), payload, signature)
} catch (SignatureException e) {
assertTrue e.getMessage().startsWith('Unable to verify test signature with JCA algorithm \'test\' using key {')
assertTrue e.getMessage().endsWith('}: foo')
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesAlgorithmTest.groovy
index ed4a6633a..8123b6844 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesAlgorithmTest.groovy
@@ -43,8 +43,6 @@ class AesAlgorithmTest {
SecretKey key = TestKeys.A128GCM //weaker than required
- Request request = new DefaultSecureRequest(new byte[1], null, null, key)
-
try {
alg.assertKey(key)
fail()
@@ -113,7 +111,8 @@ class AesAlgorithmTest {
def ins = Streams.of('data')
def key = TestKeys.A256GCM
def aad = Strings.utf8('aad')
- def req = new DefaultAeadRequest(ins, null, secureRandom, key, Streams.of(aad))
+ def req = AeadRequest.builder().payload(ins).random(secureRandom).key(key)
+ .associatedData(Streams.of(aad)).build()
def returnedSecureRandom = alg.ensureSecureRandom(req)
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithmTest.groovy
index 54f444b1a..ebd626dc2 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithmTest.groovy
@@ -23,11 +23,13 @@ import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.impl.lang.CheckedFunction
import io.jsonwebtoken.lang.Arrays
+import io.jsonwebtoken.security.AeadResult
import io.jsonwebtoken.security.Keys
import io.jsonwebtoken.security.SecretKeyBuilder
import org.junit.Test
import javax.crypto.Cipher
+import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
import static org.junit.Assert.*
@@ -71,7 +73,7 @@ class AesGcmKeyAlgorithmTest {
def out = new ByteArrayOutputStream(8192)
def encRequest = new DefaultAeadRequest(Streams.of(cek.getEncoded()), null, null, kek, null, iv)
- def encResult = new DefaultAeadResult(out)
+ def encResult = AeadResult.with(out)
Jwts.ENC.A256GCM.encrypt(encRequest, encResult)
assertArrayEquals tag, encResult.digest
@@ -87,7 +89,7 @@ class AesGcmKeyAlgorithmTest {
def template = new JcaTemplate('AES')
def header = Jwts.header().add('alg', alg.id).add('enc', 'foo')
- def kek = template.generateSecretKey(keyLength)
+ SecretKey kek = template.generateSecretKey(keyLength)
def cek = template.generateSecretKey(keyLength)
def enc = new GcmAesAeadAlgorithm(keyLength) {
@Override
@@ -97,9 +99,7 @@ class AesGcmKeyAlgorithmTest {
}
def delegate = new DefaultMutableJweHeader(header)
- def ereq = new DefaultKeyRequest(kek, null, null, delegate, enc)
-
- def result = alg.getEncryptionKey(ereq)
+ def result = alg.getEncryptionKey(kek, delegate, enc)
byte[] encryptedKeyBytes = result.getPayload()
assertFalse "encryptedKey must be populated", Arrays.length(encryptedKeyBytes) == 0
@@ -135,8 +135,7 @@ class AesGcmKeyAlgorithmTest {
}
}
def delegate = new DefaultMutableJweHeader(headerBuilder)
- def ereq = new DefaultKeyRequest(kek, null, null, delegate, enc)
- def result = alg.getEncryptionKey(ereq)
+ def result = alg.getEncryptionKey(kek, delegate, enc)
headerBuilder.remove(headerName)
@@ -147,7 +146,7 @@ class AesGcmKeyAlgorithmTest {
def header = headerBuilder.build() as JweHeader
try {
- alg.getDecryptionKey(new DefaultDecryptionKeyRequest(encryptedKeyBytes, null, null, header, enc, kek))
+ alg.getDecryptionKey(encryptedKeyBytes, kek, header, enc)
fail()
} catch (MalformedJwtException iae) {
assertEquals exmsg, iae.getMessage()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultHashAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultHashAlgorithmTest.groovy
index d9e1435b3..97d967a98 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultHashAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultHashAlgorithmTest.groovy
@@ -32,9 +32,9 @@ class DefaultHashAlgorithmTest {
byte[] data = Strings.utf8('Hello World')
InputStream payload = Streams.of(data)
for (HashAlgorithm alg : algs) {
- byte[] hash = alg.digest(new DefaultRequest<>(payload, null, null))
+ byte[] hash = alg.digest(payload)
payload.reset()
- assertTrue alg.verify(new DefaultVerifyDigestRequest(payload, null, null, hash))
+ assertTrue alg.verify(payload, hash)
payload.reset()
}
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkThumbprintTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkThumbprintTest.groovy
index a6af05791..a7349163d 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkThumbprintTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkThumbprintTest.groovy
@@ -31,7 +31,7 @@ class DefaultJwkThumbprintTest {
private static String content = "Hello World"
private static HashAlgorithm alg = Jwks.HASH.SHA256
- private static byte[] digest = alg.digest(new DefaultRequest(Streams.of(content), null, null))
+ private static byte[] digest = alg.digest(Streams.of(content))
private static String expectedToString = Encoders.BASE64URL.encode(digest)
private static String expectedUriString = DefaultJwkThumbprint.URI_PREFIX + alg.getId() + ":" + expectedToString
private static URI expectedUri = URI.create(expectedUriString)
@@ -83,7 +83,7 @@ class DefaultJwkThumbprintTest {
// same alg, different digest:
def payload = Streams.of(Strings.utf8('Hello World!'))
- byte[] digest2 = alg.digest(new DefaultRequest<>(payload, null, null))
+ byte[] digest2 = alg.digest(payload)
assertFalse thumbprint == new DefaultJwkThumbprint(digest2, DefaultHashAlgorithm.SHA1)
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DirectKeyAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DirectKeyAlgorithmTest.groovy
index 82a2c08a1..d6e8c45f7 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/DirectKeyAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/DirectKeyAlgorithmTest.groovy
@@ -19,6 +19,7 @@ import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.DefaultJweHeader
import io.jsonwebtoken.lang.Arrays
import io.jsonwebtoken.security.DecryptionKeyRequest
+import io.jsonwebtoken.security.KeyRequest
import org.junit.Test
import javax.crypto.spec.SecretKeySpec
@@ -39,15 +40,15 @@ class DirectKeyAlgorithmTest {
void testGetEncryptionKey() {
def alg = new DirectKeyAlgorithm()
def key = new SecretKeySpec(new byte[1], "AES")
- def request = new DefaultKeyRequest(key, null, null, new DefaultJweHeader([:]), Jwts.ENC.A128GCM)
- def result = alg.getEncryptionKey(request)
+ def header = new DefaultJweHeader([:])
+ def result = alg.getEncryptionKey(key, header, Jwts.ENC.A128GCM)
assertSame key, result.getKey()
assertEquals 0, Arrays.length(result.getPayload()) //must not have an encrypted key
}
@Test(expected = IllegalArgumentException)
void testGetEncryptionKeyWithNullRequest() {
- new DirectKeyAlgorithm().getEncryptionKey(null)
+ new DirectKeyAlgorithm().getEncryptionKey(null as KeyRequest)
}
@Test(expected = IllegalArgumentException)
@@ -76,7 +77,7 @@ class DirectKeyAlgorithmTest {
@Test(expected = IllegalArgumentException)
void testGetDecryptionKeyWithNullRequest() {
- new DirectKeyAlgorithm().getDecryptionKey(null)
+ new DirectKeyAlgorithm().getDecryptionKey(null as DecryptionKeyRequest)
}
@Test(expected = IllegalArgumentException)
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcSignatureAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcSignatureAlgorithmTest.groovy
index a4993bc27..9fabb7f07 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcSignatureAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcSignatureAlgorithmTest.groovy
@@ -23,6 +23,7 @@ import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.io.Decoders
import io.jsonwebtoken.lang.Strings
import io.jsonwebtoken.security.InvalidKeyException
+import io.jsonwebtoken.security.SecureRequest
import io.jsonwebtoken.security.SignatureException
import org.junit.Test
@@ -120,7 +121,7 @@ class EcSignatureAlgorithmTest {
@Test
void testSignWithPublicKey() {
ECPublicKey key = TestKeys.ES256.pair.public as ECPublicKey
- def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, key)
+ def request = SecureRequest.builder().payload(Streams.of(new byte[1])).key(key).build()
def alg = Jwts.SIG.ES256
try {
alg.digest(request)
@@ -137,9 +138,8 @@ class EcSignatureAlgorithmTest {
BigInteger order = BigInteger.ONE
ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new TestECField(), BigInteger.ONE, BigInteger.ONE), new ECPoint(BigInteger.ONE, BigInteger.ONE), order, 1)
ECPrivateKey priv = new TestECPrivateKey(algorithm: 'EC', params: spec)
- def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, priv)
try {
- it.digest(request)
+ it.digest(priv, Streams.of(new byte[1]))
} catch (InvalidKeyException expected) {
String msg = "The provided Elliptic Curve signing key size (aka order bit length) is " +
"${Bytes.bitsMsg(order.bitLength())}, but the '${it.getId()}' algorithm requires EC Keys with " +
@@ -154,9 +154,8 @@ class EcSignatureAlgorithmTest {
void testSignWithInvalidKeyFieldLength() {
def keypair = Jwts.SIG.ES256.keyPair().build()
def data = "foo".getBytes(StandardCharsets.UTF_8)
- def req = new DefaultSecureRequest(Streams.of(data), null, null, keypair.private)
try {
- Jwts.SIG.ES384.digest(req)
+ Jwts.SIG.ES384.digest(keypair.private, Streams.of(data))
} catch (InvalidKeyException expected) {
String msg = "The provided Elliptic Curve signing key size (aka order bit length) is " +
"256 bits (32 bytes), but the 'ES384' algorithm requires EC Keys with " +
@@ -174,8 +173,7 @@ class EcSignatureAlgorithmTest {
payload.reset()
def pair = it.keyPair().build()
def key = pair.getPrivate()
- def signRequest = new DefaultSecureRequest(payload, null, null, key)
- byte[] signature = it.digest(signRequest)
+ byte[] signature = it.digest(key, payload)
payload.reset()
def verifyRequest = new DefaultVerifySecureDigestRequest(payload, null, null, key, signature)
try {
@@ -330,7 +328,7 @@ class EcSignatureAlgorithmTest {
assertTrue keypair.getPrivate() instanceof ECPrivateKey
def data = Strings.ascii(withoutSignature)
def payload = Streams.of(data)
- def signature = alg.digest(new DefaultSecureRequest<>(payload, null, null, keypair.private))
+ def signature = alg.digest(keypair.private, payload)
payload.reset()
assertTrue alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signature))
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcdhKeyAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcdhKeyAlgorithmTest.groovy
index 00907b426..2cf897ee5 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcdhKeyAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EcdhKeyAlgorithmTest.groovy
@@ -23,6 +23,7 @@ import io.jsonwebtoken.impl.DefaultMutableJweHeader
import io.jsonwebtoken.security.DecryptionKeyRequest
import io.jsonwebtoken.security.InvalidKeyException
import io.jsonwebtoken.security.Jwks
+import io.jsonwebtoken.security.KeyRequest
import org.junit.Test
import java.security.PrivateKey
@@ -49,8 +50,9 @@ class EcdhKeyAlgorithmTest {
PublicKey encKey = TestKeys.X25519.pair.public as PublicKey
def header = new DefaultMutableJweHeader(Jwts.header())
def provider = TestKeys.BC
- def request = new DefaultKeyRequest(encKey, provider, null, header, Jwts.ENC.A128GCM)
- def result = alg.getEncryptionKey(request)
+ def req = KeyRequest.builder().payload(encKey).provider(provider).header(header)
+ .encryptionAlgorithm(Jwts.ENC.A128GCM).build()
+ def result = alg.getEncryptionKey(req)
assertNotNull result.getKey()
}
@@ -124,9 +126,8 @@ class EcdhKeyAlgorithmTest {
def alg = new EcdhKeyAlgorithm()
PublicKey encKey = TestKeys.RS256.pair.public as PublicKey // not an elliptic curve key, must fail
def header = new DefaultMutableJweHeader(Jwts.header())
- def request = new DefaultKeyRequest(encKey, null, null, header, Jwts.ENC.A128GCM)
try {
- alg.getEncryptionKey(request)
+ alg.getEncryptionKey(encKey, header, Jwts.ENC.A128GCM)
fail()
} catch (InvalidKeyException expected) {
String msg = "Unable to determine JWA-standard Elliptic Curve for encryption key [${KeysBridge.toString(encKey)}]"
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EdSignatureAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EdSignatureAlgorithmTest.groovy
index a7c9b9848..08011d3e4 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/EdSignatureAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/EdSignatureAlgorithmTest.groovy
@@ -17,6 +17,7 @@ package io.jsonwebtoken.impl.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.UnsupportedJwtException
+import io.jsonwebtoken.security.SecureRequest
import io.jsonwebtoken.security.SignatureException
import org.junit.Test
@@ -60,7 +61,7 @@ class EdSignatureAlgorithmTest {
void testGetRequestJcaNameByKeyAlgorithmNameOnly() {
def key = new TestKey(algorithm: EdwardsCurve.X25519.OID)
def payload = [0x00] as byte[]
- def req = new DefaultSecureRequest(payload, null, null, key)
+ def req = SecureRequest.builder().payload(payload).key(key).build()
assertEquals 'X25519', alg.getJcaName(req) // Not the EdDSA default
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithmTest.groovy
index 04264341a..228f556de 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithmTest.groovy
@@ -17,6 +17,8 @@ package io.jsonwebtoken.impl.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.io.Streams
+import io.jsonwebtoken.security.AeadResult
+import io.jsonwebtoken.security.DecryptAeadRequest
import org.junit.Test
import javax.crypto.SecretKey
@@ -63,7 +65,7 @@ class GcmAesAeadAlgorithmTest {
def ins = Streams.of(P)
def aad = Streams.of(AAD)
def out = new ByteArrayOutputStream(8192)
- def res = new DefaultAeadResult(out)
+ def res = AeadResult.with(out)
def req = new DefaultAeadRequest(ins, null, null, KEY, aad, IV)
alg.encrypt(req, res)
@@ -77,7 +79,8 @@ class GcmAesAeadAlgorithmTest {
// now test decryption:
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, res.iv, res.digest)
+ def dreq = DecryptAeadRequest.builder().payload(Streams.of(ciphertext))
+ .key(KEY).associatedData(aad).iv(res.iv).digest(res.digest).build()
alg.decrypt(dreq, out)
assertArrayEquals(P, out.toByteArray())
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/HashAlgorithmsTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/HashAlgorithmsTest.groovy
index 463ae739c..f021e525d 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/HashAlgorithmsTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/HashAlgorithmsTest.groovy
@@ -21,8 +21,6 @@ import io.jsonwebtoken.security.HashAlgorithm
import io.jsonwebtoken.security.Jwks
import org.junit.Test
-import java.nio.charset.StandardCharsets
-
import static org.junit.Assert.*
class HashAlgorithmsTest {
@@ -79,18 +77,12 @@ class HashAlgorithmsTest {
assertNull reg.get('invalid')
}
- static DefaultRequest request(String msg) {
- byte[] data = msg.getBytes(StandardCharsets.UTF_8)
- InputStream payload = Streams.of(data)
- return new DefaultRequest(payload, null, null)
- }
-
static void testSha(HashAlgorithm alg) {
String id = alg.getId()
int c = ('-' as char) as int
def digestLength = id.substring(id.lastIndexOf(c) + 1) as int
assertTrue alg.getJcaName().endsWith('' + digestLength)
- def digest = alg.digest(request("hello"))
+ def digest = alg.digest(Streams.of("hello"))
assertEquals digestLength, (digest.length * Byte.SIZE)
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithmTest.groovy
index 4c0bd0935..97d466755 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithmTest.groovy
@@ -20,6 +20,8 @@ import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.lang.Strings
import io.jsonwebtoken.security.AeadAlgorithm
+import io.jsonwebtoken.security.AeadRequest
+import io.jsonwebtoken.security.DecryptAeadRequest
import io.jsonwebtoken.security.SignatureException
import org.junit.Test
@@ -66,10 +68,8 @@ class HmacAesAeadAlgorithmTest {
def plaintext = Streams.of(data)
ByteArrayOutputStream out = new ByteArrayOutputStream(8192)
- def res = new DefaultAeadResult(out)
- def req = new DefaultAeadRequest(plaintext, null, null, key, null)
-
- alg.encrypt(req, res)
+ def req = AeadRequest.builder().payload(plaintext).key(key).build()
+ def res = alg.encrypt(req, out)
def iv = res.getIv()
def realTag = res.getDigest()
@@ -80,7 +80,8 @@ class HmacAesAeadAlgorithmTest {
byte[] ciphertext = out.toByteArray()
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), key, null, iv, fakeTag)
+ def dreq = DecryptAeadRequest.builder().payload(Streams.of(ciphertext))
+ .key(key).iv(iv).digest(fakeTag).build();
alg.decrypt(dreq, out)
}
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkThumbprintsTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkThumbprintsTest.groovy
index 008a24f55..83d7b7657 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkThumbprintsTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkThumbprintsTest.groovy
@@ -33,8 +33,7 @@ class JwkThumbprintsTest {
static byte[] digest(String json, HashAlgorithm alg) {
def payload = Streams.of(json)
- def req = new DefaultRequest(payload, null, null)
- return alg.digest(req)
+ return alg.digest(payload)
}
static JwkThumbprint thumbprint(String json, HashAlgorithm alg) {
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithmTest.groovy
index 242308bf3..2876672e0 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithmTest.groovy
@@ -21,7 +21,6 @@ import io.jsonwebtoken.impl.DefaultJweHeaderMutator
import io.jsonwebtoken.impl.DefaultMutableJweHeader
import io.jsonwebtoken.io.Encoders
import io.jsonwebtoken.lang.Strings
-import io.jsonwebtoken.security.KeyRequest
import io.jsonwebtoken.security.Keys
import io.jsonwebtoken.security.Password
import org.junit.Test
@@ -43,9 +42,8 @@ class Pbes2HsAkwAlgorithmTest {
int iterations = 50 // must be 1000 or more
def header = Jwts.header().pbes2Count(iterations) as DefaultJweHeaderMutator
def mutable = new DefaultMutableJweHeader(header)
- KeyRequest req = new DefaultKeyRequest<>(KEY, null, null, mutable, Jwts.ENC.A256GCM)
try {
- alg.getEncryptionKey(req)
+ alg.getEncryptionKey(KEY, mutable, Jwts.ENC.A256GCM)
fail()
} catch (IllegalArgumentException iae) {
assertEquals Pbes2HsAkwAlgorithm.MIN_ITERATIONS_MSG_PREFIX + iterations, iae.getMessage()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateConstructorsTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateConstructorsTest.groovy
index ea01367d9..d60e8b2e8 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateConstructorsTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateConstructorsTest.groovy
@@ -19,6 +19,7 @@ import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.lang.Functions
import io.jsonwebtoken.lang.Classes
import io.jsonwebtoken.security.Jwks
+import io.jsonwebtoken.security.Suppliers
import org.junit.Test
class PrivateConstructorsTest {
@@ -36,5 +37,6 @@ class PrivateConstructorsTest {
new Jwks.CRV()
new Jwks.HASH()
new Jwks.OP()
+ new Suppliers()
}
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB1Test.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB1Test.groovy
index 246b82871..995fe3ea4 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB1Test.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB1Test.groovy
@@ -17,6 +17,7 @@ package io.jsonwebtoken.impl.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.io.Streams
+import io.jsonwebtoken.security.DecryptAeadRequest
import org.junit.Test
import javax.crypto.SecretKey
@@ -91,9 +92,8 @@ class RFC7518AppendixB1Test {
def alg = Jwts.ENC.A128CBC_HS256
def aad = Streams.of(A)
def out = new ByteArrayOutputStream(8192)
- def result = new DefaultAeadResult(out)
def request = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)
- alg.encrypt(request, result)
+ def result = alg.encrypt(request, out)
byte[] ciphertext = out.toByteArray()
byte[] tag = result.getDigest()
@@ -105,7 +105,8 @@ class RFC7518AppendixB1Test {
// now test decryption:
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, iv, tag)
+ def dreq = DecryptAeadRequest.builder()
+ .payload(Streams.of(ciphertext)).key(KEY).associatedData(aad).iv(iv).digest(tag).build()
alg.decrypt(dreq, out)
assertArrayEquals(P, out.toByteArray())
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB2Test.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB2Test.groovy
index 8272af884..4b0a7729e 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB2Test.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB2Test.groovy
@@ -18,6 +18,7 @@ package io.jsonwebtoken.impl.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.io.Streams
import io.jsonwebtoken.security.AeadRequest
+import io.jsonwebtoken.security.DecryptAeadRequest
import org.junit.Test
import javax.crypto.SecretKey
@@ -87,9 +88,8 @@ class RFC7518AppendixB2Test {
def alg = Jwts.ENC.A192CBC_HS384
def aad = Streams.of(A)
def out = new ByteArrayOutputStream(8192)
- def result = new DefaultAeadResult(out)
AeadRequest req = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)
- alg.encrypt(req, result)
+ def result = alg.encrypt(req, out)
byte[] ciphertext = out.toByteArray()
byte[] tag = result.getDigest()
@@ -101,7 +101,8 @@ class RFC7518AppendixB2Test {
// now test decryption:
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, iv, tag)
+ def dreq = DecryptAeadRequest.builder().payload(Streams.of(ciphertext))
+ .key(KEY).associatedData(aad).iv(iv).digest(tag).build()
alg.decrypt(dreq, out)
assertArrayEquals(P, out.toByteArray())
}
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB3Test.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB3Test.groovy
index 80fff053b..4d3bfa592 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB3Test.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB3Test.groovy
@@ -89,9 +89,8 @@ class RFC7518AppendixB3Test {
def alg = Jwts.ENC.A256CBC_HS512
def aad = Streams.of(A)
def out = new ByteArrayOutputStream(8192)
- def res = new DefaultAeadResult(out)
AeadRequest req = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)
- alg.encrypt(req, res)
+ def res = alg.encrypt(req, out)
byte[] ciphertext = out.toByteArray()
byte[] tag = res.getDigest()
byte[] iv = res.getIv()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaSignatureAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaSignatureAlgorithmTest.groovy
index 05d24d73a..446dea026 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaSignatureAlgorithmTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaSignatureAlgorithmTest.groovy
@@ -21,6 +21,7 @@ import io.jsonwebtoken.impl.lang.Bytes
import io.jsonwebtoken.impl.lang.CheckedFunction
import io.jsonwebtoken.lang.Assert
import io.jsonwebtoken.security.InvalidKeyException
+import io.jsonwebtoken.security.SecureRequest
import io.jsonwebtoken.security.WeakKeyException
import org.junit.Test
@@ -88,7 +89,7 @@ class RsaSignatureAlgorithmTest {
@Test
void testValidateSigningKeyNotPrivate() {
RSAPublicKey key = createMock(RSAPublicKey)
- def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, key)
+ def request = SecureRequest.builder().payload(Streams.of(new byte[1])).key(key).build()
try {
Jwts.SIG.RS256.digest(request)
fail()
@@ -116,9 +117,8 @@ class RsaSignatureAlgorithmTest {
algs.each {
def pair = it.getId().startsWith("PS") ? pssPair : rsaPair
- def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, pair.getPrivate())
try {
- it.digest(request)
+ it.digest(pair.getPrivate(), Streams.of(new byte[1]))
fail()
} catch (WeakKeyException expected) {
String id = it.getId()
diff --git a/impl/src/test/groovy/io/jsonwebtoken/security/EncryptionAlgorithmsTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/security/EncryptionAlgorithmsTest.groovy
index dabb50d82..4daef6019 100644
--- a/impl/src/test/groovy/io/jsonwebtoken/security/EncryptionAlgorithmsTest.groovy
+++ b/impl/src/test/groovy/io/jsonwebtoken/security/EncryptionAlgorithmsTest.groovy
@@ -17,9 +17,6 @@ package io.jsonwebtoken.security
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.impl.io.Streams
-import io.jsonwebtoken.impl.security.DefaultAeadRequest
-import io.jsonwebtoken.impl.security.DefaultAeadResult
-import io.jsonwebtoken.impl.security.DefaultDecryptAeadRequest
import io.jsonwebtoken.impl.security.GcmAesAeadAlgorithm
import io.jsonwebtoken.lang.Registry
import org.junit.Test
@@ -106,10 +103,8 @@ class EncryptionAlgorithmsTest {
def key = alg.key().build()
def out = new ByteArrayOutputStream()
- def request = new DefaultAeadRequest(Streams.of(PLAINTEXT_BYTES), null, null, key, null)
- def result = new DefaultAeadResult(out)
-
- alg.encrypt(request, result)
+ def request = AeadRequest.builder().payload(Streams.of(PLAINTEXT_BYTES)).key(key).build()
+ def result = alg.encrypt(request, out)
byte[] iv = result.getIv()
byte[] tag = result.getDigest() //there is always a tag, even if there is no AAD
assertNotNull tag
@@ -123,7 +118,7 @@ class EncryptionAlgorithmsTest {
def ciphertext = Streams.of(ciphertextBytes)
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(ciphertext, key, null, iv, tag)
+ def dreq = DecryptAeadRequest.builder().payload(ciphertext).key(key).iv(iv).digest(tag).build()
alg.decrypt(dreq, out)
byte[] decryptedPlaintextBytes = out.toByteArray()
@@ -141,10 +136,8 @@ class EncryptionAlgorithmsTest {
def plaintextIn = Streams.of(PLAINTEXT_BYTES)
def out = new ByteArrayOutputStream(8192)
def aad = Streams.of(AAD_BYTES)
- def req = new DefaultAeadRequest(plaintextIn, null, null, key, aad)
- def res = new DefaultAeadResult(out)
-
- alg.encrypt(req, res)
+ def req = AeadRequest.builder().payload(plaintextIn).key(key).associatedData(aad).build()
+ def res = alg.encrypt(req, out)
byte[] iv = res.getIv()
byte[] tag = res.getDigest()
byte[] ciphertextBytes = out.toByteArray()
@@ -157,7 +150,8 @@ class EncryptionAlgorithmsTest {
def ciphertext = Streams.of(ciphertextBytes)
out = new ByteArrayOutputStream(8192)
- def dreq = new DefaultDecryptAeadRequest(ciphertext, key, aad, iv, tag)
+ def dreq = DecryptAeadRequest.builder()
+ .payload(ciphertext).key(key).associatedData(aad).iv(iv).digest(tag).build()
alg.decrypt(dreq, out)
byte[] decryptedPlaintextBytes = out.toByteArray()
assertArrayEquals(PLAINTEXT_BYTES, decryptedPlaintextBytes)
diff --git a/impl/src/test/groovy/io/jsonwebtoken/security/SecureDigestAlgorithmTest.groovy b/impl/src/test/groovy/io/jsonwebtoken/security/SecureDigestAlgorithmTest.groovy
new file mode 100644
index 000000000..9564e26e2
--- /dev/null
+++ b/impl/src/test/groovy/io/jsonwebtoken/security/SecureDigestAlgorithmTest.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2025 jsonwebtoken.io
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.jsonwebtoken.security
+
+import io.jsonwebtoken.Jwts
+import org.junit.Test
+
+import java.nio.charset.StandardCharsets
+
+import static org.junit.Assert.assertTrue
+
+class SecureDigestAlgorithmTest {
+
+ // only need one each of mac algorithm and signature algorithm - no need to take time for key generation
+ static final def algs = [Jwts.SIG.HS256, Jwts.SIG.ES256]
+
+ @Test
+ void testRoundtrip() {
+
+ final msg = 'hello world'
+ final is = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8))
+
+ algs.each { alg ->
+ def skey
+ def vkey
+
+ if (alg instanceof KeyPairBuilderSupplier) {
+ def pair = alg.keyPair().build()
+ skey = pair.getPrivate()
+ vkey = pair.getPublic()
+ } else {
+ skey = vkey = alg.key().build()
+ }
+
+ byte[] digest = alg.digest(skey, is)
+ is.reset()
+ assertTrue alg.verify(vkey, is, digest)
+ is.reset()
+ }
+ }
+}