Immutables (#790)

* Jwts#header() and JwtBuilder#header() API cleanup

* added license headers, removed unused Conjunctor interface concept

* impl checkpoint for Registry-to-Map implementation change

* Jwts.SIG and Jwts.ENC conversion checkpoint (complete)

* Jwts.KEY and Jwks.HASH conversion checkpoint (complete)

* File header and Javadoc cleanup.  Removed unused SignatureRequest.java concept.

* Changed deprecated API usage in test case

* - Removed *Accessor concepts where possible, just using the *Header interfaces was sufficient
- KeyAlgorithm#getEncryptionKey now accepts a JweHeader that is mutable. Implementations can just use Map#put to modify the header state if desired.

* MapMutator method renaming to avoid odd conventions

* introduced ProtectedJwt concept and intermediate DefaultProtectedJwt implementation

* Removed all usages of CompressionCodecs.java in favor of a new Jwts.ZIP entry.
Renamed all Standard***AlgorithmsBridge to Standard***Algorithms

* CompressionCodec to CompressionAlgorithm transition (complete, code coverage 100%)
This commit is contained in:
lhazlewood 2023-08-04 12:35:33 -07:00 committed by GitHub
parent 992d75d0b4
commit 529f04dd90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
229 changed files with 5269 additions and 5280 deletions

View File

@ -6,6 +6,12 @@ This is a big release! JJWT now fully supports Encrypted JSON Web Tokens (JWE) a
sections below enumerating all new features as well as important notes on breaking changes or backwards-incompatible
changes made in preparation for the upcoming 1.0 release.
**Because breaking changes are being introduced, it is strongly recommended to wait until the upcoming 1.0 release
where you can address breaking changes one time only**.
Those that need immediate JWE encryption and JWK key support
however will likely want to upgrade now and deal with the smaller subset of breaking changes in the 1.0 release.
#### Simplified Starter Jar
Those upgrading to new modular JJWT versions from old single-jar versions will transparently obtain everything
@ -76,11 +82,11 @@ custom code previously written to extend JJWT to use keys from those KeyStores o
The `io.jsonwebtoken.SignatureAlgorithm` enum has been deprecated in favor of new
`io.jsonwebtoken.security.SecureDigestAlgorithm`, `io.jsonwebtoken.security.MacAlgorithm`, and
`io.jsonwebtoken.security.SignatureAlgorithm` interfaces to allow custom algorithm implementations. The new
`SIG` constant in the `Jwts` helper class is a registry of all standard JWS algorithms as expected, exactly like the
`io.jsonwebtoken.security.SignatureAlgorithm` interfaces to allow custom algorithm implementations. The new nested
`Jwts.SIG` static inner class is a registry of all standard JWS algorithms as expected, exactly like the
old enum. This change was made because enums are a static concept by design and cannot
support custom values: those who wanted to use custom signature algorithms could not do so until now. The new
interface now allows anyone to plug in and support custom algorithms with JJWT as desired.
interfaces now allow anyone to plug in and support custom algorithms with JJWT as desired.
#### KeyBuilder and KeyPairBuilder
@ -101,37 +107,31 @@ the old enum-based static utility methods did not.
Now that the JWE and JWK specifications are implemented, only a few things remain for JJWT to be considered at
version 1.0. We have been waiting to apply the 1.0 release version number until the entire set of JWT specifications
are fully supported and we drop JDK 7 support (to allow users to use JDK 8 APIs). To that end, we have had to
deprecate some concepts, or in some rare cases, completely break backwards compatibility to ensure the transition to
1.0 (and JDK 8 APIs) are possible. Any backwards-incompatible changes are listed in the next section below.
are fully supported **and** we drop JDK 7 support (to allow users to use JDK 8 APIs). To that end, we have had to
deprecate some concepts, or in some cases, completely break backwards compatibility to ensure the transition to
1.0 (and JDK 8 APIs) are possible. Most backwards-incompatible changes are listed in the next section below.
#### Backwards Compatibility Breaking Changes, Warnings and Deprecations
* `io.jsonwebtoken.Jwts`'s `header(Map)`, `jwsHeader()` and `jwsHeader(Map)` methods have been deprecated in favor
of the new `header()` builder-based method to support method chaining and dynamic Header type creation.
* `io.jsonwebtoken.Jwt`'s `getBody()` method has been deprecated in favor of a new `getPayload()` method to
reflect correct JWT specification nomenclature/taxonomy.
* `io.jsonwebtoken.Jws`'s `getSignature()` method has been deprecated in favor of a new `getDigest()` method to
support expected congruent behavior with `Jwe` instances (both have digests).
* `io.jsonwebtoken.CompressionCodec` now inherits a new `io.jsonwebtoken.Identifiable` interface and its `getId()`
method is preferred over the now-deprecated `getAlgorithmName()` method. This is to guarantee API congruence with
all other JWT-identifiable algorithm names that can be set as a header value.
* `io.jsonwebtoken.Header` has been changed to accept a type-parameter for sub-type method return values, i.e.
`io.jsonwebtoken.Header<T extends Header>` and a new `io.jsonwebtoken.UnprotectedHeader` interface has been
introduced to represent the concrete type of header without integrity protection. This new `UnprotectedHeader` is
to be used where the previous generic `Header` (non-`JweHeader` and non-`JwsHeader`) interface was used.
* Accordingly, the `Jwts.header()` and `Jwts.header(Map<String,?>)` now return instances of `UnprotectedHeader` instead
of just `Header`.
#### Breaking Changes
* `io.jsonwebtoken.Jwts`'s `header(Map)`, `jwsHeader()` and `jwsHeader(Map)` methods have been removed in favor
of the new `header()` builder-based method to support method chaining and dynamic Header type creation.
* **JWTs that do not contain JSON Claims now have a payload type of `byte[]` instead of `String`** (that is,
`Jwt<byte[]>` instead of `Jwt<String>`). This is because JWTs, especially when used with the
`cty` (Content Type) header, are capable of handling _any_ type of payload, not just Strings. The previous JJWT
@ -151,7 +151,7 @@ deprecate some concepts, or in some rare cases, completely break backwards compa
* The `JwtParser`'s `Jwt<Header, String> parsePlaintextJwt(String plaintextJwt)` and
`Jws<String> parsePlaintextJws(String plaintextJws)` methods have been changed to
`Jwt<UnprotectedHeader, byte[]> parseContentJwt(String plaintextJwt)` and
`Jwt<Header, byte[]> parseContentJwt(String plaintextJwt)` and
`Jws<byte[]> parseContentJws(String plaintextJws)` respectively.
* `JwtHandler`'s `onPlaintextJwt(String)` and `onPlaintextJws(String)` methods have been changed to
@ -165,25 +165,28 @@ deprecate some concepts, or in some rare cases, completely break backwards compa
* `io.jsonwebtoken.SigningKeyResolver`'s `resolveSigningKey(JwsHeader, String)` method has been changed to
`resolveSigningKey(JwsHeader, byte[])`.
* `io.jsonwebtoken.Jwts`'s `parser()` method deprecated 4 years ago has been renamed to `legacyParser()` to
allow an updated `parser()` method to return a `JwtParserBuilder` instead of a direct `JwtParser` instance.
This `legacyParser()` method will be removed entirely for the 1.0 release - please change your code to use the
updated `parser()` method that returns a builder as soon as possible.
* `io.jsonwebtoken.Jwts`'s `header()` method has been renamed to `unprotectedHeader()` to allow a newer/updated
`header()` method to return a `DynamicHeaderBuilder` instead of a direct `Header` instance. This new method /
return value is the recommended approach for building headers, as it will dynamically create an `UnprotectedHeader`,
`JwsHeader` or `JweHeader` automatically based on builder state.
* **`io.jsonwebtoken.Claims` and `io.jsonwebtoken.Header` instances are now immutable** to enhance security and thread
safety. Creation and mutation are supported with newly introduced `ClaimsBuilder` and `HeaderBuilder` concepts.
* `io.jsonwebtoken.Jwts`'s `headerBuilder()` method has been renamed to `header()` and returns a
`DynamicHeaderBuilder` instead of a direct `Header` instance. This builder method is the recommended approach
for building headers in the future, as it will dynamically create an `UnprotectedHeader`, `JwsHeader` or `JweHeader`
* Consequently, `io.jsonwebtoken.Jwts`'s `claims()` static method has been changed to return a `ClaimsBuilder` instead
of a `Claims` instance.
* Similarly, `io.jsonwebtoken.Jwts`'s `header()` static method has been changed to return a `HeaderBuilder` instead of
a `Header` instance. The `HeaderBuilder` will dynamically create a `Header`, `JwsHeader` or `JweHeader`
automatically based on builder state.
* `io.jsonwebtoken.Jwts`'s `header()` method now returns a `DynamicHeaderBuilder` instead of a
direct `Header` instance. This new method / return value is the recommended approach for building headers
in the future, as it will dynamically create an `UnprotectedHeader`, `JwsHeader` or `JweHeader` automatically
based on builder state.
* `io.jsonwebtoken.Jwts`'s `parser()` method deprecated 4 years ago has been changed to now return a
`JwtParserBuilder` instead of a direct `JwtParser` instance.
* `io.jsonwebtoken.CompressionCodec` implementations are no longer discoverable via `java.util.ServiceLoader` due to
runtime performance problems with the JDK's `ServiceLoader` implementation per
https://github.com/jwtk/jjwt/issues/648.
* Prior to this release, if there was a serialization problem when serializing the JWT Header, an `IllegalStateException`
was thrown. If there was a problem when serializing the JWT claims, an `IllegalArgumentException` was
@ -207,6 +210,7 @@ deprecate some concepts, or in some rare cases, completely break backwards compa
This is to ensure JWKs have `toString()` and application log safety (do not print secure material), but still
serialize to JSON correctly.
* `io.jsonwebtoken.InvalidClaimException` and it's two subclasses (`IncorrectClaimException` and `MissingClaimException`)
were previously mutable, allowing the corresponding claim name and claim value to be set on the exception after
creation. These should have always been immutable without those setters (just getters), and this was a previous

View File

@ -1426,7 +1426,7 @@ key algorithms:
<sup><b>2</b>. Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
These are all represented as constants in the `io.jsonwebtoken.Jwts.SIG` registry singleton.
These are all represented as constants in the `io.jsonwebtoken.Jwts.SIG` convenience class.
<a name="jws-key"></a>
### Signature Algorithms Keys
@ -3362,7 +3362,7 @@ AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc.
// Create the compact JWE:
String jwe = Jwts.builder().setIssuer("me")
// Optional work factor is specified in the header:
//.setHeader(Jwts.headerBuilder().setPbes2Count(pbkdf2Iterations))
//.header().setPbes2Count(pbkdf2Iterations)).and()
.encryptWith(password, alg, enc)
.compact();

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -43,7 +43,7 @@ public abstract class ClaimJwtException extends JwtException {
/**
* The header associated with the Claims that failed validation.
*/
private final Header<?> header;
private final Header header;
/**
* The Claims that failed validation.
@ -57,7 +57,7 @@ public abstract class ClaimJwtException extends JwtException {
* @param claims the claims obtained
* @param message the exception message
*/
protected ClaimJwtException(Header<?> header, Claims claims, String message) {
protected ClaimJwtException(Header header, Claims claims, String message) {
super(message);
this.header = header;
this.claims = claims;
@ -72,7 +72,7 @@ public abstract class ClaimJwtException extends JwtException {
* @param message the exception message
* @param cause the exception that caused this ClaimJwtException to be thrown.
*/
protected ClaimJwtException(Header<?> header, Claims claims, String message, Throwable cause) {
protected ClaimJwtException(Header header, Claims claims, String message, Throwable cause) {
super(message, cause);
this.header = header;
this.claims = claims;
@ -92,7 +92,7 @@ public abstract class ClaimJwtException extends JwtException {
*
* @return the header associated with the {@link #getClaims() claims} that failed validation.
*/
public Header<?> getHeader() {
public Header getHeader() {
return header;
}
}

View File

@ -19,25 +19,24 @@ import java.util.Date;
import java.util.Map;
/**
* A JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4">Claims set</a>.
* A JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4">Claims set</a>.
*
* <p>This is ultimately a JSON map and any values can be added to it, but JWT standard names are provided as
* type-safe getters and setters for convenience.</p>
* <p>This is an immutable JSON map with convenient type-safe getters for JWT standard claim names.</p>
*
* <p>Because this interface extends <code>Map&lt;String, Object&gt;</code>, if you would like to add your own properties,
* you simply use map methods, for example:</p>
* <p>Additionally, this interface also extends <code>Map&lt;String, Object&gt;</code>, so you can use standard
* {@code Map} accessor/iterator methods as desired, for example:</p>
*
* <blockquote><pre>
* claims.{@link Map#put(Object, Object) put}("someKey", "someValue");</pre></blockquote>
* claims.get("someKey");</pre></blockquote>
*
* <h2>Creation</h2>
*
* <p>It is easiest to create a {@code Claims} instance by calling one of the
* {@link Jwts#claims() JWTs.claims()} factory methods.</p>
* <p>However, because {@code Claims} instances are immutable, calling any of the map mutation methods
* (such as {@code Map.}{@link Map#put(Object, Object) put}, etc) will result in a runtime exception. The
* {@code Map} interface is implemented specifically for the convenience of working with existing Map-based utilities
* and APIs.</p>
*
* @since 0.1
*/
public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
public interface Claims extends Map<String, Object>, Identifiable {
/** JWT {@code Issuer} claims parameter name: <code>"iss"</code> */
String ISSUER = "iss";
@ -61,7 +60,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
String ID = "jti";
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.1">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1">
* <code>iss</code></a> (issuer) value or {@code null} if not present.
*
* @return the JWT {@code iss} value or {@code null} if not present.
@ -69,13 +68,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
String getIssuer();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setIssuer(String iss);
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.2">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2">
* <code>sub</code></a> (subject) value or {@code null} if not present.
*
* @return the JWT {@code sub} value or {@code null} if not present.
@ -83,13 +76,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
String getSubject();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setSubject(String sub);
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.3">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3">
* <code>aud</code></a> (audience) value or {@code null} if not present.
*
* @return the JWT {@code aud} value or {@code null} if not present.
@ -97,13 +84,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
String getAudience();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setAudience(String aud);
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.4">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4">
* <code>exp</code></a> (expiration) timestamp or {@code null} if not present.
*
* <p>A JWT obtained after this timestamp should not be used.</p>
@ -113,13 +94,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
Date getExpiration();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setExpiration(Date exp);
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.5">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5">
* <code>nbf</code></a> (not before) timestamp or {@code null} if not present.
*
* <p>A JWT obtained before this timestamp should not be used.</p>
@ -129,13 +104,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
Date getNotBefore();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setNotBefore(Date nbf);
/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.6">
* Returns the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6">
* <code>iat</code></a> (issued at) timestamp or {@code null} if not present.
*
* <p>If present, this value is the timestamp when the JWT was created.</p>
@ -145,13 +114,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
Date getIssuedAt();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setIssuedAt(Date iat);
/**
* Returns the JWTs <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.7">
* Returns the JWTs <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7">
* <code>jti</code></a> (JWT ID) value or {@code null} if not present.
*
* <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If available, this value is expected to be
@ -161,14 +124,9 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
*
* @return the JWT {@code jti} value or {@code null} if not present.
*/
@Override // just for JavaDoc specific to the JWT spec
String getId();
/**
* {@inheritDoc}
*/
@Override //only for better/targeted JavaDoc
Claims setId(String jti);
/**
* Returns the JWTs claim ({@code claimName}) value as a type {@code requiredType}, or {@code null} if not present.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 jsonwebtoken.io
* 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.
@ -15,11 +15,15 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.lang.MapMutator;
/**
* A JWT {@link Header} that is not integrity protected via either digital signature or encryption. It will
* always have an {@link #getAlgorithm() algorithm} of {@code none}.
* {@link Builder} used to create an immutable {@link Claims} instance.
*
* @see JwtBuilder
* @see Claims
* @since JJWT_RELEASE_VERSION
*/
public interface UnprotectedHeader extends Header<UnprotectedHeader> {
public interface ClaimsBuilder extends MapMutator<String, Object, ClaimsBuilder>, ClaimsMutator<ClaimsBuilder>, Builder<Claims> {
}

View File

@ -28,7 +28,7 @@ import java.util.Date;
public interface ClaimsMutator<T extends ClaimsMutator<T>> {
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.1">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1">
* <code>iss</code></a> (issuer) value. A {@code null} value will remove the property from the JSON map.
*
* @param iss the JWT {@code iss} value or {@code null} to remove the property from the JSON map.
@ -37,7 +37,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setIssuer(String iss);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.2">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2">
* <code>sub</code></a> (subject) value. A {@code null} value will remove the property from the JSON map.
*
* @param sub the JWT {@code sub} value or {@code null} to remove the property from the JSON map.
@ -46,7 +46,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setSubject(String sub);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.3">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3">
* <code>aud</code></a> (audience) value. A {@code null} value will remove the property from the JSON map.
*
* @param aud the JWT {@code aud} value or {@code null} to remove the property from the JSON map.
@ -55,7 +55,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setAudience(String aud);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.4">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4">
* <code>exp</code></a> (expiration) timestamp. A {@code null} value will remove the property from the JSON map.
*
* <p>A JWT obtained after this timestamp should not be used.</p>
@ -66,7 +66,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setExpiration(Date exp);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.5">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5">
* <code>nbf</code></a> (not before) timestamp. A {@code null} value will remove the property from the JSON map.
*
* <p>A JWT obtained before this timestamp should not be used.</p>
@ -77,7 +77,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setNotBefore(Date nbf);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.6">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6">
* <code>iat</code></a> (issued at) timestamp. A {@code null} value will remove the property from the JSON map.
*
* <p>The value is the timestamp when the JWT was created.</p>
@ -88,7 +88,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {
T setIssuedAt(Date iat);
/**
* Sets the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.7">
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7">
* <code>jti</code></a> (JWT ID) value. A {@code null} value will remove the property from the JSON map.
*
* <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a

View File

@ -15,6 +15,8 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.io.CompressionAlgorithm;
/**
* Compresses and decompresses byte arrays according to a compression algorithm.
*
@ -24,11 +26,13 @@ package io.jsonwebtoken;
* {@link Identifiable#getId() getId()} will be used as the JWT
* <a href="https://tools.ietf.org/html/rfc7516#section-4.1.3"><code>zip</code></a> header value.</p>
*
* @see CompressionCodecs#DEFLATE
* @see CompressionCodecs#GZIP
* @see Jwts.ZIP#DEF
* @see Jwts.ZIP#GZIP
* @since 0.6.0
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link io.jsonwebtoken.io.CompressionAlgorithm} to equal the RFC name for this concept.
*/
public interface CompressionCodec extends Identifiable {
@Deprecated
public interface CompressionCodec extends CompressionAlgorithm {
/**
* The algorithm name to use as the JWT
@ -36,29 +40,10 @@ public interface CompressionCodec extends Identifiable {
*
* @return the algorithm name to use as the JWT
* <a href="https://tools.ietf.org/html/rfc7516#section-4.1.3"><code>zip</code></a> header value.
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link Identifiable#getId()} to ensure congruence with
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #getId()} to ensure congruence with
* all other identifiable algorithms.
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
String getAlgorithmName();
/**
* Compresses the specified byte array, returning the compressed byte array result.
*
* @param content bytes to compress
* @return compressed bytes
* @throws CompressionException if the specified byte array cannot be compressed.
*/
byte[] compress(byte[] content) throws CompressionException;
/**
* Decompresses the specified compressed byte array, returning the decompressed byte array result. The
* specified byte array must already be in compressed form.
*
* @param compressed compressed bytes
* @return decompressed bytes
* @throws CompressionException if the specified byte array cannot be decompressed.
*/
byte[] decompress(byte[] compressed) throws CompressionException;
}

View File

@ -15,23 +15,25 @@
*/
package io.jsonwebtoken;
import java.util.Collection;
/**
* Looks for a JWT {@code zip} header, and if found, returns the corresponding {@link CompressionCodec} the parser
* can use to decompress the JWT body.
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
* {@link Jwts.ZIP#DEF DEFLATE} and {@link Jwts.ZIP#GZIP GZIP} algorithms by default - you do not need to
* specify a {@code CompressionCodecResolver} in these cases.</p>
*
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you can implement
* your own {@link CompressionCodecResolver} and specify that when
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} and
* {@link io.jsonwebtoken.JwtBuilder#compressWith(io.jsonwebtoken.io.CompressionAlgorithm) building} and
* {@link io.jsonwebtoken.JwtParser#setCompressionCodecResolver(CompressionCodecResolver) parsing} JWTs.</p>
*
* @see JwtParserBuilder#setCompressionCodecResolver(CompressionCodecResolver)
* @see JwtParserBuilder#addCompressionAlgorithms(Collection)
* @since 0.6.0
* @deprecated in favor of {@link Locator}
* @see JwtParserBuilder#setCompressionCodecLocator(Locator)
* @deprecated in favor of {@link JwtParserBuilder#addCompressionAlgorithms(Collection)}
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
@ -45,6 +47,6 @@ public interface CompressionCodecResolver {
* @return CompressionCodec matching the {@code zip} header, or null if there is no {@code zip} header.
* @throws CompressionException if a {@code zip} header value is found and not supported.
*/
CompressionCodec resolveCompressionCodec(Header<?> header) throws CompressionException;
CompressionCodec resolveCompressionCodec(Header header) throws CompressionException;
}

View File

@ -15,15 +15,15 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.lang.Classes;
/**
* Provides default implementations of the {@link CompressionCodec} interface.
*
* @see #DEFLATE
* @see #GZIP
* @see Jwts.ZIP#DEF
* @see Jwts.ZIP#GZIP
* @since 0.7.0
* @deprecated in favor of {@link Jwts.ZIP}.
*/
@Deprecated //TODO: delete for 1.0
public final class CompressionCodecs {
private CompressionCodecs() {
@ -32,9 +32,11 @@ public final class CompressionCodecs {
/**
* Codec implementing the <a href="https://tools.ietf.org/html/rfc7518">JWA</a> standard
* <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
*
* @deprecated in favor of {@link Jwts.ZIP#DEF}.
*/
public static final CompressionCodec DEFLATE =
Classes.newInstance("io.jsonwebtoken.impl.compression.DeflateCompressionCodec");
@Deprecated
public static final CompressionCodec DEFLATE = (CompressionCodec) Jwts.ZIP.DEF;
/**
* Codec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> compression algorithm.
@ -44,9 +46,11 @@ public final class CompressionCodecs {
* <p><b>This is not a standard JWA compression algorithm</b>. Be sure to use this only when you are confident
* that all parties accessing the token support the gzip algorithm.</p>
*
* <p>If you're concerned about compatibility, the {@link #DEFLATE DEFLATE} code is JWA standards-compliant.</p>
* <p>If you're concerned about compatibility, the {@link Jwts.ZIP#DEF DEF} code is JWA standards-compliant.</p>
*
* @deprecated in favor of {@link Jwts.ZIP#GZIP}
*/
public static final CompressionCodec GZIP =
Classes.newInstance("io.jsonwebtoken.impl.compression.GzipCompressionCodec");
@Deprecated
public static final CompressionCodec GZIP = (CompressionCodec) Jwts.ZIP.GZIP;
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.lang.MapMutator;
import io.jsonwebtoken.security.X509Builder;
/**
* A {@link Builder} that dynamically determines the type of {@link Header} to create based on builder state.
* <ul>
* <li>If only standard {@link Header} properties have been set (that is, no
* {@link JwsHeader} or {@link JweHeader} properties have been set), an {@link UnprotectedHeader} will be created.</li>
* <li>If any {@link ProtectedHeader} properties have been set (but no {@link JweHeader} properties), a
* {@link JwsHeader} will be created.</li>
* <li>If any {@link JweHeader} properties have been set, a {@link JweHeader} will be created.</li>
* </ul>
*
* @since JJWT_RELEASE_VERSION
*/
public interface DynamicHeaderBuilder extends
MapMutator<String, Object, DynamicHeaderBuilder>,
X509Builder<DynamicHeaderBuilder>,
JweHeaderMutator<DynamicHeaderBuilder>,
Builder<Header<?>> {
}

View File

@ -29,7 +29,7 @@ public class ExpiredJwtException extends ClaimJwtException {
* @param claims jwt claims (body)
* @param message the message explaining why the exception is thrown.
*/
public ExpiredJwtException(Header<?> header, Claims claims, String message) {
public ExpiredJwtException(Header header, Claims claims, String message) {
super(header, claims, message);
}
@ -42,7 +42,7 @@ public class ExpiredJwtException extends ClaimJwtException {
* @param claims jwt claims (body)
* @since 0.5
*/
public ExpiredJwtException(Header<?> header, Claims claims, String message, Throwable cause) {
public ExpiredJwtException(Header header, Claims claims, String message, Throwable cause) {
super(header, claims, message, cause);
}
}

View File

@ -18,25 +18,31 @@ package io.jsonwebtoken;
import java.util.Map;
/**
* A JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-5">JOSE header</a>.
* A JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-5">JOSE header</a>.
*
* <p>This is ultimately a JSON map and any values can be added to it, but JWT JOSE standard names are provided as
* type-safe getters and setters for convenience.</p>
* <p>This is an immutable JSON map with convenient type-safe getters for JWT standard header parameter names.</p>
*
* <p>Because this interface extends {@code Map&lt;String, Object&gt;}, if you would like to add your own properties,
* you simply use map methods, for example:</p>
* <p>Because this interface extends <code>Map&lt;String, Object&gt;</code>, you can use standard {@code Map}
* accessor/iterator methods as desired, for example:</p>
*
* <pre>
* header.{@link Map#put(Object, Object) put}("headerParamName", "headerParamValue");
* </pre>
* <blockquote><pre>
* header.get("someKey");</pre></blockquote>
*
* <h2>Creation</h2>
* <p>However, because {@code Header} instances are immutable, calling any of the map mutation methods
* (such as {@code Map.}{@link Map#put(Object, Object) put}, etc) will result in a runtime exception.</p>
*
* <p>It is easiest to create a {@code Header} instance by using {@link Jwts#header()}.</p>
* <p><b>Security</b></p>
*
* <p>The {@code Header} interface itself makes no implications of integrity protection via either digital signatures or
* encryption. Instead, {@link JwsHeader} and {@link JweHeader} represent this information for respective
* {@link Jws} and {@link Jwe} instances.</p>
*
* @see ProtectedHeader
* @see JwsHeader
* @see JweHeader
* @since 0.1
*/
public interface Header<T extends Header<T>> extends Map<String, Object>, HeaderMutator<T> {
public interface Header extends Map<String, Object> {
/**
* JWT {@code Type} (typ) value: <code>"JWT"</code>
@ -74,7 +80,6 @@ public interface Header<T extends Header<T>> extends Map<String, Object>, Header
*
* @deprecated use {@link #COMPRESSION_ALGORITHM} instead.
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
@ -117,14 +122,14 @@ public interface Header<T extends Header<T>> extends Map<String, Object>, Header
* <ul>
* <li>If the JWT is a Signed JWT (a JWS), the <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">
* <code>alg</code></a> (Algorithm) header parameter identifies the cryptographic algorithm used to secure the
* JWS. Consider using {@link Jwts#SIG}.{@link io.jsonwebtoken.lang.Registry#find(Object) find(id)}
* JWS. Consider using {@link Jwts.SIG}.{@link io.jsonwebtoken.lang.Registry#get(Object) get(id)}
* to convert this string value to a type-safe {@code SecureDigestAlgorithm} instance.</li>
* <li>If the JWT is an Encrypted JWT (a JWE), the
* <a href="https://tools.ietf.org/html/rfc7516#section-4.1.1"><code>alg</code></a> (Algorithm) header parameter
* identifies the cryptographic key management algorithm used to encrypt or determine the value of the Content
* Encryption Key (CEK). The encrypted content is not usable if the <code>alg</code> value does not represent a
* supported algorithm, or if the recipient does not have a key that can be used with that algorithm. Consider
* using {@link Jwts#KEY}.{@link io.jsonwebtoken.lang.Registry#find(Object) find(id)} to convert this string value
* using {@link Jwts.KEY}.{@link io.jsonwebtoken.lang.Registry#get(Object) get(id)} to convert this string value
* to a type-safe {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} instance.</li>
* </ul>
*

View File

@ -15,13 +15,15 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.lang.MapMutator;
/**
* Mutation (modifications) to a {@link Header Header} instance.
*
* @param <T> the mutator subtype, for method chaining
* @since JJWT_RELEASE_VERSION
*/
public interface HeaderMutator<T extends HeaderMutator<T>> {
public interface HeaderMutator<T extends HeaderMutator<T>> extends MapMutator<String, Object, T> {
/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-5.1">

View File

@ -18,10 +18,7 @@ package io.jsonwebtoken;
/**
* An object that may be uniquely identified by an {@link #getId() id} relative to other instances of the same type.
*
* <p>All JWT concepts that have a
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html">JWA</a> identifier value implement this interface.
* Specifically, there are four JWT concepts that are {@code Identifiable}. The following table indicates how
* their {@link #getId() id} values are used.</p>
* <p>The following table indicates how various JWT or JWK {@link #getId() getId()} values are used.</p>
*
* <table>
* <caption>JWA Identifiable Concepts</caption>
@ -33,6 +30,31 @@ package io.jsonwebtoken;
* </thead>
* <tbody>
* <tr>
* <td>{@link io.jsonwebtoken.Claims Claims}</td>
* <td>JWT's <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7">{@code jti} (JWT ID)</a>
* claim.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.security.Jwk Jwk}</td>
* <td>JWK's <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5">{@code kid} (Key ID)</a>
* parameter value.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.CompressionCodec CompressionCodec}</td>
* <td>JWE protected header's
* <a href="https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3">{@code zip} (Compression Algorithm)</a>
* parameter value.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.security.HashAlgorithm HashAlgorithm}</td>
* <td>Within a {@link io.jsonwebtoken.security.JwkThumbprint JwkThumbprint}'s URI value.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.security.MacAlgorithm MacAlgorithm}</td>
* <td>JWS protected header's
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">{@code alg} (Algorithm)</a> parameter value.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.security.SignatureAlgorithm SignatureAlgorithm}</td>
* <td>JWS protected header's
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">{@code alg} (Algorithm)</a> parameter value.</td>
@ -49,11 +71,6 @@ package io.jsonwebtoken;
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">{@code enc} (Encryption Algorithm)</a>
* parameter value.</td>
* </tr>
* <tr>
* <td>{@link io.jsonwebtoken.security.Jwk Jwk}</td>
* <td>JWK's <a href="https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5">{@code kid} (Key ID)</a>
* parameter value.</td>
* </tr>
* </tbody>
* </table>
*

View File

@ -32,7 +32,7 @@ public class IncorrectClaimException extends InvalidClaimException {
* @param claimValue the value of the claim that could not be validated
* @param message the exception message
*/
public IncorrectClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message) {
public IncorrectClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {
super(header, claims, claimName, claimValue, message);
}
@ -46,7 +46,7 @@ public class IncorrectClaimException extends InvalidClaimException {
* @param message the exception message
* @param cause the underlying cause that resulted in this exception being thrown
*/
public IncorrectClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
public IncorrectClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
super(header, claims, claimName, claimValue, message, cause);
}
}

View File

@ -44,7 +44,7 @@ public class InvalidClaimException extends ClaimJwtException {
* @param claimValue the value of the claim that could not be validated
* @param message the exception message
*/
protected InvalidClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message) {
protected InvalidClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {
super(header, claims, message);
this.claimName = claimName;
this.claimValue = claimValue;
@ -60,7 +60,7 @@ public class InvalidClaimException extends ClaimJwtException {
* @param message the exception message
* @param cause the underlying cause that resulted in this exception being thrown
*/
protected InvalidClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
protected InvalidClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
super(header, claims, message, cause);
this.claimName = claimName;
this.claimValue = claimValue;

View File

@ -22,7 +22,7 @@ package io.jsonwebtoken;
* @param <B> payload type, either {@link Claims} or {@code byte[]} content.
* @since JJWT_RELEASE_VERSION
*/
public interface Jwe<B> extends Jwt<JweHeader, B> {
public interface Jwe<B> extends ProtectedJwt<JweHeader, B> {
/**
* Returns the Initialization Vector used during JWE encryption and decryption.
@ -30,13 +30,4 @@ public interface Jwe<B> extends Jwt<JweHeader, B> {
* @return the Initialization Vector used during JWE encryption and decryption.
*/
byte[] getInitializationVector();
/**
* Returns the Additional Authenticated Data authentication Tag used for JWE header
* authenticity and integrity verification.
*
* @return the Additional Authenticated Data authentication Tag used for JWE header
* authenticity and integrity verification.
*/
byte[] getAadTag();
}

View File

@ -18,17 +18,16 @@ package io.jsonwebtoken;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.KeyAlgorithm;
import io.jsonwebtoken.security.PublicJwk;
import io.jsonwebtoken.security.StandardKeyAlgorithms;
import javax.crypto.SecretKey;
import java.security.Key;
/**
* A <a href="https://tools.ietf.org/html/rfc7516">JWE</a> header.
* A <a href="https://www.rfc-editor.org/rfc/rfc7516.html">JWE</a> header.
*
* @since JJWT_RELEASE_VERSION
*/
public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<JweHeader> {
public interface JweHeader extends ProtectedHeader {
/**
* Returns the JWE <a href="https://tools.ietf.org/html/rfc7516#section-4.1.2">{@code enc} (Encryption
@ -66,11 +65,11 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* @return the <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">{@code epk} (Ephemeral
* Public Key)</a> header value created by the JWE originator for use with key agreement algorithms, or
* {@code null} if not present.
* @see Jwts#KEY
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
PublicJwk<?> getEphemeralPublicKey();
@ -81,10 +80,10 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* @return any information about the JWE producer for use with key agreement algorithms, or {@code null} if not
* present.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
byte[] getAgreementPartyUInfo();
@ -95,10 +94,10 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* @return any information about the JWE recipient for use with key agreement algorithms, or {@code null} if not
* present.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
byte[] getAgreementPartyVInfo();
@ -114,9 +113,9 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* automatically when producing the encryption key.</p>
*
* @return the 96-bit initialization vector generated during key encryption, or {@code null} if not present.
* @see StandardKeyAlgorithms#A128GCMKW
* @see StandardKeyAlgorithms#A192GCMKW
* @see StandardKeyAlgorithms#A256GCMKW
* @see Jwts.KEY#A128GCMKW
* @see Jwts.KEY#A192GCMKW
* @see Jwts.KEY#A256GCMKW
*/
byte[] getInitializationVector();
@ -131,9 +130,9 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* automatically when producing the encryption key.</p>
*
* @return the 128-bit authentication tag resulting from key encryption, or {@code null} if not present.
* @see StandardKeyAlgorithms#A128GCMKW
* @see StandardKeyAlgorithms#A192GCMKW
* @see StandardKeyAlgorithms#A256GCMKW
* @see Jwts.KEY#A128GCMKW
* @see Jwts.KEY#A192GCMKW
* @see Jwts.KEY#A256GCMKW
*/
byte[] getAuthenticationTag();
@ -144,9 +143,9 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* @return the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, or {@code null}
* if not present.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2">JWE <code>p2c</code> (PBES2 Count) Header Parameter</a>
* @see StandardKeyAlgorithms#PBES2_HS256_A128KW
* @see StandardKeyAlgorithms#PBES2_HS384_A192KW
* @see StandardKeyAlgorithms#PBES2_HS512_A256KW
* @see Jwts.KEY#PBES2_HS256_A128KW
* @see Jwts.KEY#PBES2_HS384_A192KW
* @see Jwts.KEY#PBES2_HS512_A256KW
*/
Integer getPbes2Count();
@ -163,9 +162,9 @@ public interface JweHeader extends ProtectedHeader<JweHeader>, JweHeaderMutator<
* @return the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption, or
* {@code null} if not present.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1">JWE <code>p2s</code> (PBES2 Salt Input) Header Parameter</a>
* @see StandardKeyAlgorithms#PBES2_HS256_A128KW
* @see StandardKeyAlgorithms#PBES2_HS384_A192KW
* @see StandardKeyAlgorithms#PBES2_HS512_A256KW
* @see Jwts.KEY#PBES2_HS256_A128KW
* @see Jwts.KEY#PBES2_HS384_A192KW
* @see Jwts.KEY#PBES2_HS512_A256KW
*/
byte[] getPbes2Salt();
}

View File

@ -16,7 +16,6 @@
package io.jsonwebtoken;
import io.jsonwebtoken.security.KeyAlgorithm;
import io.jsonwebtoken.security.StandardKeyAlgorithms;
/**
* Mutation (modifications) to a {@link JweHeader} instance.
@ -26,20 +25,6 @@ import io.jsonwebtoken.security.StandardKeyAlgorithms;
*/
public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends ProtectedHeaderMutator<T> {
// /**
// * Sets the JWE <a href="https://tools.ietf.org/html/rfc7516#section-4.1.2">{@code enc} (Encryption
// * Algorithm)</a> header value. A {@code null} value will remove the property from the JSON map.
// *
// * <p>This should almost never be set by JJWT users directly - JJWT will always set this value to the value
// * returned by {@link AeadAlgorithm#getId()} when performing encryption, overwriting any potential previous
// * value.</p>
// *
// * @param enc the encryption algorithm identifier obtained from {@link AeadAlgorithm#getId()}.
// * @return this header for method chaining
// */
// @SuppressWarnings("UnusedReturnValue")
// JweHeader setEncryptionAlgorithm(String enc);
/**
* Sets any information about the JWE producer for use with key agreement algorithms. A {@code null} or empty value
* removes the property from the JSON map.
@ -47,10 +32,10 @@ public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends Protect
* @param info information about the JWE producer to use with key agreement algorithms.
* @return the header for method chaining.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
T setAgreementPartyUInfo(byte[] info);
@ -65,10 +50,10 @@ public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends Protect
* @param info information about the JWE producer to use with key agreement algorithms.
* @return the header for method chaining.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
T setAgreementPartyUInfo(String info);
@ -79,10 +64,10 @@ public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends Protect
* @param info information about the JWE recipient to use with key agreement algorithms.
* @return the header for method chaining.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
T setAgreementPartyVInfo(byte[] info);
@ -97,10 +82,10 @@ public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends Protect
* @param info information about the JWE recipient to use with key agreement algorithms.
* @return the header for method chaining.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>
* @see StandardKeyAlgorithms#ECDH_ES
* @see StandardKeyAlgorithms#ECDH_ES_A128KW
* @see StandardKeyAlgorithms#ECDH_ES_A192KW
* @see StandardKeyAlgorithms#ECDH_ES_A256KW
* @see Jwts.KEY#ECDH_ES
* @see Jwts.KEY#ECDH_ES_A128KW
* @see Jwts.KEY#ECDH_ES_A192KW
* @see Jwts.KEY#ECDH_ES_A256KW
*/
T setAgreementPartyVInfo(String info);
@ -122,25 +107,10 @@ public interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends Protect
* greater than or equal to 1000 (one thousand).
* @return the header for method chaining
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2">JWE <code>p2c</code> (PBES2 Count) Header Parameter</a>
* @see StandardKeyAlgorithms#PBES2_HS256_A128KW
* @see StandardKeyAlgorithms#PBES2_HS384_A192KW
* @see StandardKeyAlgorithms#PBES2_HS512_A256KW
* @see Jwts.KEY#PBES2_HS256_A128KW
* @see Jwts.KEY#PBES2_HS384_A192KW
* @see Jwts.KEY#PBES2_HS512_A256KW
* @see <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2">OWASP PBKDF2 Iteration Recommendations</a>
*/
T setPbes2Count(int count);
// /**
// * Sets the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption. This should
// * almost never be used by JJWT users directly - it should instead be automatically generated and set within a
// * PBKDF2-based {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} implementation.
// *
// * @param salt the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption.
// * @return the header for method chaining
// * @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1">JWE <code>p2s</code> (PBES2 Salt Input) Header Parameter</a>
// * @see Jwts.KEY#PBES2_HS256_A128KW
// * @see Jwts.KEY#PBES2_HS384_A192KW
// * @see Jwts.KEY#PBES2_HS512_A256KW
// */
// JweHeader setPbes2Salt(byte[] salt);
}

View File

@ -21,12 +21,14 @@ package io.jsonwebtoken;
* @param <B> the type of the JWS body contents, either a String or a {@link Claims} instance.
* @since 0.1
*/
public interface Jws<B> extends Jwt<JwsHeader, B> {
public interface Jws<B> extends ProtectedJwt<JwsHeader, B> {
/**
* Returns the verified JWS signature as a Base64Url string.
*
* @return the verified JWS signature as a Base64Url string.
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #getDigest() getDigest()}.
*/
@Deprecated
String getSignature(); //TODO for 1.0: return a byte[]
}

View File

@ -20,7 +20,7 @@ package io.jsonwebtoken;
*
* @since 0.1
*/
public interface JwsHeader extends ProtectedHeader<JwsHeader> {
public interface JwsHeader extends ProtectedHeader {
/**
* JWS <a href="https://tools.ietf.org/html/rfc7515#section-4.1.1">Algorithm Header</a> name: the string literal <b><code>alg</code></b>

View File

@ -18,10 +18,11 @@ package io.jsonwebtoken;
/**
* An expanded (not compact/serialized) JSON Web Token.
*
* @param <P> the type of the JWT payload, either a byte array or a {@link Claims} instance.
* @param <H> the type of the JWT header
* @param <P> the type of the JWT payload, either a content byte array or a {@link Claims} instance.
* @since 0.1
*/
public interface Jwt<H extends Header<H>, P> {
public interface Jwt<H extends Header, P> {
/**
* Returns the JWT {@link Header} or {@code null} if not present.

View File

@ -15,20 +15,19 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.io.Decoder;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoder;
import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.InvalidKeyException;
import io.jsonwebtoken.security.KeyAlgorithm;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.Password;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import io.jsonwebtoken.security.StandardKeyAlgorithms;
import io.jsonwebtoken.security.StandardSecureDigestAlgorithms;
import io.jsonwebtoken.security.WeakKeyException;
import io.jsonwebtoken.security.X509Builder;
import javax.crypto.SecretKey;
import java.security.Key;
@ -70,32 +69,38 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
JwtBuilder setSecureRandom(SecureRandom secureRandom);
/**
* Sets (and replaces) any existing header with the specified header. If you do not want to replace the existing
* header and only want to append to it, use the {@link #setHeaderParams(java.util.Map)} method instead.
* Returns the {@link JwtBuilder.Header} to use to modify the constructed JWT's header name/value pairs as desired.
* When finished, callers may return to JWT construction via the {@link JwtBuilder.Header#and() and()} method.
* For example:
*
* @param header the header to set (and potentially replace any existing header).
* @return the builder for method chaining.
*/
JwtBuilder setHeader(Header<?> header); //replaces any existing header with the specified header.
/**
* Sets (and replaces) any existing header with the specified header. If you do not want to replace the existing
* header and only want to append to it, use the {@link #setHeaderParams(java.util.Map)} method instead.
* <blockquote><pre>
* String jwt = Jwts.builder()
*
* @param header the header to set (and potentially replace any existing header).
* @return the builder for method chaining.
*/
JwtBuilder setHeader(Map<String, ?> header);
/**
* Sets (and replaces) any existing header with the header resulting from the specified builder's
* {@link Builder#build()} result.
* <b>.header()
* .setKeyId("keyId")
* .set(myHeaderMap)
* // ... other header params ...
* .{@link JwtBuilder.Header#and() and()}</b> //return back to the JwtBuilder
*
* @param builder the builder to use to obtain the header
* @return the JwtBuilder for method chaining.
* .setSubject("Joe") // resume JwtBuilder calls
* // ... etc ...
* .compact();</pre></blockquote>
*
* @return the {@link JwtBuilder.Header} to use for header construction.
* @since JJWT_RELEASE_VERSION
*/
JwtBuilder setHeader(Builder<? extends Header<?>> builder);
JwtBuilder.Header header();
/**
* Sets (and replaces) any existing header with the specified name/value pairs. If you do not want to replace the
* existing header and only want to append to it, call
* {@link #header()}{@code .}{@link io.jsonwebtoken.lang.MapMutator#set(Map) set(map)}
* instead.
*
* @param map the name/value pairs to set as (and potentially replace) the constructed JWT header.
* @return the builder for method chaining.
*/
JwtBuilder setHeader(Map<String, ?> map);
/**
* Applies the specified name/value pairs to the header. If a header does not yet exist at the time this method
@ -118,7 +123,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
/**
* Sets the JWT payload to the string's UTF-8-encoded bytes. It is strongly recommended to also set the
* {@link Header#getContentType() contentType} header value so the JWT recipient may inspect that value to
* {@link Header#setContentType(String) contentType} header value so the JWT recipient may inspect that value to
* determine how to convert the byte array to the final data type as desired. In this case, consider using
* {@link #setContent(byte[], String)} instead.
*
@ -173,7 +178,15 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <a href="https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10">JWT specification recommendations</a>.</p>
*
* <p>If for some reason you do not wish to adhere to the JWT specification recommendation, do not call this
* method - instead call {@link #setContent(byte[])} and {@link Header#setContentType(String)} independently.</p>
* method - instead call {@link #setContent(byte[])} and set the header's
* {@link Header#setContentType(String) contentType} independently. For example:</p>
*
* <blockquote><pre>
* Jwts.builder()
* .header().setContentType("application/whatever").and()
* .setContent(byteArray)
* ...
* .build();</pre></blockquote>
*
* <p>If you want the JWT payload to be JSON claims, use the {@link #setClaims(Claims)} or
* {@link #setClaims(java.util.Map)} methods instead.</p>
@ -229,7 +242,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <code>iss</code></a> (issuer) value. A {@code null} value will remove the property from the Claims.
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setIssuer(String) issuer} field with the specified value. This allows you to write
* the Claims {@link Claims#getIssuer() issuer} field with the specified value. This allows you to write
* code like this:</p>
*
* <pre>
@ -238,7 +251,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
*
* <p>instead of this:</p>
* <pre>
* Claims claims = Jwts.claims().setIssuer("Joe");
* Claims claims = Jwts.claims().setIssuer("Joe").build();
* String jwt = Jwts.builder().setClaims(claims).compact();
* </pre>
* <p>if desired.</p>
@ -256,18 +269,16 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <code>sub</code></a> (subject) value. A {@code null} value will remove the property from the Claims.
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setSubject(String) subject} field with the specified value. This allows you to write
* the Claims {@link Claims#getSubject() subject} field with the specified value. This allows you to write
* code like this:</p>
*
* <pre>
* String jwt = Jwts.builder().setSubject("Me").compact();
* </pre>
* <blockquote><pre>
* String jwt = Jwts.builder().setSubject("Me").compact();</pre></blockquote>
*
* <p>instead of this:</p>
* <pre>
* Claims claims = Jwts.claims().setSubject("Me");
* String jwt = Jwts.builder().setClaims(claims).compact();
* </pre>
* <blockquote><pre>
* Claims claims = Jwts.claims().setSubject("Me").build();
* String jwt = Jwts.builder().setClaims(claims).compact();</pre></blockquote>
* <p>if desired.</p>
*
* @param sub the JWT {@code sub} value or {@code null} to remove the property from the Claims map.
@ -283,7 +294,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <code>aud</code></a> (audience) value. A {@code null} value will remove the property from the Claims.
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setAudience(String) audience} field with the specified value. This allows you to write
* the Claims {@link Claims#getAudience() audience} field with the specified value. This allows you to write
* code like this:</p>
*
* <pre>
@ -312,7 +323,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <p>A JWT obtained after this timestamp should not be used.</p>
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setExpiration(java.util.Date) expiration} field with the specified value. This allows
* the Claims {@link Claims#getExpiration() expiration} field with the specified value. This allows
* you to write code like this:</p>
*
* <pre>
@ -341,7 +352,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <p>A JWT obtained before this timestamp should not be used.</p>
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setNotBefore(java.util.Date) notBefore} field with the specified value. This allows
* the Claims {@link Claims#getNotBefore() notBefore} field with the specified value. This allows
* you to write code like this:</p>
*
* <pre>
@ -370,7 +381,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <p>The value is the timestamp when the JWT was created.</p>
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setIssuedAt(java.util.Date) issuedAt} field with the specified value. This allows
* the Claims {@link Claims#getIssuedAt() issuedAt} field with the specified value. This allows
* you to write code like this:</p>
*
* <pre>
@ -401,7 +412,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* assigned to a different data object. The ID can be used to prevent the JWT from being replayed.</p>
*
* <p>This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set
* the Claims {@link Claims#setId(String) id} field with the specified value. This allows
* the Claims {@link Claims#getId() id} field with the specified value. This allows
* you to write code like this:</p>
*
* <pre>
@ -475,67 +486,67 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <td>{@link SecretKey}</td>
* <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA256")</code><sup>1</sup></td>
* <td>256 &lt;= size &lt;= 383 <sup>2</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#HS256 HS256}</td>
* <td>{@link Jwts.SIG#HS256 HS256}</td>
* </tr>
* <tr>
* <td>{@link SecretKey}</td>
* <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA384")</code><sup>1</sup></td>
* <td>384 &lt;= size &lt;= 511</td>
* <td>{@link StandardSecureDigestAlgorithms#HS384 HS384}</td>
* <td>{@link Jwts.SIG#HS384 HS384}</td>
* </tr>
* <tr>
* <td>{@link SecretKey}</td>
* <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA512")</code><sup>1</sup></td>
* <td>512 &lt;= size</td>
* <td>{@link StandardSecureDigestAlgorithms#HS512 HS512}</td>
* <td>{@link Jwts.SIG#HS512 HS512}</td>
* </tr>
* <tr>
* <td>{@link ECKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>256 &lt;= size &lt;= 383 <sup>3</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#ES256 ES256}</td>
* <td>{@link Jwts.SIG#ES256 ES256}</td>
* </tr>
* <tr>
* <td>{@link ECKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>384 &lt;= size &lt;= 520 <sup>4</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#ES384 ES384}</td>
* <td>{@link Jwts.SIG#ES384 ES384}</td>
* </tr>
* <tr>
* <td>{@link ECKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td><b>521</b> &lt;= size <sup>4</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#ES512 ES512}</td>
* <td>{@link Jwts.SIG#ES512 ES512}</td>
* </tr>
* <tr>
* <td>{@link RSAKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>2048 &lt;= size &lt;= 3071 <sup>5,6</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#RS256 RS256}</td>
* <td>{@link Jwts.SIG#RS256 RS256}</td>
* </tr>
* <tr>
* <td>{@link RSAKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>3072 &lt;= size &lt;= 4095 <sup>6</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#RS384 RS384}</td>
* <td>{@link Jwts.SIG#RS384 RS384}</td>
* </tr>
* <tr>
* <td>{@link RSAKey}</td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>4096 &lt;= size <sup>5</sup></td>
* <td>{@link StandardSecureDigestAlgorithms#RS512 RS512}</td>
* <td>{@link Jwts.SIG#RS512 RS512}</td>
* </tr>
* <tr>
* <td><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html">EdECKey</a><sup>7</sup></td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>256</td>
* <td>{@link StandardSecureDigestAlgorithms#Ed25519 Ed25519}</td>
* <td>{@link Jwts.SIG#Ed25519 Ed25519}</td>
* </tr>
* <tr>
* <td><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html">EdECKey</a><sup>7</sup></td>
* <td><code>instanceof {@link PrivateKey}</code></td>
* <td>456</td>
* <td>{@link StandardSecureDigestAlgorithms#Ed448 Ed448}</td>
* <td>{@link Jwts.SIG#Ed448 Ed448}</td>
* </tr>
* </tbody>
* </table>
@ -561,18 +572,18 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* {@code RSAKey}s with key lengths less than 2048 bits will be rejected with a
* {@link WeakKeyException}.</li>
* <li>Technically any RSA key of length &gt;= 2048 bits may be used with the
* {@link StandardSecureDigestAlgorithms#RS256 RS256}, {@link StandardSecureDigestAlgorithms#RS384 RS384}, and
* {@link StandardSecureDigestAlgorithms#RS512 RS512} algorithms, so we assume an RSA signature algorithm based on the key
* {@link Jwts.SIG#RS256 RS256}, {@link Jwts.SIG#RS384 RS384}, and
* {@link Jwts.SIG#RS512 RS512} algorithms, so we assume an RSA signature algorithm based on the key
* length to parallel similar decisions in the JWT specification for HMAC and ECDSA signature algorithms.
* This is not required - just a convenience.</li>
* <li><a href="https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html">EdECKey</a>s
* require JDK &gt;= 15 or BouncyCastle in the runtime classpath.</li>
* </ol>
*
* <p>This implementation does not use the {@link StandardSecureDigestAlgorithms#PS256 PS256},
* {@link StandardSecureDigestAlgorithms#PS384 PS384}, or {@link StandardSecureDigestAlgorithms#PS512 PS512} RSA variants for any
* specified {@link RSAKey} because the the {@link StandardSecureDigestAlgorithms#RS256 RS256},
* {@link StandardSecureDigestAlgorithms#RS384 RS384}, and {@link StandardSecureDigestAlgorithms#RS512 RS512} algorithms are
* <p>This implementation does not use the {@link Jwts.SIG#PS256 PS256},
* {@link Jwts.SIG#PS384 PS384}, or {@link Jwts.SIG#PS512 PS512} RSA variants for any
* specified {@link RSAKey} because the the {@link Jwts.SIG#RS256 RS256},
* {@link Jwts.SIG#RS384 RS384}, and {@link Jwts.SIG#RS512 RS512} algorithms are
* available in the JDK by default while the {@code PS}* variants require either JDK 11 or an additional JCA
* Provider (like BouncyCastle). If you wish to use a {@code PS}* variant with your key, use the
* {@link #signWith(Key, SecureDigestAlgorithm)} method instead.</p>
@ -585,7 +596,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* @return the builder instance for method chaining.
* @throws InvalidKeyException if the Key is insufficient, unsupported, or explicitly disallowed by the JWT
* specification as described above in <em>recommended signature algorithms</em>.
* @see Jwts#SIG
* @see Jwts.SIG
* @see #signWith(Key, SecureDigestAlgorithm)
* @since 0.10.0
*/
@ -686,7 +697,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
*
* <p><b>This has been deprecated since JJWT_RELEASE_VERSION. Use
* {@link #signWith(Key, SecureDigestAlgorithm)} instead</b>. Standard JWA algorithms
* are represented as instances of this new interface in the {@link Jwts#SIG}
* are represented as instances of this new interface in the {@link Jwts.SIG}
* algorithm registry.</p>
*
* <p>Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS.</p>
@ -710,7 +721,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
/**
* Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS.
*
* <p>The {@link Jwts#SIG} registry makes available all standard signature
* <p>The {@link Jwts.SIG} registry makes available all standard signature
* algorithms defined in the JWA specification.</p>
*
* <p>It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.
@ -724,10 +735,10 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for
* the specified algorithm.
* @see #signWith(Key)
* @see Jwts#SIG
* @see Jwts.SIG
* @since JJWT_RELEASE_VERSION
*/
<K extends Key> JwtBuilder signWith(K key, io.jsonwebtoken.security.SecureDigestAlgorithm<? super K, ?> alg) throws InvalidKeyException;
<K extends Key> JwtBuilder signWith(K key, SecureDigestAlgorithm<? super K, ?> alg) throws InvalidKeyException;
/**
* Encrypts the constructed JWT with the specified symmetric {@code key} using the provided {@code enc}ryption
@ -740,12 +751,12 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <ul>
* <li>If the provided {@code key} is a {@link Password Password} instance,
* the {@code KeyAlgorithm} used will be one of the three JWA-standard password-based key algorithms
* ({@link StandardKeyAlgorithms#PBES2_HS256_A128KW PBES2_HS256_A128KW},
* {@link StandardKeyAlgorithms#PBES2_HS384_A192KW PBES2_HS384_A192KW}, or
* {@link StandardKeyAlgorithms#PBES2_HS512_A256KW PBES2_HS512_A256KW}) as determined by the {@code enc} algorithm's
* ({@link Jwts.KEY#PBES2_HS256_A128KW PBES2_HS256_A128KW},
* {@link Jwts.KEY#PBES2_HS384_A192KW PBES2_HS384_A192KW}, or
* {@link Jwts.KEY#PBES2_HS512_A256KW PBES2_HS512_A256KW}) as determined by the {@code enc} algorithm's
* {@link AeadAlgorithm#getKeyBitLength() key length} requirement.</li>
* <li>If the {@code key} is otherwise a standard {@code SecretKey}, the {@code KeyAlgorithm} will be
* {@link StandardKeyAlgorithms#DIRECT}, indicating that {@code key} should be used directly with the
* {@link Jwts.KEY#DIRECT DIRECT}, indicating that {@code key} should be used directly with the
* {@code enc} algorithm. In this case, the {@code key} argument <em>MUST</em> be of sufficient strength to
* use with the specified {@code enc} algorithm, otherwise an exception will be thrown during encryption. If
* desired, secure-random keys suitable for an {@link AeadAlgorithm} may be generated using the algorithm's
@ -754,9 +765,9 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
*
* @param key the symmetric encryption key to use with the {@code enc} algorithm.
* @param enc the {@link AeadAlgorithm} algorithm used to encrypt the JWE, usually one of the JWA-standard
* algorithms accessible via {@link Jwts#ENC}.
* algorithms accessible via {@link Jwts.ENC}.
* @return the JWE builder for method chaining.
* @see Jwts#ENC
* @see Jwts.ENC
*/
JwtBuilder encryptWith(SecretKey key, AeadAlgorithm enc);
@ -777,7 +788,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* </ol>
*
* <p>Most application developers will reference one of the JWA
* {@link Jwts#KEY standard key algorithms} and {@link Jwts#ENC standard encryption algorithms}
* {@link Jwts.KEY standard key algorithms} and {@link Jwts.ENC standard encryption algorithms}
* when invoking this method, but custom implementations are also supported.</p>
*
* @param <K> the type of key that must be used with the specified {@code keyAlg} instance.
@ -786,13 +797,13 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* {@code enc} algorithm
* @param enc the {@link AeadAlgorithm} algorithm used to encrypt the JWE
* @return the JWE builder for method chaining.
* @see Jwts#ENC
* @see Jwts#KEY
* @see Jwts.ENC
* @see Jwts.KEY
*/
<K extends Key> JwtBuilder encryptWith(K key, KeyAlgorithm<? super K, ?> keyAlg, AeadAlgorithm enc);
/**
* Compresses the JWT payload using the specified {@link CompressionCodec}.
* Compresses the JWT payload using the specified {@link CompressionAlgorithm}.
*
* <p>If your compact JWTs are large, and you want to reduce their total size during network transmission, this
* can be useful. For example, when embedding JWTs in URLs, some browsers may not support URLs longer than a
@ -809,12 +820,12 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <p>Compression when creating JWE tokens however should be universally accepted for any
* library that supports JWE.</p>
*
* @param codec implementation of the {@link CompressionCodec} to be used.
* @param alg implementation of the {@link CompressionAlgorithm} to be used.
* @return the builder for method chaining.
* @see io.jsonwebtoken.CompressionCodecs
* @since 0.6.0
* @see Jwts.ZIP
* @since JJWT_RELEASE_VERSION
*/
JwtBuilder compressWith(CompressionCodec codec);
JwtBuilder compressWith(CompressionAlgorithm alg);
/**
* Perform Base64Url encoding with the specified Encoder.
@ -850,4 +861,21 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* @return A compact URL-safe JWT string.
*/
String compact();
/**
* Editable header for use with a {@link JwtBuilder} that supports method chaining for any/all
* standard JWT, JWS and JWE header parameters. Once header parameters are configured, the associated
* {@link JwtBuilder} may be obtained with the {@link #and() and()} method for continued configuration.
*
* @since JJWT_RELEASE_VERSION
*/
interface Header extends JweHeaderMutator<Header>, X509Builder<Header> {
/**
* Returns the associated JwtBuilder for continued configuration.
*
* @return the associated JwtBuilder for continued configuration.
*/
JwtBuilder and();
}
}

View File

@ -26,7 +26,7 @@ public interface JwtHandler<T> {
/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
* an Unprotected content JWT. An Unprotected content JWT has a byte array payload that is not
* an unprotected content JWT. An unprotected content JWT has a byte array payload that is not
* cryptographically signed or encrypted. If the JWT creator set the (optional)
* {@link Header#getContentType() contentType} header value, the application may inspect that value to determine
* how to convert the byte array to the final content type as desired.
@ -34,7 +34,7 @@ public interface JwtHandler<T> {
* @param jwt the parsed Unprotected content JWT
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
*/
T onContentJwt(Jwt<UnprotectedHeader, byte[]> jwt);
T onContentJwt(Jwt<Header, byte[]> jwt);
/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
@ -43,7 +43,7 @@ public interface JwtHandler<T> {
* @param jwt the parsed claims JWT
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
*/
T onClaimsJwt(Jwt<UnprotectedHeader, Claims> jwt);
T onClaimsJwt(Jwt<Header, Claims> jwt);
/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is

View File

@ -31,12 +31,12 @@ package io.jsonwebtoken;
public abstract class JwtHandlerAdapter<T> implements JwtHandler<T> {
@Override
public T onContentJwt(Jwt<UnprotectedHeader, byte[]> jwt) {
public T onContentJwt(Jwt<Header, byte[]> jwt) {
throw new UnsupportedJwtException("Unprotected content JWTs are not supported.");
}
@Override
public T onClaimsJwt(Jwt<UnprotectedHeader, Claims> jwt) {
public T onClaimsJwt(Jwt<Header, Claims> jwt) {
throw new UnsupportedJwtException("Unprotected Claims JWTs are not supported.");
}

View File

@ -21,6 +21,7 @@ import io.jsonwebtoken.security.SecurityException;
import io.jsonwebtoken.security.SignatureException;
import java.security.Key;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
@ -344,18 +345,17 @@ public interface JwtParser {
*
* <p><b>Default Support</b></p>
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
* <p>JJWT's default {@link JwtParser} implementation supports both the {@link Jwts.ZIP#DEF DEFLATE}
* and {@link Jwts.ZIP#GZIP GZIP} algorithms by default - you do not need to
* specify a {@code CompressionCodecResolver} in these cases.</p>
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you may implement
* your own {@link CompressionCodecResolver} and specify that via this method and also when
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} JWTs.</p>
* {@link io.jsonwebtoken.JwtBuilder#compressWith(io.jsonwebtoken.io.CompressionAlgorithm) building} JWTs.</p>
*
* @param compressionCodecResolver the compression codec resolver used to decompress the JWT payload.
* @return the parser for method chaining.
* @since 0.6.0
* @deprecated in favor of {@link JwtParserBuilder#setCompressionCodecLocator(Locator)}.
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link JwtParserBuilder#addCompressionAlgorithms(Collection)}.
* To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an
* immutable JwtParser.
* <p><b>NOTE: this method will be removed before version 1.0</b>
@ -529,7 +529,7 @@ public interface JwtParser {
* @see #parse(String)
* @since 0.2
*/
Jwt<UnprotectedHeader, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException,
Jwt<Header, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException,
SignatureException, SecurityException, IllegalArgumentException;
/**
@ -559,7 +559,7 @@ public interface JwtParser {
* @see #parse(String)
* @since 0.2
*/
Jwt<UnprotectedHeader, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException,
Jwt<Header, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException,
MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;
/**

View File

@ -15,14 +15,13 @@
*/
package io.jsonwebtoken;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.io.Decoder;
import io.jsonwebtoken.io.Deserializer;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.KeyAlgorithm;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import io.jsonwebtoken.security.StandardKeyAlgorithms;
import io.jsonwebtoken.security.StandardSecureDigestAlgorithms;
import java.security.Key;
import java.security.Provider;
@ -57,7 +56,7 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
* @return the builder for method chaining.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5">Unsecured JWS Security Considerations</a>
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6">Using the Algorithm &quot;none&quot;</a>
* @see StandardSecureDigestAlgorithms#NONE
* @see Jwts.SIG#NONE
* @see #enableUnsecuredDecompression()
* @since JJWT_RELEASE_VERSION
*/
@ -86,7 +85,7 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5">Unsecured JWS Security Considerations</a>
* @see <a href="https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf">In the
* Compression Hornets Nest: A Security Study of Data Compression in Network Services</a>
* @see StandardSecureDigestAlgorithms#NONE
* @see Jwts.SIG#NONE
* @see #enableUnsecuredJws()
* @since JJWT_RELEASE_VERSION
*/
@ -427,27 +426,24 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
/**
* Adds the specified compression codecs to the parser's total set of supported compression codecs,
* overwriting any previously-added compression codecs with the same {@link CompressionCodec#getId() id}s. If the
* parser encounters a JWT {@code zip} header value that matches a compression codec's
* {@link CompressionCodec#getId() CompressionCodec.getId()}, that codec will be used for decompression.
* Adds the specified compression algorithms to the parser's total set of supported compression algorithms,
* overwriting any previously-added compression algorithms with the same {@link CompressionAlgorithm#getId() id}s.
* If the parser encounters a JWT {@code zip} header value that matches a compression algorithm's
* {@link CompressionAlgorithm#getId() id}, that algorithm will be used for decompression.
*
* <p>There may be only one registered {@code CompressionCodec} per {@code id}, and the {@code codecs}
* collection is added in iteration order; if a duplicate id is found when iterating the {@code codecs}
* <p>There may be only one registered {@code CompressionAlgorithm} per {@code id}, and the {@code algs}
* collection is added in iteration order; if a duplicate id is found when iterating the {@code algs}
* collection, the later element will evict any previously-added algorithm with the same {@code id}.</p>
*
* <p>Finally, {@link CompressionCodecs#DEFLATE} and {@link CompressionCodecs#GZIP} are added last,
* <em>after</em> those in the {@code codecs} collection, to ensure that JWA standard algorithms cannot be
* <p>Finally, {@link Jwts.ZIP#DEF} and {@link Jwts.ZIP#GZIP} algorithms are added last,
* <em>after</em> those in the {@code algs} collection, to ensure that JWA standard algorithms cannot be
* accidentally replaced.</p>
*
* <p>This method is a simpler alternative than creating and registering a custom locator via the
* {@link #setCompressionCodecLocator(Locator)} method.</p>
*
* @param codecs collection of compression codecs to add to the parser's total set of supported compression codecs.
* @param algs collection of compression algorithms to add to the parser's total set of supported compression algorithms.
* @return the builder for method chaining.
* @since JJWT_RELEASE_VERSION
*/
JwtParserBuilder addCompressionCodecs(Collection<? extends CompressionCodec> codecs);
JwtParserBuilder addCompressionAlgorithms(Collection<? extends CompressionAlgorithm> algs);
/**
* Adds the specified AEAD encryption algorithms to the parser's total set of supported encryption algorithms,
@ -457,7 +453,7 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
* collection is added in iteration order; if a duplicate id is found when iterating the {@code encAlgs}
* collection, the later element will evict any previously-added algorithm with the same {@code id}.</p>
*
* <p>Finally, the {@link Jwts#ENC JWA standard encryption algorithms} are added last,
* <p>Finally, the {@link Jwts.ENC JWA standard encryption algorithms} are added last,
* <em>after</em> those in the {@code encAlgs} collection, to ensure that JWA standard algorithms cannot be
* accidentally replaced.</p>
*
@ -478,7 +474,7 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
* {@code sigAlgs} collection, the later element will evict any previously-added algorithm with the same
* {@code id}.</p>
*
* <p>Finally, the {@link Jwts#SIG JWA standard signature and MAC algorithms} are
* <p>Finally, the {@link Jwts.SIG JWA standard signature and MAC algorithms} are
* added last, <em>after</em> those in the {@code sigAlgs} collection, to ensure that JWA standard algorithms
* cannot be accidentally replaced.</p>
*
@ -497,7 +493,7 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
* collection is added in iteration order; if a duplicate id is found when iterating the {@code keyAlgs}
* collection, the later element will evict any previously-added algorithm with the same {@code id}.</p>
*
* <p>Finally, the {@link StandardKeyAlgorithms#values() JWA standard key management algorithms}
* <p>Finally, the {@link Jwts.KEY#get() JWA standard key management algorithms}
* are added last, <em>after</em> those in the {@code keyAlgs} collection, to ensure that JWA standard algorithms
* cannot be accidentally replaced.</p>
*
@ -509,70 +505,35 @@ public interface JwtParserBuilder extends Builder<JwtParser> {
JwtParserBuilder addKeyAlgorithms(Collection<? extends KeyAlgorithm<?, ?>> keyAlgs);
/**
* <p><b>Deprecated as of JJWT JJWT_RELEASE_VERSION. This method will be removed before the 1.0 release.</b></p>
*
* <p>This method has been deprecated as of JJWT version JJWT_RELEASE_VERSION because it imposed unnecessary
* implementation requirements on application developers when simply adding to a compression algorithm collection
* would suffice. Use the {@link #addCompressionAlgorithms(Collection)} method instead to add
* any custom algorithm implementations without needing to also implement a Locator implementation.</p>
*
* <p><b>Previous Documentation</b></p>
* <p>
* Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to
* decompress the JWT body. If the parsed JWT is not compressed, this resolver is not used.
*
* <p><b>NOTE:</b> Compression is not defined by the JWS Specification - only the JWE Specification - and it is
* <p><b>WARNING:</b> Compression is not defined by the JWS Specification - only the JWE Specification - and it is
* not expected that other libraries (including JJWT versions &lt; 0.6.0) are able to consume a compressed JWS
* body correctly. This method is only useful if the compact JWS was compressed with JJWT &gt;= 0.6.0 or
* another library that you know implements the same behavior.</p>
* body correctly.</p>
*
* <p><b>Default Support</b></p>
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
* <p>JJWT's default {@link JwtParser} implementation supports both the {@link Jwts.ZIP#DEF DEF}
* and {@link Jwts.ZIP#GZIP GZIP} algorithms by default - you do not need to
* specify a {@code CompressionCodecResolver} in these cases.</p>
*
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must
* implement your own {@link CompressionCodecResolver} and specify that via this method and also when
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} JWTs.</p>
*
* @param compressionCodecResolver the compression codec resolver used to decompress the JWT body.
* @return the parser builder for method chaining.
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #setCompressionCodecLocator(Locator)} to use the
* congruent {@code Locator} concept used elsewhere (such as {@link #setKeyLocator(Locator)}).
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #addCompressionAlgorithms(Collection)}.
*/
@Deprecated
JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver);
/**
* Sets the {@link CompressionCodec} {@code Locator} used to acquire the {@code CompressionCodec} that should be
* used to decompress the JWT body.
*
* <p><b>NOTE:</b> Compression is not defined by the JWS Specification - only the JWE Specification - and it is
* not expected that other libraries (including JJWT versions &lt; 0.6.0) are able to consume a compressed JWS
* body correctly. This method is only useful if the compact JWS was compressed with JJWT &gt;= 0.6.0 or
* another library that you know implements the same behavior.</p>
*
* <p><b>Simple Registration</b></p>
*
* <p>If a CompressionCodec can be resolved in the JWT Header via a simple {@code zip} header value lookup, it is
* recommended to call the {@link #addCompressionCodecs(Collection)} method instead of this one. That method
* will add the codec to the total set of supported codecs and lookup will achieved by matching the
* {@link CompressionCodec#getId() CompressionCodec.getId()} against the {@code zip} header value automatically.</p>
*
* <p>You only need to call this method with a custom locator if compression codec lookup cannot be based on the
* {@code zip} header value.</p>
*
* <p><b>Default Support</b></p>
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
* specify a {@code CompressionCodec} {@link Locator} in these cases.</p>
*
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, and
* {@link #addCompressionCodecs(Collection)} is not sufficient, you must
* implement your own {@code CompressionCodec} {@link Locator} and specify that via this method and also when
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} JWTs.</p>
*
* @param locator the compression codec locator used to decompress the JWT body.
* @return the parser builder for method chaining.
* @since JJWT_RELEASE_VERSION
*/
JwtParserBuilder setCompressionCodecLocator(Locator<CompressionCodec> locator);
/**
* Perform Base64Url decoding with the specified Decoder
*

File diff suppressed because it is too large Load Diff

View File

@ -37,5 +37,5 @@ public interface Locator<T> {
* {@link JweHeader} depending on if the respective JWT is an unprotected JWT, JWS or JWE.
* @return an object referenced in the specified {@code header}, or {@code null} if the object couldn't be found.
*/
T locate(Header<?> header);
T locate(Header header);
}

View File

@ -19,7 +19,7 @@ import io.jsonwebtoken.lang.Assert;
/**
* Adapter pattern implementation for the {@link Locator} interface. Subclasses can override any of the
* {@link #locate(UnprotectedHeader)}, {@link #locate(ProtectedHeader)}, {@link #locate(JwsHeader)}, or
* {@link #doLocate(Header)}, {@link #locate(ProtectedHeader)}, {@link #locate(JwsHeader)}, or
* {@link #locate(JweHeader)} methods for type-specific logic if desired when the encountered header is an
* unprotected JWT, or an integrity-protected JWT (either a JWS or JWE).
*
@ -34,25 +34,23 @@ public abstract class LocatorAdapter<T> implements Locator<T> {
}
/**
* Inspects the specified header, and delegates to the {@link #locate(UnprotectedHeader)} method if the header
* is an {@link UnprotectedHeader} or the {@link #locate(ProtectedHeader)} method if the header is either a
* {@link JwsHeader} or {@link JweHeader}.
* Inspects the specified header, and delegates to the {@link #locate(ProtectedHeader)} method if the header
* is protected (either a {@link JwsHeader} or {@link JweHeader}), or the {@link #doLocate(Header)} method
* if the header is not integrity protected.
*
* @param header the JWT header to inspect; may be an instance of {@link UnprotectedHeader}, {@link JwsHeader} or
* @param header the JWT header to inspect; may be an instance of {@link Header}, {@link JwsHeader}, or
* {@link JweHeader} depending on if the respective JWT is an unprotected JWT, JWS or JWE.
* @return an object referenced in the specified header, or {@code null} if the referenced object cannot be found
* or does not exist.
*/
@Override
public final T locate(Header<?> header) {
public final T locate(Header header) {
Assert.notNull(header, "Header cannot be null.");
if (header instanceof ProtectedHeader<?>) {
ProtectedHeader<?> protectedHeader = (ProtectedHeader<?>) header;
if (header instanceof ProtectedHeader) {
ProtectedHeader protectedHeader = (ProtectedHeader) header;
return locate(protectedHeader);
} else {
Assert.isInstanceOf(UnprotectedHeader.class, header, "Unrecognized Header type.");
return locate((UnprotectedHeader) header);
}
return doLocate(header);
}
/**
@ -65,7 +63,7 @@ public abstract class LocatorAdapter<T> implements Locator<T> {
* @return an object referenced in the specified {@link ProtectedHeader}, or {@code null} if the referenced
* object cannot be found or does not exist.
*/
protected T locate(ProtectedHeader<?> header) {
protected T locate(ProtectedHeader header) {
if (header instanceof JwsHeader) {
return locate((JwsHeader) header);
} else {
@ -99,14 +97,15 @@ public abstract class LocatorAdapter<T> implements Locator<T> {
}
/**
* Returns an object referenced in the specified Unprotected JWT header, or {@code null} if the referenced
* Returns an object referenced in the specified unprotected JWT header, or {@code null} if the referenced
* object cannot be found or does not exist. Default implementation simply returns {@code null}.
*
* @param header the header of an encountered JWE.
* @return an object referenced in the specified Unprotected JWT header, or {@code null} if the referenced
* @param header the header of an encountered JWT.
* @return an object referenced in the specified unprotected JWT header, or {@code null} if the referenced
* object cannot be found or does not exist.
*/
protected T locate(UnprotectedHeader header) {
@SuppressWarnings("unused")
protected T doLocate(Header header) {
return null;
}
}

View File

@ -32,7 +32,7 @@ public class MissingClaimException extends InvalidClaimException {
* @param claimValue the value of the claim that could not be validated
* @param message the message explaining why the exception is thrown.
*/
public MissingClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message) {
public MissingClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {
super(header, claims, claimName, claimValue, message);
}
@ -46,8 +46,10 @@ public class MissingClaimException extends InvalidClaimException {
* @param claimValue the value of the claim that could not be validated
* @param message the message explaining why the exception is thrown.
* @param cause the underlying cause that resulted in this exception being thrown.
* @deprecated since JJWT_RELEASE_VERSION since it is not used in JJWT's codebase
*/
public MissingClaimException(Header<?> header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
@Deprecated
public MissingClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {
super(header, claims, claimName, claimValue, message, cause);
}
}

View File

@ -29,7 +29,7 @@ public class PrematureJwtException extends ClaimJwtException {
* @param claims jwt claims (body)
* @param message the message explaining why the exception is thrown.
*/
public PrematureJwtException(Header<?> header, Claims claims, String message) {
public PrematureJwtException(Header header, Claims claims, String message) {
super(header, claims, message);
}
@ -41,8 +41,10 @@ public class PrematureJwtException extends ClaimJwtException {
* @param message exception message
* @param cause cause
* @since 0.5
* @deprecated since JJWT_RELEASE_VERSION since it is not used in JJWT's codebase
*/
public PrematureJwtException(Header<?> header, Claims claims, String message, Throwable cause) {
@Deprecated
public PrematureJwtException(Header header, Claims claims, String message, Throwable cause) {
super(header, claims, message, cause);
}
}

View File

@ -24,12 +24,11 @@ import java.util.Set;
/**
* A JWT header that is integrity protected, either by JWS digital signature or JWE AEAD encryption.
*
* @param <T> The exact header subtype returned during mutation (setter) operations.
* @see JwsHeader
* @see JweHeader
* @since JJWT_RELEASE_VERSION
*/
public interface ProtectedHeader<T extends ProtectedHeader<T>> extends Header<T>, ProtectedHeaderMutator<T>, X509Accessor {
public interface ProtectedHeader extends Header, X509Accessor {
/**
* Returns the {@code jku} (JWK Set URL) value that refers to a

View File

@ -0,0 +1,37 @@
/*
* 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;
import io.jsonwebtoken.security.DigestSupplier;
/**
* A {@code ProtectedJwt} is a {@link Jwt} that is integrity protected via a cryptographic algorithm that produces
* a cryptographic digest, such as a MAC, Digital Signature or Authentication Tag.
*
* <p><b>Cryptographic Digest</b></p>
* <p>This interface extends DigestSupplier to make available the {@code ProtectedJwt}'s associated cryptographic
* digest:</p>
* <ul>
* <li>If the JWT is a {@link Jws}, {@link #getDigest() getDigest() } returns the JWS signature.</li>
* <li>If the JWT is a {@link Jwe}, {@link #getDigest() getDigest() } returns the AAD Authentication Tag.</li>
* </ul>
*
* @param <H> the type of the JWT protected header
* @param <P> the type of the JWT payload, either a content byte array or a {@link Claims} instance.
* @since JJWT_RELEASE_VERSION
*/
public interface ProtectedJwt<H extends ProtectedHeader, P> extends Jwt<H, P>, DigestSupplier {
}

View File

@ -18,7 +18,6 @@ package io.jsonwebtoken;
import io.jsonwebtoken.security.InvalidKeyException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import io.jsonwebtoken.security.StandardSecureDigestAlgorithms;
import io.jsonwebtoken.security.WeakKeyException;
import javax.crypto.SecretKey;
@ -35,7 +34,7 @@ import java.util.List;
* <a href="https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-31">JSON Web Algorithms</a> specification.
*
* @since 0.1
* @deprecated since JJWT_RELEASE_VERSION; use {@link StandardSecureDigestAlgorithms} instead.
* @deprecated since JJWT_RELEASE_VERSION; use {@link Jwts.SIG} instead.
*/
@Deprecated
public enum SignatureAlgorithm {

View File

@ -0,0 +1,64 @@
/*
* 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.io;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.Jwts;
import java.util.Collection;
/**
* Compresses and decompresses byte arrays.
*
* <p><b>&quot;zip&quot; identifier</b></p>
*
* <p>{@code CompressionAlgorithm} extends {@code Identifiable}; the value returned from
* {@link Identifiable#getId() getId()} will be used as the JWT
* <a href="https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3"><code>zip</code></a> header value.</p>
*
* <p><b>Custom Implementations</b></p>
* <p>A custom implementation of this interface may be used when creating a JWT by calling the
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionAlgorithm)} method. To ensure that parsing is
* possible, the parser must be aware of the implementation by calling
* {@link io.jsonwebtoken.JwtParserBuilder#addCompressionAlgorithms(Collection)} during parser construction.</p>
*
* @see Jwts.ZIP#DEF
* @see Jwts.ZIP#GZIP
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3">JSON Web Encryption Compression Algorithms Registry</a>
* @since JJWT_RELEASE_VERSION
*/
public interface CompressionAlgorithm extends Identifiable {
/**
* Compresses the specified byte array, returning the compressed byte array result.
*
* @param content bytes to compress
* @return compressed bytes
* @throws CompressionException if the specified byte array cannot be compressed.
*/
byte[] compress(byte[] content) throws CompressionException;
/**
* Decompresses the specified compressed byte array, returning the decompressed byte array result. The
* specified byte array must already be in compressed form.
*
* @param compressed compressed bytes
* @return decompressed bytes
* @throws CompressionException if the specified byte array cannot be decompressed.
*/
byte[] decompress(byte[] compressed) throws CompressionException;
}

View File

@ -495,6 +495,8 @@ public final class Assert {
*
* @param value value to assert is not null
* @param msg exception message to use if {@code value} is null
* @param <T> value type
* @return the non-null value
* @throws IllegalStateException with the specified {@code msg} if {@code value} is null.
* @since JJWT_RELEASE_VERSION
*/

View File

@ -18,7 +18,7 @@ package io.jsonwebtoken.lang;
/**
* Type-safe interface that reflects the <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern</a>.
*
* @param <T> The type of object that will be created each time {@link #build()} is invoked.
* @param <T> The type of object that will be created when {@link #build()} is invoked.
* @since JJWT_RELEASE_VERSION
*/
public interface Builder<T> {

View File

@ -29,37 +29,45 @@ import java.util.Map;
*/
public interface MapMutator<K, V, T extends MapMutator<K, V, T>> {
/**
* Removes all entries from the map. The map will be empty after this call returns.
* <p>This method is the same as {@link Map#clear Map.clear}, but instead returns the mutator instance for
* method chaining.</p>
*
* @return the mutator/builder for method chaining.
*/
T empty();
/**
* Sets the specified name/value pair in the map. A {@code null} or empty value will remove the property
* from the map entirely.
* <p>This method is the same as {@link Map#put Map.put}, but instead returns the mutator instance for
* method chaining.</p>
*
* @param key the map key
* @param value the value to set for the specified header parameter name
* @return the mutator/builder for method chaining.
*/
T put(K key, V value);
/**
* Removes the map entry with the specified key
*
* @param key the key for the map entry to remove.
* @return the mutator/builder for method chaining.
*/
T remove(K key);
T set(K key, V value);
/**
* Sets the specified name/value pairs in the map. If any name has a {@code null} or empty value, that
* map entry will be removed from the map entirely.
* <p>This method is the same as {@link Map#putAll Map.putAll}, but instead returns the mutator instance for
* method chaining.</p>
*
* @param m the map to add
* @return the mutator/builder for method chaining.
*/
T putAll(Map<? extends K, ? extends V> m);
T set(Map<? extends K, ? extends V> m);
/**
* Removes all entries from the map. The map will be empty after this call returns.
* Removes the map entry with the specified key.
* <p>This method is the same as {@link Map#remove Map.remove}, but instead returns the mutator instance for
* method chaining.</p>
*
* @param key the key for the map entry to remove.
* @return the mutator/builder for method chaining.
*/
T clear();
T delete(K key);
}

View File

@ -15,41 +15,37 @@
*/
package io.jsonwebtoken.lang;
import java.util.Collection;
import java.util.Map;
/**
* An immutable read-only repository of key-value pairs.
* An immutable (read-only) repository of key-value pairs. In addition to {@link Map} read methods, this interface also
* provides guaranteed/expected lookup via the {@link #forKey(Object)} method.
*
* <p><b>Immutability</b></p>
*
* <p>Registries are immutable and cannot be changed. {@code Registry} extends the
* {@link Map} interface purely out of convenience: to allow easy key/value
* pair access and iteration, and other conveniences provided by the Map interface, as well as for seamless use with
* existing Map-based APIs. Attempting to call any of
* the {@link Map} interface's mutation methods however (such as {@link Map#put(Object, Object) put},
* {@link Map#remove(Object) remove}, {@link Map#clear() clear}, etc) will throw an
* {@link UnsupportedOperationException}.</p>
*
* @param <K> key type
* @param <V> value type
* @since JJWT_RELEASE_VERSION
*/
public interface Registry<K, V> {
/**
* Returns all registry values as a read-only collection.
*
* @return all registry values as a read-only collection.
*/
Collection<V> values();
public interface Registry<K, V> extends Map<K, V> {
/**
* Returns the value assigned the specified key or throws an {@code IllegalArgumentException} if there is no
* associated value. If a value is not required, consider using the {@link #find(Object)} method instead.
* associated value. If a value is not required, consider using the {@link #get(Object)} method instead.
*
* @param key the registry key assigned to the required value
* @return the value assigned the specified key
* @throws IllegalArgumentException if there is no value assigned the specified key
* @see #find(Object)
*/
V get(K key) throws IllegalArgumentException;
/**
* Returns the value assigned the specified key or {@code null} if there is no associated value.
*
* @param key the registry key assigned to the required value
* @return the value assigned the specified key or {@code null} if there is no associated value.
* @see #get(Object)
*/
V find(K key);
V forKey(K key) throws IllegalArgumentException;
}

View File

@ -231,6 +231,32 @@ public final class Strings {
return str;
}
/**
* Returns the specified string's UTF-8 bytes, or {@code null} if the string is {@code null}.
*
* @param s the string to obtain UTF-8 bytes
* @return the specified string's UTF-8 bytes, or {@code null} if the string is {@code null}.
* @since JJWT_RELEASE_VERSION
*/
public static byte[] utf8(String s) {
byte[] bytes = null;
if (s != null) {
bytes = s.getBytes(UTF_8);
}
return bytes;
}
/**
* Returns {@code new String(utf8Bytes, StandardCharsets.UTF_8)}.
*
* @param utf8Bytes UTF-8 bytes to use with the {@code String} constructor.
* @return {@code new String(utf8Bytes, StandardCharsets.UTF_8)}.
* @since JJWT_RELEASE_VERSION
*/
public static String utf8(byte[] utf8Bytes) {
return new String(utf8Bytes, UTF_8);
}
/**
* Returns a String representation (1s and 0s) of the specified byte.
*
@ -1281,6 +1307,7 @@ public final class Strings {
* <p>To this:</p>
* <blockquote><pre>
* nespace(sb).append(nextWord);</pre></blockquote>
*
* @param sb the string builder to append a space to if non-empty
* @return the string builder argument for method chaining.
* @since JJWT_RELEASE_VERSION

View File

@ -26,7 +26,7 @@ import javax.crypto.SecretKey;
* Per <a href="https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.2">JWE RFC 7516, Section 4.1.2</a>, all JWEs
* <em>MUST</em> use an AEAD algorithm to encrypt or decrypt the JWE payload/content. Consequently, all
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">JWA &quot;enc&quot; algorithms</a> are AEAD
* algorithms, and they are accessible as concrete instances via {@link Jwts#ENC}.
* algorithms, and they are accessible as concrete instances via {@link Jwts.ENC}.
*
* <p><b>&quot;enc&quot; identifier</b></p>
*
@ -57,7 +57,7 @@ import javax.crypto.SecretKey;
* <p>The resulting {@code key} is guaranteed to have the correct algorithm parameters and strength/length necessary for
* that exact {@code aeadAlgorithm} instance.</p>
*
* @see Jwts#ENC
* @see Jwts.ENC
* @see Identifiable#getId()
* @see KeyLengthSupplier
* @see KeyBuilderSupplier

View File

@ -40,18 +40,18 @@ import java.security.PublicKey;
* <tbody>
* <tr>
* <td>{@link HashAlgorithm}</td>
* <td>{@link StandardHashAlgorithms}</td>
* <td>{@link Jwks.HASH}</td>
* <td>Unsecured (unkeyed), does not require a key to compute or verify digests.</td>
* </tr>
* <tr>
* <td>{@link MacAlgorithm}</td>
* <td>{@link StandardSecureDigestAlgorithms}</td>
* <td>{@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}</td>
* <td>Requires a {@link SecretKey} to both compute and verify digests (aka
* &quot;Message Authentication Codes&quot;).</td>
* </tr>
* <tr>
* <td>{@link SignatureAlgorithm}</td>
* <td>{@link StandardSecureDigestAlgorithms}</td>
* <td>{@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}</td>
* <td>Requires a {@link PrivateKey} to compute and {@link PublicKey} to verify digests
* (aka &quot;Digital Signatures&quot;).</td>
* </tr>
@ -71,8 +71,8 @@ import java.security.PublicKey;
*
* @param <R> the type of {@link Request} used when computing a digest.
* @param <V> the type of {@link VerifyDigestRequest} used when verifying a digest.
* @see StandardHashAlgorithms
* @see StandardSecureDigestAlgorithms
* @see Jwks.HASH
* @see io.jsonwebtoken.Jwts.SIG Jwts.SIG
* @since JJWT_RELEASE_VERSION
*/
public interface DigestAlgorithm<R extends Request<byte[]>, V extends VerifyDigestRequest> extends Identifiable {

View File

@ -34,9 +34,9 @@ import io.jsonwebtoken.Identifiable;
*
* <p>Constant definitions and utility methods for common (<em>but not all</em>)
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> are available via the {@link StandardHashAlgorithms} singleton.</p>
* Algorithms</a> are available via {@link Jwks.HASH}.</p>
*
* @see StandardHashAlgorithms
* @see Jwks.HASH
* @since JJWT_RELEASE_VERSION
*/
public interface HashAlgorithm extends DigestAlgorithm<Request<byte[]>, VerifyDigestRequest> {

View File

@ -82,10 +82,10 @@ public interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilde
/**
* Sets the JWK's {@link #setId(String) kid} value to be the Base64URL-encoding of its {@code SHA-256}
* {@link Jwk#thumbprint(HashAlgorithm) thumbprint}. That is, the constructed JWK's {@code kid} value will equal
* <code>jwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}({@link Jwks#HASH}.{@link StandardHashAlgorithms#SHA256 SHA256}).{@link JwkThumbprint#toString() toString()}</code>.
* <code>jwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}({@link Jwks.HASH}.{@link Jwks.HASH#SHA256 SHA256}).{@link JwkThumbprint#toString() toString()}</code>.
*
* <p>This is a convenience method that delegates to {@link #setIdFromThumbprint(HashAlgorithm)} using
* {@link Jwks#HASH}{@code .}{@link StandardHashAlgorithms#SHA256 SHA256}.</p>
* {@link Jwks.HASH}{@code .}{@link Jwks.HASH#SHA256 SHA256}.</p>
*
* @return the builder for method chaining.
*/
@ -99,7 +99,7 @@ public interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilde
*
* @param alg the hash algorithm to use to compute the thumbprint.
* @return the builder for method chaining.
* @see StandardHashAlgorithms
* @see Jwks.HASH
*/
T setIdFromThumbprint(HashAlgorithm alg);

View File

@ -15,7 +15,9 @@
*/
package io.jsonwebtoken.security;
import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.lang.Classes;
import io.jsonwebtoken.lang.Registry;
/**
* Utility methods for creating
@ -25,10 +27,10 @@ import io.jsonwebtoken.lang.Classes;
* <p>Standard <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>
* are available via the {@link #HASH} registry constant to allow for easy code-completion in IDEs. For example, when
* are available via the {@link Jwks.HASH} registry constants to allow for easy code-completion in IDEs. For example, when
* typing:</p>
* <blockquote><pre>
* Jwts.{@link #HASH}.// press hotkeys to suggest individual hash algorithms or utility methods</pre></blockquote>
* Jwks.{@link Jwks.HASH HASH}.// press hotkeys to suggest individual hash algorithms or utility methods</pre></blockquote>
*
* @see #builder()
* @since JJWT_RELEASE_VERSION
@ -43,27 +45,107 @@ public final class Jwks {
private static final String PARSERBUILDER_CLASSNAME = "io.jsonwebtoken.impl.security.DefaultJwkParserBuilder";
/**
* Registry of various (<em>but not all</em>)
* Various (<em>but not all</em>)
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>. For
* example:
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>.
* Each algorithm is made available as a ({@code public static final}) constant for direct type-safe
* reference in application code. For example:
* <blockquote><pre>
* Jwks.{@link Jwks#builder}()
* // ... etc ...
* .{@link JwkBuilder#setIdFromThumbprint(HashAlgorithm) setIdFromThumbprint}(Jwts.HASH.{@link StandardHashAlgorithms#SHA256 SHA256}) // &lt;---
* .{@link JwkBuilder#setIdFromThumbprint(HashAlgorithm) setIdFromThumbprint}(Jwts.HASH.{@link Jwks.HASH#SHA256 SHA256}) // &lt;---
* .build()</pre></blockquote>
* <p>or</p>
* <blockquote><pre>
* HashAlgorithm hashAlg = Jwks.HASH.{@link StandardHashAlgorithms#SHA256 SHA256};
* HashAlgorithm hashAlg = Jwks.HASH.{@link Jwks.HASH#SHA256 SHA256};
* {@link JwkThumbprint} thumbprint = aJwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}(hashAlg);
* String <a href="https://www.rfc-editor.org/rfc/rfc9278#section-3">rfcMandatoryPrefix</a> = "urn:ietf:params:oauth:jwk-thumbprint:" + hashAlg.getId();
* assert thumbprint.toURI().toString().startsWith(rfcMandatoryPrefix);
* </pre></blockquote>
* <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>
*
* @see #get()
* @since JJWT_RELEASE_VERSION
*/
public static final StandardHashAlgorithms HASH = StandardHashAlgorithms.get();
public static final class HASH {
private static final String IMPL_CLASSNAME = "io.jsonwebtoken.impl.security.StandardHashAlgorithms";
private static final Registry<String, HashAlgorithm> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);
/**
* Returns a registry of various (<em>but not all</em>)
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>.
*
* @return a registry of various (<em>but not all</em>)
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>.
*/
public static Registry<String, HashAlgorithm> get() {
return REGISTRY;
}
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-256}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-256} {@code MessageDigest} algorithm.
*/
public static final HashAlgorithm SHA256 = get().forKey("sha-256");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-384}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-384} {@code MessageDigest} algorithm.
*/
public static final HashAlgorithm SHA384 = get().forKey("sha-384");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-512}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-512} {@code MessageDigest} algorithm.
*/
public static final HashAlgorithm SHA512 = get().forKey("sha-512");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-256}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-256} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public static final HashAlgorithm SHA3_256 = get().forKey("sha3-256");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-384}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-384} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public static final HashAlgorithm SHA3_384 = get().forKey("sha3-384");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-512}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-512} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public static final HashAlgorithm SHA3_512 = get().forKey("sha3-512");
//prevent instantiation
private HASH() {
}
}
/**
* Return a new JWK builder instance, allowing for type-safe JWK builder coercion based on a provided key or key pair.

View File

@ -30,7 +30,7 @@ import java.security.Key;
*
* <p>All standard Key Algorithms are defined in
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1">JWA (RFC 7518), Section 4.1</a>,
* and they are all available as concrete instances via {@link Jwts#KEY}.</p>
* and they are all available as concrete instances via {@link Jwts.KEY}.</p>
*
* <p><b>&quot;alg&quot; identifier</b></p>
*
@ -40,7 +40,7 @@ import java.security.Key;
*
* @param <E> The type of key to use to obtain the AEAD encryption key
* @param <D> The type of key to use to obtain the AEAD decryption key
* @see Jwts#KEY
* @see Jwts.KEY
* @see <a href="https://tools.ietf.org/html/rfc7516#section-2">RFC 7561, Section 2: JWE Key (Management) Algorithms</a>
* @since JJWT_RELEASE_VERSION
*/

View File

@ -58,8 +58,8 @@ public interface KeyRequest<T> extends Request<T> {
AeadAlgorithm getEncryptionAlgorithm();
/**
* Returns the {@link JweHeader} that will be used to construct the final JWE, available for reading or writing
* any {@link KeyAlgorithm}-specific information.
* Returns the {@link JweHeader} that will be used to construct the final JWE header, available for
* reading or writing any {@link KeyAlgorithm}-specific information.
*
* <p>For an encryption key request, any <em>public</em> information specific to the called {@code KeyAlgorithm}
* implementation that is required to be transmitted in the JWE (such as an initialization vector,
@ -71,8 +71,8 @@ public interface KeyRequest<T> extends Request<T> {
* (such as an initialization vector, authentication tag, ephemeral key, etc) is expected to be available in
* this header.</p>
*
* @return the {@link JweHeader} that will be used to construct the final JWE, available for reading or writing
* any {@link KeyAlgorithm}-specific information.
* @return the {@link JweHeader} that will be used to construct the final JWE header, available for
* reading or writing any {@link KeyAlgorithm}-specific information.
*/
JweHeader getHeader();
}

View File

@ -68,7 +68,7 @@ public final class Keys {
"is not secure enough for any JWT HMAC-SHA algorithm. The JWT " +
"JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a " +
"size >= 256 bits (the key size must be greater than or equal to the hash " +
"output size). Consider using the StandardSecureDigestAlgorithms.HS256.keyBuilder() method (or HS384.keyBuilder() " +
"output size). Consider using the Jwts.SIG.HS256.keyBuilder() method (or HS384.keyBuilder() " +
"or HS512.keyBuilder()) to create a key guaranteed to be secure enough for your preferred HMAC-SHA " +
"algorithm. See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.";
throw new WeakKeyException(msg);
@ -81,9 +81,9 @@ public final class Keys {
* length for that specific algorithm by calling their {@code keyBuilder()} method directly. For example:</p>
*
* <pre><code>
* {@link StandardSecureDigestAlgorithms#HS256}.keyBuilder().build();
* {@link StandardSecureDigestAlgorithms#HS384}.keyBuilder().build();
* {@link StandardSecureDigestAlgorithms#HS512}.keyBuilder().build();
* {@link Jwts.SIG#HS256}.keyBuilder().build();
* {@link Jwts.SIG#HS384}.keyBuilder().build();
* {@link Jwts.SIG#HS512}.keyBuilder().build();
* </code></pre>
*
* <p>Call those methods as needed instead of this static {@code secretKeyFor} helper method - the returned
@ -130,7 +130,7 @@ public final class Keys {
@Deprecated
public static SecretKey secretKeyFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get(alg.name());
SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get().get(alg.name());
if (!(salg instanceof MacAlgorithm)) {
String msg = "The " + alg.name() + " algorithm does not support shared secret keys.";
throw new IllegalArgumentException(msg);
@ -145,11 +145,11 @@ public final class Keys {
* for that specific algorithm by calling their {@code keyPairBuilder()} method directly. For example:</p>
*
* <blockquote><pre>
* Jwts.SIG.{@link StandardSecureDigestAlgorithms#RS256 RS256}.keyPairBuilder().build();
* Jwts.SIG.{@link StandardSecureDigestAlgorithms#RS384 RS384}.keyPairBuilder().build();
* Jwts.SIG.{@link StandardSecureDigestAlgorithms#RS512 RS512}.keyPairBuilder().build();
* Jwts.SIG.{@link Jwts.SIG#RS256 RS256}.keyPairBuilder().build();
* Jwts.SIG.{@link Jwts.SIG#RS384 RS384}.keyPairBuilder().build();
* Jwts.SIG.{@link Jwts.SIG#RS512 RS512}.keyPairBuilder().build();
* ... etc ...
* Jwts.SIG.{@link StandardSecureDigestAlgorithms#ES512 ES512}.keyPairBuilder().build();</pre></blockquote>
* Jwts.SIG.{@link Jwts.SIG#ES512 ES512}.keyPairBuilder().build();</pre></blockquote>
*
* <p>Call those methods as needed instead of this static {@code keyPairFor} helper method - the returned
* {@link KeyPairBuilder} allows callers to specify a preferred Provider or SecureRandom on the builder if
@ -235,7 +235,7 @@ public final class Keys {
@Deprecated
public static KeyPair keyPairFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get(alg.name());
SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get().get(alg.name());
if (!(salg instanceof SignatureAlgorithm)) {
String msg = "The " + alg.name() + " algorithm does not support Key Pairs.";
throw new IllegalArgumentException(msg);

View File

@ -55,9 +55,9 @@ import javax.crypto.SecretKey;
* <p><b>JWA Standard Implementations</b></p>
*
* <p>Constant definitions and utility methods for all JWA (RFC 7518) standard MAC algorithms are
* available via the {@link StandardSecureDigestAlgorithms} registry singleton.</p>
* available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>
*
* @see StandardSecureDigestAlgorithms
* @see io.jsonwebtoken.Jwts.SIG Jwts.SIG
* @since JJWT_RELEASE_VERSION
*/
public interface MacAlgorithm extends SecureDigestAlgorithm<SecretKey, SecretKey>,

View File

@ -24,7 +24,7 @@ import java.security.PublicKey;
* <p><b>JWK Private Key vs Java {@code PrivateKey} differences</b></p>
*
* <p>Unlike the Java cryptography APIs, the JWK specification requires all public key <em>and</em> private key
* properties to be contained within every private JWK. As such, a {@code PrivateJwk} of course represents
* properties to be contained within every private JWK. As such, a {@code PrivateJwk} indeed represents
* private key fields as its name implies, but it is probably more similar to the Java JCA concept of a
* {@link java.security.KeyPair} since it contains everything for both keys.</p>
*

View File

@ -36,7 +36,7 @@ import java.security.Key;
*
* <p>Constant definitions and utility methods for all JWA (RFC 7518) standard
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3">Cryptographic Algorithms for Digital Signatures and
* MACs</a> are available via the {@link StandardSecureDigestAlgorithms} utility class.</p>
* MACs</a> are available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>
*
* <p><b>&quot;alg&quot; identifier</b></p>
*

View File

@ -46,9 +46,9 @@ import java.security.PublicKey;
* <p><b>JWA Standard Implementations</b></p>
*
* <p>Constant definitions and utility methods for all JWA (RFC 7518) standard signature algorithms are
* available via the {@link StandardSecureDigestAlgorithms} registry singleton.</p>
* available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>
*
* @see StandardSecureDigestAlgorithms
* @see io.jsonwebtoken.Jwts.SIG Jwts.SIG
* @since JJWT_RELEASE_VERSION
*/
public interface SignatureAlgorithm extends SecureDigestAlgorithm<PrivateKey, PublicKey>, KeyPairBuilderSupplier {

View File

@ -1,32 +0,0 @@
/*
* Copyright (C) 2021 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 java.security.Key;
/**
* A request to a {@link SignatureAlgorithm} to compute a digital signature or
* <a href="https://en.wikipedia.org/wiki/Digital_signature">digital signature</a> or
* <a href="https://en.wikipedia.org/wiki/Message_authentication_code">message
* authentication code</a>.
* <p>The content for signature input will be available via {@link #getPayload()}, and the key used to compute
* the signature will be available via {@link #getKey()}.</p>
*
* @param <K> the type of {@link Key} used to compute a digital signature or message authentication code
* @since JJWT_RELEASE_VERSION
*/
public interface SignatureRequest<K extends Key> extends SecureRequest<byte[], K> {
}

View File

@ -1,164 +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.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.lang.Classes;
import io.jsonwebtoken.lang.Registry;
import java.util.Collection;
/**
* {@link Registry} singleton containing all standard JWE
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">Encryption Algorithms</a>
* codified in the <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1">
* JSON Web Signature and Encryption Algorithms Registry</a>. These are most commonly accessed via the
* {@link io.jsonwebtoken.Jwts#ENC} convenience alias when creating a JWE. For example:
* <blockquote><pre>
* {@link Jwts#builder()}.
* // ... etc ...
* .encryptWith(secretKey, <b>{@link Jwts#ENC}.A256GCM</b>) // or A128GCM, A192GCM, etc...
* .build()</pre></blockquote>
* <p>Direct type-safe references as shown above are often better than calling {@link #get(String)} or
* {@link #find(String)} which can be susceptible to misspelled or otherwise invalid string values.</p>
*
* @see #get()
* @see #get(String)
* @see #find(String)
* @see #values()
* @see AeadAlgorithm
*
* @since JJWT_RELEASE_VERSION
*/
public final class StandardEncryptionAlgorithms implements Registry<String, AeadAlgorithm> {
private static final Registry<String, AeadAlgorithm> DELEGATE =
Classes.newInstance("io.jsonwebtoken.impl.security.StandardEncryptionAlgorithmsBridge");
private static final StandardEncryptionAlgorithms INSTANCE = new StandardEncryptionAlgorithms();
/**
* Returns this registry (a static singleton).
*
* @return this registry (a static singleton).
*/
public static StandardEncryptionAlgorithms get() { // named `get` to mimic java.util.function.Supplier
return INSTANCE;
}
/**
* {@code AES_128_CBC_HMAC_SHA_256} authenticated encryption algorithm as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.3">RFC 7518, Section 5.2.3</a>. This algorithm
* requires a 256-bit (32 byte) key.
*/
public final AeadAlgorithm A128CBC_HS256 = get("A128CBC-HS256");
/**
* {@code AES_192_CBC_HMAC_SHA_384} authenticated encryption algorithm, as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.4">RFC 7518, Section 5.2.4</a>. This algorithm
* requires a 384-bit (48 byte) key.
*/
public final AeadAlgorithm A192CBC_HS384 = get("A192CBC-HS384");
/**
* {@code AES_256_CBC_HMAC_SHA_512} authenticated encryption algorithm, as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.2.5">RFC 7518, Section 5.2.5</a>. This algorithm
* requires a 512-bit (64 byte) key.
*/
public final AeadAlgorithm A256CBC_HS512 = get("A256CBC-HS512");
/**
* &quot;AES GCM using 128-bit key&quot; as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
* algorithm requires a 128-bit (16 byte) key.
*
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final AeadAlgorithm A128GCM = get("A128GCM");
/**
* &quot;AES GCM using 192-bit key&quot; as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
* algorithm requires a 192-bit (24 byte) key.
*
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final AeadAlgorithm A192GCM = get("A192GCM");
/**
* &quot;AES GCM using 256-bit key&quot; as defined by
* <a href="https://tools.ietf.org/html/rfc7518#section-5.3">RFC 7518, Section 5.3</a><b><sup>1</sup></b>. This
* algorithm requires a 256-bit (32 byte) key.
*
* <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final AeadAlgorithm A256GCM = get("A256GCM");
/**
* Prevent external instantiation.
*/
private StandardEncryptionAlgorithms() {
}
/**
* Returns all JWE-standard AEAD encryption algorithms as an unmodifiable collection.
*
* @return all JWE-standard AEAD encryption algorithms as an unmodifiable collection.
*/
public Collection<AeadAlgorithm> values() {
return DELEGATE.values();
}
/**
* Returns the JWE-standard Encryption Algorithm with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">{@code enc} algorithm identifier</a> or
* throws an {@link IllegalArgumentException} if there is no JWE-standard algorithm for the specified
* {@code id}. If a JWE-standard instance result is not mandatory, consider using the {@link #find(String)}
* method instead.
*
* @param id a JWE standard {@code enc} algorithm identifier
* @return the associated Encryption Algorithm instance.
* @throws IllegalArgumentException if there is no JWE-standard algorithm for the specified identifier.
* @see #find(String)
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">RFC 7518, Section 5.1</a>
*/
@Override
public AeadAlgorithm get(String id) {
return DELEGATE.get(id);
}
/**
* Returns the JWE Encryption Algorithm with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">{@code enc} algorithm identifier</a> or
* {@code null} if a JWE-standard algorithm for the specified {@code id} cannot be found. If a JWE-standard
* instance must be resolved, consider using the {@link #get(String)} method instead.
*
* @param id a JWE standard {@code enc} algorithm identifier
* @return the associated standard Encryption Algorithm instance or {@code null} otherwise.
* @see #get(String)
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1">RFC 7518, Section 5.1</a>
*/
@Override
public AeadAlgorithm find(String id) {
return DELEGATE.find(id);
}
}

View File

@ -1,178 +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.security;
import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.lang.Classes;
import io.jsonwebtoken.lang.Registry;
import java.util.Collection;
/**
* Registry of various (<em>but not all</em>)
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid
* <a href="https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier">JWK Thumbprint URIs</a>. For
* example:
* <blockquote><pre>
* Jwks.{@link JwkBuilder builder}()
* // ... etc ...
* .{@link JwkBuilder#setIdFromThumbprint(HashAlgorithm) setIdFromThumbprint}(Jwks.HASH.{@link StandardHashAlgorithms#SHA256 SHA256}) // &lt;---
* .build()</pre></blockquote>
* <p>or</p>
* <blockquote><pre>
* HashAlgorithm hashAlg = Jwks.HASH.{@link StandardHashAlgorithms#SHA256 SHA256};
* {@link JwkThumbprint} thumbprint = aJwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}(hashAlg);
* String <a href="https://www.rfc-editor.org/rfc/rfc9278#section-3">rfcMandatoryPrefix</a> = "urn:ietf:params:oauth:jwk-thumbprint:" + hashAlg.getId();
* assert thumbprint.toURI().toString().startsWith(rfcMandatoryPrefix);
* </pre></blockquote>
*
* @see #get()
* @see #values()
* @see #find(String)
* @see #get(String)
* @see HashAlgorithm
* @since JJWT_RELEASE_VERSION
*/
public final class StandardHashAlgorithms implements Registry<String, HashAlgorithm> {
private static final Registry<String, HashAlgorithm> DELEGATE =
Classes.newInstance("io.jsonwebtoken.impl.security.StandardHashAlgorithmsBridge");
private static final StandardHashAlgorithms INSTANCE = new StandardHashAlgorithms();
/**
* Returns this registry (a static singleton).
*
* @return this registry (a static singleton).
*/
public static StandardHashAlgorithms get() { // named `get` to mimic java.util.function.Supplier
return INSTANCE;
}
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-256}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-256} {@code MessageDigest} algorithm.
*/
public final HashAlgorithm SHA256 = get("sha-256");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-384}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-384} {@code MessageDigest} algorithm.
*/
public final HashAlgorithm SHA384 = get("sha-384");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha-512}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA-512} {@code MessageDigest} algorithm.
*/
public final HashAlgorithm SHA512 = get("sha-512");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-256}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-256} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final HashAlgorithm SHA3_256 = get("sha3-256");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-384}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-384} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final HashAlgorithm SHA3_384 = get("sha3-384");
/**
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA
* hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)
* value of {@code sha3-512}. It is a {@code HashAlgorithm} alias for the native
* Java JCA {@code SHA3-512} {@code MessageDigest} algorithm.
* <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final HashAlgorithm SHA3_512 = get("sha3-512");
/**
* Prevent external instantiation.
*/
private StandardHashAlgorithms() {
}
/**
* Returns common
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> as an unmodifiable collection.
*
* @return common
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA Hash
* Algorithms</a> as an unmodifiable collection.
*/
public Collection<HashAlgorithm> values() {
return DELEGATE.values();
}
/**
* Returns the {@code HashAlgorithm} instance with the specified IANA algorithm {@code id}, or throws an
* {@link IllegalArgumentException} if there is no supported algorithm for the specified {@code id}. The
* {@code id} parameter is expected to equal one of the string values in the <b>{@code Hash Name String}</b>
* column within the
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml">IANA Named Information
* Hash Algorithm Registry</a> table. If a supported instance result is not mandatory, consider using the
* {@link #find(String)} method instead.
*
* @param id an IANA {@code Hash Name String} hash algorithm identifier.
* @return the associated {@code HashAlgorithm} instance.
* @throws IllegalArgumentException if there is no supported algorithm for the specified identifier.
* @see #find(String)
* @see <a href="https://www.iana.org/assignments/named-information/named-information.xhtml">IANA Named
* Information Hash Algorithm Registry</a>
*/
@Override
public HashAlgorithm get(String id) {
return DELEGATE.get(id);
}
/**
* Returns the {@code HashAlgorithm} instance with the specified IANA algorithm {@code id}, or {@code null} if
* the specified {@code id} cannot be found. The {@code id} parameter is expected to equal one of the string
* values in the <b>{@code Hash Name String}</b> column within the
* <a href="https://www.iana.org/assignments/named-information/named-information.xhtml">IANA Named Information
* Hash Algorithm Registry</a> table. If a standard instance must be resolved, consider using the
* {@link #get(String)} method instead.
*
* @param id an IANA {@code Hash Name String} hash algorithm identifier
* @return the associated {@code HashAlgorithm} instance if found or {@code null} otherwise.
* @see <a href="https://www.iana.org/assignments/named-information/named-information.xhtml">IANA Named Information
* Hash Algorithm Registry</a>
* @see #get(String)
*/
@Override
public HashAlgorithm find(String id) {
return DELEGATE.find(id);
}
}

View File

@ -1,712 +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.security;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Classes;
import io.jsonwebtoken.lang.Registry;
import javax.crypto.SecretKey;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Collection;
/**
* {@link Registry} singleton containing all standard JWE
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4">Key Management Algorithms</a>. These are most
* commonly accessed via the {@link io.jsonwebtoken.Jwts#KEY} convenience alias when creating a JWE. For example:
* <blockquote><pre>
* {@link Jwts#builder()}.
* // ... etc ...
* .encryptWith(rsaPublicKey, <b>{@link Jwts#KEY}.RSA_OAEP</b>, Jwts.ENC.A256GCM) // &lt;--
* .build()</pre></blockquote>
* <p>Direct type-safe references as shown above are often better than calling {@link #get(String)} or
* {@link #find(String)} which can be susceptible to misspelled or otherwise invalid string values.</p>
*
* @see #get()
* @see #get(String)
* @see #find(String)
* @see #values()
* @see KeyAlgorithm
* @since JJWT_RELEASE_VERSION
*/
public final class StandardKeyAlgorithms implements Registry<String, KeyAlgorithm<?, ?>> {
private static final Registry<String, KeyAlgorithm<?, ?>> REGISTRY =
Classes.newInstance("io.jsonwebtoken.impl.security.StandardKeyAlgorithmsBridge");
private static final StandardKeyAlgorithms INSTANCE = new StandardKeyAlgorithms();
/**
* Returns this registry (a static singleton).
*
* @return this registry (a static singleton).
*/
public static StandardKeyAlgorithms get() { // named `get` to mimic java.util.function.Supplier
return INSTANCE;
}
/**
* Key algorithm reflecting direct use of a shared symmetric key as the JWE AEAD encryption key, as defined
* by <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.5">RFC 7518 (JWA), Section 4.5</a>. This
* algorithm does not produce encrypted key ciphertext.
*/
public final KeyAlgorithm<SecretKey, SecretKey> DIRECT = doGet("dir");
/**
* AES Key Wrap algorithm with default initial value using a 128-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4">RFC 7518 (JWA), Section 4.4</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 128-bit shared symmetric key using the
* AES Key Wrap algorithm, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the 128-bit shared symmetric key,
* using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A128KW = doGet("A128KW");
/**
* AES Key Wrap algorithm with default initial value using a 192-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4">RFC 7518 (JWA), Section 4.4</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 192-bit shared symmetric key using the
* AES Key Wrap algorithm, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the 192-bit shared symmetric key,
* using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A192KW = doGet("A192KW");
/**
* AES Key Wrap algorithm with default initial value using a 256-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4">RFC 7518 (JWA), Section 4.4</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 256-bit shared symmetric key using the
* AES Key Wrap algorithm, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the 256-bit shared symmetric key,
* using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A256KW = doGet("A256KW");
/**
* Key wrap algorithm with AES GCM using a 128-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7">RFC 7518 (JWA), Section 4.7</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 128-bit shared symmetric key using the
* AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext
* and GCM authentication tag.</li>
* <li>Sets the generated initialization vector as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Sets the resulting GCM authentication tag as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Obtains the required initialization vector from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Obtains the required GCM authentication tag from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Decrypts the encrypted key ciphertext with the 128-bit shared symmetric key, the initialization vector
* and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key
* plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A128GCMKW = doGet("A128GCMKW");
/**
* Key wrap algorithm with AES GCM using a 192-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7">RFC 7518 (JWA), Section 4.7</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 192-bit shared symmetric key using the
* AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext
* and GCM authentication tag.</li>
* <li>Sets the generated initialization vector as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Sets the resulting GCM authentication tag as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Obtains the required initialization vector from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Obtains the required GCM authentication tag from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Decrypts the encrypted key ciphertext with the 192-bit shared symmetric key, the initialization vector
* and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key \
* plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A192GCMKW = doGet("A192GCMKW");
/**
* Key wrap algorithm with AES GCM using a 256-bit key, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7">RFC 7518 (JWA), Section 4.7</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>
* <li>Encrypts this newly-generated {@code SecretKey} with a 256-bit shared symmetric key using the
* AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext
* and GCM authentication tag.</li>
* <li>Sets the generated initialization vector as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Sets the resulting GCM authentication tag as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Obtains the required initialization vector from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1">&quot;iv&quot;
* (Initialization Vector) Header Parameter</a></li>
* <li>Obtains the required GCM authentication tag from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2">&quot;tag&quot;
* (Authentication Tag) Header Parameter</a></li>
* <li>Decrypts the encrypted key ciphertext with the 256-bit shared symmetric key, the initialization vector
* and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key \
* plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final SecretKeyAlgorithm A256GCMKW = doGet("A256GCMKW");
/**
* Key encryption algorithm using <code>PBES2 with HMAC SHA-256 and &quot;A128KW&quot; wrapping</code>
* as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8">RFC 7518 (JWA), Section 4.8</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Determines the number of PBDKF2 iterations via the JWE header's
* {@link JweHeader#getPbes2Count() pbes2Count} value. If that value is not set, a suitable number of
* iterations will be chosen based on
* <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2">OWASP
* PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>
* <li>Generates a new secure-random salt input and sets it as the JWE header
* {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>
* <li>Derives a 128-bit Key Encryption Key with the PBES2-HS256 password-based key derivation algorithm,
* using the provided password, iteration count, and input salt as arguments.</li>
* <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A128KW} key wrap
* algorithm using the 128-bit derived password-based Key Encryption Key from step {@code #3},
* producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated
* {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required PBKDF2 input salt from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1">&quot;p2s&quot;
* (PBES2 Salt Input) Header Parameter</a></li>
* <li>Obtains the required PBKDF2 iteration count from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2">&quot;p2c&quot;
* (PBES2 Count) Header Parameter</a></li>
* <li>Derives the 128-bit Key Encryption Key with the PBES2-HS256 password-based key derivation algorithm,
* using the provided password, obtained salt input, and obtained iteration count as arguments.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with with the {@code A128KW} key unwrap
* algorithm using the 128-bit derived password-based Key Encryption Key from step {@code #3},
* producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<Password, Password> PBES2_HS256_A128KW = doGet("PBES2-HS256+A128KW");
/**
* Key encryption algorithm using <code>PBES2 with HMAC SHA-384 and &quot;A192KW&quot; wrapping</code>
* as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8">RFC 7518 (JWA), Section 4.8</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Determines the number of PBDKF2 iterations via the JWE header's
* {@link JweHeader#getPbes2Count() pbes2Count} value. If that value is not set, a suitable number of
* iterations will be chosen based on
* <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2">OWASP
* PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>
* <li>Generates a new secure-random salt input and sets it as the JWE header
* {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>
* <li>Derives a 192-bit Key Encryption Key with the PBES2-HS384 password-based key derivation algorithm,
* using the provided password, iteration count, and input salt as arguments.</li>
* <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A192KW} key wrap
* algorithm using the 192-bit derived password-based Key Encryption Key from step {@code #3},
* producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated
* {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required PBKDF2 input salt from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1">&quot;p2s&quot;
* (PBES2 Salt Input) Header Parameter</a></li>
* <li>Obtains the required PBKDF2 iteration count from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2">&quot;p2c&quot;
* (PBES2 Count) Header Parameter</a></li>
* <li>Derives the 192-bit Key Encryption Key with the PBES2-HS384 password-based key derivation algorithm,
* using the provided password, obtained salt input, and obtained iteration count as arguments.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with with the {@code A192KW} key unwrap
* algorithm using the 192-bit derived password-based Key Encryption Key from step {@code #3},
* producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<Password, Password> PBES2_HS384_A192KW = doGet("PBES2-HS384+A192KW");
/**
* Key encryption algorithm using <code>PBES2 with HMAC SHA-512 and &quot;A256KW&quot; wrapping</code>
* as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8">RFC 7518 (JWA), Section 4.8</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Determines the number of PBDKF2 iterations via the JWE header's
* {@link JweHeader#getPbes2Count() pbes2Count} value. If that value is not set, a suitable number of
* iterations will be chosen based on
* <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2">OWASP
* PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>
* <li>Generates a new secure-random salt input and sets it as the JWE header
* {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>
* <li>Derives a 256-bit Key Encryption Key with the PBES2-HS512 password-based key derivation algorithm,
* using the provided password, iteration count, and input salt as arguments.</li>
* <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A256KW} key wrap
* algorithm using the 256-bit derived password-based Key Encryption Key from step {@code #3},
* producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated
* {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required PBKDF2 input salt from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1">&quot;p2s&quot;
* (PBES2 Salt Input) Header Parameter</a></li>
* <li>Obtains the required PBKDF2 iteration count from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2">&quot;p2c&quot;
* (PBES2 Count) Header Parameter</a></li>
* <li>Derives the 256-bit Key Encryption Key with the PBES2-HS512 password-based key derivation algorithm,
* using the provided password, obtained salt input, and obtained iteration count as arguments.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with with the {@code A256KW} key unwrap
* algorithm using the 256-bit derived password-based Key Encryption Key from step {@code #3},
* producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<Password, Password> PBES2_HS512_A256KW = doGet("PBES2-HS512+A256KW");
/**
* Key Encryption with {@code RSAES-PKCS1-v1_5}, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.2">RFC 7518 (JWA), Section 4.2</a>.
* This algorithm requires a key size of 2048 bits or larger.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the RSA key wrap algorithm, using the JWE
* recipient's RSA Public Key, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the RSA key unwrap algorithm, using the JWE recipient's
* RSA Private Key, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> RSA1_5 = doGet("RSA1_5");
/**
* Key Encryption with {@code RSAES OAEP using default parameters}, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.3">RFC 7518 (JWA), Section 4.3</a>.
* This algorithm requires a key size of 2048 bits or larger.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the RSA OAEP with SHA-1 and MGF1 key wrap algorithm,
* using the JWE recipient's RSA Public Key, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the RSA OAEP with SHA-1 and MGF1 key unwrap algorithm,
* using the JWE recipient's RSA Private Key, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> RSA_OAEP = doGet("RSA-OAEP");
/**
* Key Encryption with {@code RSAES OAEP using SHA-256 and MGF1 with SHA-256}, as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.3">RFC 7518 (JWA), Section 4.3</a>.
* This algorithm requires a key size of 2048 bits or larger.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the RSA OAEP with SHA-256 and MGF1 key wrap
* algorithm, using the JWE recipient's RSA Public Key, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the RSA OAEP with SHA-256 and MGF1 key unwrap algorithm,
* using the JWE recipient's RSA Private Key, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> RSA_OAEP_256 = doGet("RSA-OAEP-256");
/**
* Key Agreement with {@code ECDH-ES using Concat KDF} as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6">RFC 7518 (JWA), Section 4.6</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the
* JWE recipient's EC Public Key.</li>
* <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key
* and the JWE recipient's EC Public Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> a symmetric Content
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* generated shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Sets the generated EC key pair's Public Key as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>
* <li>Returns the derived symmetric {@code SecretKey} for JJWT to use to encrypt the entire JWE with the
* associated {@link AeadAlgorithm}. Encrypted key ciphertext is not produced with this algorithm, so
* the resulting JWE will not contain any embedded key ciphertext.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required ephemeral Elliptic Curve Public Key from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a>.</li>
* <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>
* <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key
* and the JWE recipient's EC Private Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> the symmetric Content
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* obtained shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Returns the derived symmetric {@code SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES = doGet("ECDH-ES");
/**
* Key Agreement with Key Wrapping via
* <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A128KW&quot;</code> as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6">RFC 7518 (JWA), Section 4.6</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the
* JWE recipient's EC Public Key.</li>
* <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key
* and the JWE recipient's EC Public Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> a 128-bit symmetric Key
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* generated shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Sets the generated EC key pair's Public Key as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the {@code A128KW} key wrap
* algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required ephemeral Elliptic Curve Public Key from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a>.</li>
* <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>
* <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key
* and the JWE recipient's EC Private Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> the symmetric Key
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* obtained shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the
* 128-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A128KW = doGet("ECDH-ES+A128KW");
/**
* Key Agreement with Key Wrapping via
* <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A192KW&quot;</code> as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6">RFC 7518 (JWA), Section 4.6</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the
* JWE recipient's EC Public Key.</li>
* <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key
* and the JWE recipient's EC Public Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> a 192-bit symmetric Key
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* generated shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Sets the generated EC key pair's Public Key as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the {@code A192KW} key wrap
* algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key
* ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required ephemeral Elliptic Curve Public Key from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a>.</li>
* <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>
* <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key
* and the JWE recipient's EC Private Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> the 192-bit symmetric
* Key Encryption {@code SecretKey} with the Concat KDF algorithm using the
* obtained shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the
* 192-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A192KW = doGet("ECDH-ES+A192KW");
/**
* Key Agreement with Key Wrapping via
* <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A256KW&quot;</code> as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6">RFC 7518 (JWA), Section 4.6</a>.
*
* <p>During JWE creation, this algorithm:</p>
* <ol>
* <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the
* JWE recipient's EC Public Key.</li>
* <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key
* and the JWE recipient's EC Public Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> a 256-bit symmetric Key
* Encryption {@code SecretKey} with the Concat KDF algorithm using the
* generated shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Sets the generated EC key pair's Public Key as the required
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>
* <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a
* specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#keyBuilder()}).</li>
* <li>Encrypts this newly-generated {@code SecretKey} with the {@code A256KW} key wrap
* algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key
* ciphertext.</li>
* <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated
* {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>
* </ol>
* <p>For JWE decryption, this algorithm:</p>
* <ol>
* <li>Obtains the required ephemeral Elliptic Curve Public Key from the
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1">&quot;epk&quot;
* (Ephemeral Public Key) Header Parameter</a>.</li>
* <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>
* <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key
* and the JWE recipient's EC Private Key.</li>
* <li><a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2">Derives</a> the 256-bit symmetric
* Key Encryption {@code SecretKey} with the Concat KDF algorithm using the
* obtained shared secret and any available
* {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and
* {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>
* <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>
* <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the
* 256-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>
* <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire
* JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>
* </ol>
*/
public final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A256KW = doGet("ECDH-ES+A256KW");
//prevent instantiation
private StandardKeyAlgorithms() {
}
// do not change this visibility. Raw type method signature not be publicly exposed
@SuppressWarnings("unchecked")
private <T> T doGet(String id) {
Assert.hasText(id, "id cannot be null or empty.");
return (T) get(id);
}
/**
* Returns all JWA-standard
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4">Key Management Algorithms</a> as an
* unmodifiable collection.
*
* @return all JWA-standard
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4">Key Management Algorithms</a> as an
* unmodifiable collection.
*/
public Collection<KeyAlgorithm<?, ?>> values() {
return REGISTRY.values();
}
/**
* Returns the JWE Key Management Algorithm with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1">{@code alg} key algorithm identifier</a> or
* {@code null} if an algorithm for the specified {@code id} cannot be found. If a JWA-standard
* instance must be resolved, consider using the {@link #get(String)} method instead.
*
* @param id a JWA standard {@code alg} key algorithm identifier
* @return the associated KeyAlgorithm instance or {@code null} otherwise.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1">RFC 7518, Section 4.1</a>
* @see #get(String)
*/
public KeyAlgorithm<?, ?> find(String id) {
return REGISTRY.find(id);
}
/**
* Returns the JWE Key Management Algorithm with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1">{@code alg} key algorithm identifier</a> or
* throws an {@link IllegalArgumentException} if there is no JWE-standard algorithm for the specified
* {@code id}. If a JWE-standard instance result is not mandatory, consider using the {@link #find(String)}
* method instead.
*
* @param id a JWA standard {@code alg} key algorithm identifier
* @return the associated {@code KeyAlgorithm} instance.
* @throws IllegalArgumentException if there is no JWA-standard algorithm for the specified identifier.
* @see #find(String)
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1">RFC 7518, Section 4.1</a>
*/
public KeyAlgorithm<?, ?> get(String id) throws IllegalArgumentException {
return REGISTRY.get(id);
}
}

View File

@ -1,254 +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.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Classes;
import io.jsonwebtoken.lang.Registry;
import java.security.Key;
import java.util.Collection;
/**
* Registry of all standard JWS
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3">Cryptographic Algorithms for Digital
* Signatures and MACs</a>. These are most commonly accessed via the {@link io.jsonwebtoken.Jwts#SIG} convenience
* alias when creating a JWS. For example:
* <blockquote><pre>
* {@link Jwts#builder()}.
* // ... etc ...
* .{@link io.jsonwebtoken.JwtBuilder#signWith(Key, SecureDigestAlgorithm) signWith}(aKey, {@link Jwts#SIG}.HS256) // &lt;--
* .build()</pre></blockquote>
*
* @see #get()
* @see #get(String)
* @see #find(String)
* @see #values()
* @since JJWT_RELEASE_VERSION
*/
public final class StandardSecureDigestAlgorithms implements Registry<String, SecureDigestAlgorithm<?, ?>> {
private static final Registry<String, SecureDigestAlgorithm<?, ?>> IMPL =
Classes.newInstance("io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithmsBridge");
private static final StandardSecureDigestAlgorithms INSTANCE = new StandardSecureDigestAlgorithms();
/**
* Returns this registry (a static singleton).
*
* @return this registry (a static singleton).
*/
public static StandardSecureDigestAlgorithms get() { // named `get` to mimic java.util.function.Supplier
return INSTANCE;
}
/**
* The &quot;none&quot; signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6">RFC 7518, Section 3.6</a>. This algorithm
* is used only when creating unsecured (not integrity protected) JWSs and is not usable in any other scenario.
* Any attempt to call its methods will result in an exception being thrown.
*/
public final SecureDigestAlgorithm<Key, Key> NONE = doGet("none");
/**
* {@code HMAC using SHA-256} message authentication algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2">RFC 7518, Section 3.2</a>. This algorithm
* requires a 256-bit (32 byte) key.
*/
public final MacAlgorithm HS256 = doGet("HS256");
/**
* {@code HMAC using SHA-384} message authentication algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2">RFC 7518, Section 3.2</a>. This algorithm
* requires a 384-bit (48 byte) key.
*/
public final MacAlgorithm HS384 = doGet("HS384");
/**
* {@code HMAC using SHA-512} message authentication algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2">RFC 7518, Section 3.2</a>. This algorithm
* requires a 512-bit (64 byte) key.
*/
public final MacAlgorithm HS512 = doGet("HS512");
/**
* {@code RSASSA-PKCS1-v1_5 using SHA-256} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3">RFC 7518, Section 3.3</a>. This algorithm
* requires a 2048-bit key.
*/
public final SignatureAlgorithm RS256 = doGet("RS256");
/**
* {@code RSASSA-PKCS1-v1_5 using SHA-384} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3">RFC 7518, Section 3.3</a>. This algorithm
* requires a 2048-bit key, but the JJWT team recommends a 3072-bit key.
*/
public final SignatureAlgorithm RS384 = doGet("RS384");
/**
* {@code RSASSA-PKCS1-v1_5 using SHA-512} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3">RFC 7518, Section 3.3</a>. This algorithm
* requires a 2048-bit key, but the JJWT team recommends a 4096-bit key.
*/
public final SignatureAlgorithm RS512 = doGet("RS512");
/**
* {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.
* This algorithm requires a 2048-bit key.
*
* <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final SignatureAlgorithm PS256 = doGet("PS256");
/**
* {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.
* This algorithm requires a 2048-bit key, but the JJWT team recommends a 3072-bit key.
*
* <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final SignatureAlgorithm PS384 = doGet("PS384");
/**
* {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.
* This algorithm requires a 2048-bit key, but the JJWT team recommends a 4096-bit key.
*
* <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime
* classpath.</p>
*/
public final SignatureAlgorithm PS512 = doGet("PS512");
/**
* {@code ECDSA using P-256 and SHA-256} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4">RFC 7518, Section 3.4</a>. This algorithm
* requires a 256-bit key.
*/
public final SignatureAlgorithm ES256 = doGet("ES256");
/**
* {@code ECDSA using P-384 and SHA-384} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4">RFC 7518, Section 3.4</a>. This algorithm
* requires a 384-bit key.
*/
public final SignatureAlgorithm ES384 = doGet("ES384");
/**
* {@code ECDSA using P-521 and SHA-512} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4">RFC 7518, Section 3.4</a>. This algorithm
* requires a 521-bit key.
*/
public final SignatureAlgorithm ES512 = doGet("ES512");
/**
* {@code EdDSA} signature algorithm as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc8037#section-3.1">RFC 8037, Section 3.1</a>. This algorithm
* requires either {@code Ed25519} or {@code Ed448} Edwards Curve keys.
* <p><b>This algorithm requires at least JDK 15 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final SignatureAlgorithm EdDSA = doGet("EdDSA");
/**
* {@code EdDSA} signature algorithm using Curve {@code Ed25519} as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc8037#section-3.1">RFC 8037, Section 3.1</a>. This algorithm
* requires {@code Ed25519} Edwards Curve keys to create signatures. <b>This is a convenience alias for
* {@link #EdDSA}</b> that defaults key generation to {@code Ed25519} keys.
* <p><b>This algorithm requires at least JDK 15 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final SignatureAlgorithm Ed25519 = doGet("Ed25519");
/**
* {@code EdDSA} signature algorithm using Curve {@code Ed448} as defined by
* <a href="https://www.rfc-editor.org/rfc/rfc8037#section-3.1">RFC 8037, Section 3.1</a>. This algorithm
* requires {@code Ed448} Edwards Curve keys to create signatures. <b>This is a convenience alias for
* {@link #EdDSA}</b> that defaults key generation to {@code Ed448} keys.
* <p><b>This algorithm requires at least JDK 15 or a compatible JCA Provider (like BouncyCastle) in the runtime
* classpath.</b></p>
*/
public final SignatureAlgorithm Ed448 = doGet("Ed448");
/**
* Prevent external instantiation.
*/
private StandardSecureDigestAlgorithms() {
}
// do not change this visibility. Raw type method signature not be publicly exposed
@SuppressWarnings("unchecked")
private <T> T doGet(String id) {
Assert.hasText(id, "id cannot be null or empty.");
return (T) get(id);
}
/**
* Returns all standard JWS
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">Digital Signature and MAC Algorithms</a>
* as an unmodifiable collection.
*
* @return all standard JWS
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">Digital Signature and MAC Algorithms</a>
* as an unmodifiable collection.
*/
public Collection<SecureDigestAlgorithm<?, ?>> values() {
return IMPL.values();
}
/**
* Returns the {@link SignatureAlgorithm} or {@link MacAlgorithm} instance with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">{@code alg} algorithm identifier</a> or
* {@code null} if an algorithm for the specified {@code id} cannot be found. If a JWA-standard
* instance must be resolved, consider using the {@link #get(String)} method instead.
*
* @param id a JWA-standard identifier defined in
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">JWA RFC 7518, Section 3.1</a>
* in the <code>&quot;alg&quot; Param Value</code> column.
* @return the {@code SecureDigestAlgorithm} instance with the specified JWA-standard identifier, or
* {@code null} if no algorithm with that identifier exists.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">RFC 7518, Section 3.1</a>
* @see #get(String)
*/
public SecureDigestAlgorithm<?, ?> find(String id) {
return IMPL.find(id);
}
/**
* Returns the {@link SignatureAlgorithm} or {@link MacAlgorithm} instance with the specified
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">{@code alg} algorithm identifier</a> or
* throws an {@link IllegalArgumentException} if there is no JWA-standard algorithm for the specified
* {@code id}. If a JWA-standard instance result is not mandatory, consider using the {@link #find(String)}
* method instead.
*
* @param id a JWA-standard identifier defined in
* <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">JWA RFC 7518, Section 3.1</a>
* in the <code>&quot;alg&quot; Param Value</code> column.
* @return the associated {@code SecureDigestAlgorithm} instance.
* @throws IllegalArgumentException if there is no JWA-standard algorithm for the specified identifier.
* @see <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">RFC 7518, Section 3.1</a>
* @see #find(String)
*/
public SecureDigestAlgorithm<?, ?> get(String id) throws IllegalArgumentException {
return IMPL.get(id);
}
}

View File

@ -23,9 +23,12 @@ import java.security.cert.X509Certificate;
import java.util.List;
/**
* Accessor methods of X.509-specific properties of an associated JWT Header or JWK, guaranteeing consistent behavior
* Accessor methods of X.509-specific properties of a
* {@link io.jsonwebtoken.ProtectedHeader ProtectedHeader} or {@link AsymmetricJwk}, guaranteeing consistent behavior
* across similar but distinct JWT concepts with identical parameter names.
*
* @see io.jsonwebtoken.ProtectedHeader
* @see AsymmetricJwk
* @since JJWT_RELEASE_VERSION
*/
public interface X509Accessor {
@ -39,8 +42,8 @@ public interface X509Accessor {
* with each certificate delimited as specified in
* <a href="https://datatracker.ietf.org/doc/html/rfc4945#section-6.1">Section 6.1 of RFC 4945</a>.
* The key in the first certificate <em>MUST</em> match the public key represented by other members of the
* associated JWT or JWK. The protocol used to acquire the resource <em>MUST</em> provide integrity protection;
* an HTTP GET request to retrieve the certificate <em>MUST</em> use
* associated ProtectedHeader or JWK. The protocol used to acquire the resource <em>MUST</em> provide integrity
* protection; an HTTP GET request to retrieve the certificate <em>MUST</em> use
* <a href="https://datatracker.ietf.org/doc/html/rfc2818">HTTP over TLS</a>; the identity of the server
* <em>MUST</em> be validated, as per
* <a href="https://datatracker.ietf.org/doc/html/rfc6125#section-6">Section 6 of RFC 6125</a>.</p>

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -29,6 +29,8 @@ import java.util.Collections;
import java.util.Map;
/**
* Deserializer using a Jackson {@link ObjectMapper}.
*
* @since 0.10.0
*/
public class JacksonDeserializer<T> implements Deserializer<T> {
@ -36,6 +38,9 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
private final Class<T> returnType;
private final ObjectMapper objectMapper;
/**
* Constructor using JJWT's default {@link ObjectMapper} singleton for deserialization.
*/
public JacksonDeserializer() {
this(JacksonSerializer.DEFAULT_OBJECT_MAPPER);
}
@ -76,6 +81,11 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
objectMapper.registerModule(module);
}
/**
* Constructor using the specified Jackson {@link ObjectMapper}.
*
* @param objectMapper the ObjectMapper to use for deserialization.
*/
@SuppressWarnings({"unchecked", "WeakerAccess", "unused"}) // for end-users providing a custom ObjectMapper
public JacksonDeserializer(ObjectMapper objectMapper) {
this(objectMapper, (Class<T>) Object.class);
@ -98,6 +108,13 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
}
}
/**
* Converts the specified byte array value to the desired typed instance using the Jackson {@link ObjectMapper}.
*
* @param bytes the byte array value to convert
* @return the desired typed instance
* @throws IOException if there is a problem during reading or instance creation
*/
protected T readValue(byte[] bytes) throws IOException {
return objectMapper.readValue(bytes, returnType);
}

View File

@ -24,6 +24,8 @@ import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.lang.Assert;
/**
* Serializer using a Jackson {@link ObjectMapper}.
*
* @since 0.10.0
*/
public class JacksonSerializer<T> implements Serializer<T> {
@ -41,11 +43,19 @@ public class JacksonSerializer<T> implements Serializer<T> {
private final ObjectMapper objectMapper;
/**
* Constructor using JJWT's default {@link ObjectMapper} singleton for serialization.
*/
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
public JacksonSerializer() {
this(DEFAULT_OBJECT_MAPPER);
}
/**
* Creates a new Jackson Serializer that uses the specified {@link ObjectMapper} for serialization.
*
* @param objectMapper the ObjectMapper to use for serialization.
*/
@SuppressWarnings("WeakerAccess") //intended for end-users to use when providing a custom ObjectMapper
public JacksonSerializer(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper cannot be null.");
@ -63,6 +73,13 @@ public class JacksonSerializer<T> implements Serializer<T> {
}
}
/**
* Serializes the specified instance value to a byte array using the underlying Jackson {@link ObjectMapper}.
*
* @param t the instance to serialize to a byte array
* @return the byte array serialization of the specified instance
* @throws JsonProcessingException if there is a problem during serialization
*/
@SuppressWarnings("WeakerAccess") //for testing
protected byte[] writeValueAsBytes(T t) throws JsonProcessingException {
return this.objectMapper.writeValueAsBytes(t);

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -1,97 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.Header;
import java.util.Map;
/**
* @since JJWT_RELEASE_VERSION
*/
public abstract class AbstractHeaderBuilder<H extends Header<H>, T extends HeaderBuilder<H, T>>
implements HeaderBuilder<H, T> {
protected final H header;
protected AbstractHeaderBuilder() {
this.header = newHeader();
onNewHeader(this.header);
}
protected abstract H newHeader();
protected void onNewHeader(H header) {
}
@SuppressWarnings("unchecked")
protected final T tthis() {
return (T) this;
}
@Override
public T setType(String typ) {
this.header.setType(typ);
return tthis();
}
@Override
public T setContentType(String cty) {
this.header.setContentType(cty);
return tthis();
}
@Override
public T setAlgorithm(String alg) {
this.header.setAlgorithm(alg);
return tthis();
}
@Override
public T setCompressionAlgorithm(String zip) {
this.header.setCompressionAlgorithm(zip);
return tthis();
}
@Override
public H build() {
return this.header;
}
@Override
public T put(String key, Object value) {
this.header.put(key, value);
return tthis();
}
@Override
public T remove(String key) {
this.header.remove(key);
return tthis();
}
@Override
public T putAll(Map<? extends String, ?> m) {
this.header.putAll(m);
return tthis();
}
@Override
public T clear() {
this.header.clear();
return tthis();
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.ProtectedHeader;
import io.jsonwebtoken.impl.security.DefaultX509Builder;
import io.jsonwebtoken.security.PublicJwk;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
/**
* @since JJWT_RELEASE_VERSION
*/
public abstract class AbstractProtectedHeaderBuilder<H extends ProtectedHeader<H>,
T extends ProtectedHeaderBuilder<H, T>>
extends AbstractHeaderBuilder<H, T> implements ProtectedHeaderBuilder<H, T> {
private DefaultX509Builder<T> x509Builder;
@Override
protected void onNewHeader(H header) {
this.x509Builder = new DefaultX509Builder<>(header, tthis(), IllegalStateException.class);
}
@Override
public T setJwkSetUrl(URI uri) {
this.header.setJwkSetUrl(uri);
return tthis();
}
@Override
public T setJwk(PublicJwk<?> jwk) {
this.header.setJwk(jwk);
return tthis();
}
@Override
public T setKeyId(String kid) {
this.header.setKeyId(kid);
return tthis();
}
@Override
public T setX509Url(URI uri) {
return this.x509Builder.setX509Url(uri);
}
@Override
public T setX509CertificateChain(List<X509Certificate> chain) {
return this.x509Builder.setX509CertificateChain(chain);
}
@Override
public T setX509CertificateSha1Thumbprint(byte[] thumbprint) {
this.header.setX509CertificateSha1Thumbprint(thumbprint);
return tthis();
}
@Override
public T setX509CertificateSha256Thumbprint(byte[] thumbprint) {
this.header.setX509CertificateSha256Thumbprint(thumbprint);
return tthis();
}
@Override
public T setCritical(Set<String> crit) {
this.header.setCritical(crit);
return tthis();
}
@Override
public T withX509Sha1Thumbprint(boolean enable) {
return x509Builder.withX509Sha1Thumbprint(enable);
}
@Override
public T withX509Sha256Thumbprint(boolean enable) {
return x509Builder.withX509Sha256Thumbprint(enable);
}
@Override
public H build() {
this.x509Builder.apply();
return this.header;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 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;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.security.AbstractAsymmetricJwk;
import io.jsonwebtoken.security.X509Mutator;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
public class AbstractX509Context<T extends X509Mutator<T>> extends FieldMap implements X509Context<T> {
public AbstractX509Context(Set<Field<?>> fieldSet) {
super(fieldSet);
}
@SuppressWarnings("unchecked")
protected T self() {
return (T) this;
}
@Override
public URI getX509Url() {
return get(AbstractAsymmetricJwk.X5U);
}
@Override
public T setX509Url(URI uri) {
put(AbstractAsymmetricJwk.X5U, uri);
return self();
}
@Override
public List<X509Certificate> getX509CertificateChain() {
return get(AbstractAsymmetricJwk.X5C);
}
@Override
public T setX509CertificateChain(List<X509Certificate> chain) {
put(AbstractAsymmetricJwk.X5C, chain);
return self();
}
@Override
public byte[] getX509CertificateSha1Thumbprint() {
return get(AbstractAsymmetricJwk.X5T);
}
@Override
public T setX509CertificateSha1Thumbprint(byte[] thumbprint) {
put(AbstractAsymmetricJwk.X5T, thumbprint);
return self();
}
@Override
public byte[] getX509CertificateSha256Thumbprint() {
return get(AbstractAsymmetricJwk.X5T_S256);
}
@Override
public T setX509CertificateSha256Thumbprint(byte[] thumbprint) {
put(AbstractAsymmetricJwk.X5T_S256, thumbprint);
return self();
}
}

View File

@ -15,14 +15,15 @@
*/
package io.jsonwebtoken.impl;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Locator;
import io.jsonwebtoken.impl.lang.Function;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.lang.Assert;
public class CompressionCodecLocator implements Function<Header<?>, CompressionCodec>, Locator<CompressionCodec> {
//TODO: delete when deleting CompressionCodecResolver
public class CompressionCodecLocator implements Function<Header, CompressionAlgorithm>, Locator<CompressionAlgorithm> {
private final CompressionCodecResolver resolver;
@ -31,12 +32,12 @@ public class CompressionCodecLocator implements Function<Header<?>, CompressionC
}
@Override
public CompressionCodec apply(Header<?> header) {
return resolver.resolveCompressionCodec(header);
public CompressionAlgorithm apply(Header header) {
return locate(header);
}
@Override
public CompressionCodec locate(Header<?> header) {
return apply(header);
public CompressionAlgorithm locate(Header header) {
return resolver.resolveCompressionCodec(header);
}
}

View File

@ -21,13 +21,12 @@ import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.Fields;
import io.jsonwebtoken.impl.lang.JwtDateConverter;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Registry;
import java.util.Date;
import java.util.Map;
import java.util.Set;
public class DefaultClaims extends JwtMap implements Claims {
public class DefaultClaims extends FieldMap implements Claims {
private static final String CONVERSION_ERROR_MSG = "Cannot convert existing claim value of type '%s' to desired type " +
"'%s'. JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. " +
@ -45,11 +44,10 @@ public class DefaultClaims extends JwtMap implements Claims {
static final Field<Date> ISSUED_AT = Fields.rfcDate(Claims.ISSUED_AT, "Issued At");
static final Field<String> JTI = Fields.string(Claims.ID, "JWT ID");
static final Set<Field<?>> FIELDS = Collections.<Field<?>>setOf(
ISSUER, SUBJECT, AUDIENCE, EXPIRATION, NOT_BEFORE, ISSUED_AT, JTI
);
static final Registry<String, Field<?>> FIELDS =
Fields.registry(ISSUER, SUBJECT, AUDIENCE, EXPIRATION, NOT_BEFORE, ISSUED_AT, JTI);
public DefaultClaims() {
protected DefaultClaims() { // visibility for testing
super(FIELDS);
}
@ -64,86 +62,44 @@ public class DefaultClaims extends JwtMap implements Claims {
@Override
public String getIssuer() {
return idiomaticGet(ISSUER);
}
@Override
public Claims setIssuer(String iss) {
put(ISSUER, iss);
return this;
return get(ISSUER);
}
@Override
public String getSubject() {
return idiomaticGet(SUBJECT);
}
@Override
public Claims setSubject(String sub) {
put(SUBJECT, sub);
return this;
return get(SUBJECT);
}
@Override
public String getAudience() {
return idiomaticGet(AUDIENCE);
}
@Override
public Claims setAudience(String aud) {
put(AUDIENCE, aud);
return this;
return get(AUDIENCE);
}
@Override
public Date getExpiration() {
return idiomaticGet(EXPIRATION);
}
@Override
public Claims setExpiration(Date exp) {
put(EXPIRATION, exp);
return this;
return get(EXPIRATION);
}
@Override
public Date getNotBefore() {
return idiomaticGet(NOT_BEFORE);
}
@Override
public Claims setNotBefore(Date nbf) {
put(NOT_BEFORE, nbf);
return this;
return get(NOT_BEFORE);
}
@Override
public Date getIssuedAt() {
return idiomaticGet(ISSUED_AT);
}
@Override
public Claims setIssuedAt(Date iat) {
put(ISSUED_AT, iat);
return this;
return get(ISSUED_AT);
}
@Override
public String getId() {
return idiomaticGet(JTI);
}
@Override
public Claims setId(String jti) {
put(JTI, jti);
return this;
return get(JTI);
}
@Override
public <T> T get(String claimName, Class<T> requiredType) {
Assert.notNull(requiredType, "requiredType argument cannot be null.");
Object value = idiomaticGet(claimName);
Object value = this.idiomaticValues.get(claimName);
if (requiredType.isInstance(value)) {
return requiredType.cast(value);
}

View File

@ -0,0 +1,81 @@
/*
* 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;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ClaimsBuilder;
import io.jsonwebtoken.impl.lang.DelegatingMapMutator;
import io.jsonwebtoken.impl.lang.Field;
import java.util.Date;
/**
* @since JJWT_RELEASE_VERSION
*/
@SuppressWarnings("unused") // used via reflection via Jwts.claims()
public final class DefaultClaimsBuilder extends DelegatingMapMutator<String, Object, FieldMap, ClaimsBuilder>
implements ClaimsBuilder {
public DefaultClaimsBuilder() {
super(new FieldMap(DefaultClaims.FIELDS));
}
<T> ClaimsBuilder put(Field<T> field, Object value) {
this.DELEGATE.put(field, value);
return self();
}
@Override
public ClaimsBuilder setIssuer(String iss) {
return put(DefaultClaims.ISSUER, iss);
}
@Override
public ClaimsBuilder setSubject(String sub) {
return put(DefaultClaims.SUBJECT, sub);
}
@Override
public ClaimsBuilder setAudience(String aud) {
return put(DefaultClaims.AUDIENCE, aud);
}
@Override
public ClaimsBuilder setExpiration(Date exp) {
return put(DefaultClaims.EXPIRATION, exp);
}
@Override
public ClaimsBuilder setNotBefore(Date nbf) {
return put(DefaultClaims.NOT_BEFORE, nbf);
}
@Override
public ClaimsBuilder setIssuedAt(Date iat) {
return put(DefaultClaims.ISSUED_AT, iat);
}
@Override
public ClaimsBuilder setId(String jti) {
return put(DefaultClaims.JTI, jti);
}
@Override
public Claims build() {
// ensure a new instance is returned so that the builder may be re-used:
return new DefaultClaims(this.DELEGATE);
}
}

View File

@ -1,212 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.DynamicHeaderBuilder;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.ProtectedHeader;
import io.jsonwebtoken.impl.security.DefaultX509Builder;
import io.jsonwebtoken.security.PublicJwk;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @since JJWT_RELEASE_VERSION
*/
public class DefaultDynamicHeaderBuilder implements DynamicHeaderBuilder {
private Header<?> header;
private DefaultX509Builder<DynamicHeaderBuilder> x509Builder;
public DefaultDynamicHeaderBuilder() {
this.header = new DefaultUnprotectedHeader();
this.x509Builder = null;
}
private ProtectedHeader<?> ensureProtected() {
ProtectedHeader<?> ph;
if (this.header instanceof ProtectedHeader<?>) {
ph = (ProtectedHeader<?>) this.header;
} else {
this.header = ph = new DefaultJwsHeader(this.header);
this.x509Builder = new DefaultX509Builder<DynamicHeaderBuilder>(ph, this, IllegalStateException.class);
}
return ph;
}
private JweHeader ensureJwe() {
JweHeader h;
if (this.header instanceof JweHeader) {
h = (JweHeader) this.header;
} else {
this.header = h = new DefaultJweHeader(this.header);
this.x509Builder = new DefaultX509Builder<DynamicHeaderBuilder>(h, this, IllegalStateException.class);
}
return h;
}
@Override
public DynamicHeaderBuilder put(String key, Object value) {
this.header.put(key, value);
return this;
}
@Override
public DynamicHeaderBuilder remove(String key) {
this.header.remove(key);
return this;
}
@Override
public DynamicHeaderBuilder putAll(Map<? extends String, ?> m) {
this.header.putAll(m);
return this;
}
@Override
public DynamicHeaderBuilder clear() {
this.header.clear();
return this;
}
@Override
public DynamicHeaderBuilder setType(String typ) {
this.header.setType(typ);
return this;
}
@Override
public DynamicHeaderBuilder setContentType(String cty) {
this.header.setContentType(cty);
return this;
}
@Override
public DynamicHeaderBuilder setAlgorithm(String alg) {
this.header.setAlgorithm(alg);
return this;
}
@Override
public DynamicHeaderBuilder setCompressionAlgorithm(String zip) {
this.header.setCompressionAlgorithm(zip);
return this;
}
@Override
public DynamicHeaderBuilder setJwkSetUrl(URI uri) {
ensureProtected().setJwkSetUrl(uri);
return this;
}
@Override
public DynamicHeaderBuilder setJwk(PublicJwk<?> jwk) {
ensureProtected().setJwk(jwk);
return this;
}
@Override
public DynamicHeaderBuilder setKeyId(String kid) {
ensureProtected().setKeyId(kid);
return this;
}
@Override
public DynamicHeaderBuilder setCritical(Set<String> crit) {
ensureProtected().setCritical(crit);
return this;
}
@Override
public DynamicHeaderBuilder withX509Sha1Thumbprint(boolean enable) {
ensureProtected();
return this.x509Builder.withX509Sha1Thumbprint(enable);
}
@Override
public DynamicHeaderBuilder withX509Sha256Thumbprint(boolean enable) {
ensureProtected();
return this.x509Builder.withX509Sha256Thumbprint(enable);
}
@Override
public DynamicHeaderBuilder setX509Url(URI uri) {
ensureProtected();
return this.x509Builder.setX509Url(uri);
}
@Override
public DynamicHeaderBuilder setX509CertificateChain(List<X509Certificate> chain) {
ensureProtected();
return this.x509Builder.setX509CertificateChain(chain);
}
@Override
public DynamicHeaderBuilder setX509CertificateSha1Thumbprint(byte[] thumbprint) {
ensureProtected();
return this.x509Builder.setX509CertificateSha1Thumbprint(thumbprint);
}
@Override
public DynamicHeaderBuilder setX509CertificateSha256Thumbprint(byte[] thumbprint) {
ensureProtected();
return this.x509Builder.setX509CertificateSha256Thumbprint(thumbprint);
}
@Override
public DynamicHeaderBuilder setAgreementPartyUInfo(byte[] info) {
ensureJwe().setAgreementPartyUInfo(info);
return this;
}
@Override
public DynamicHeaderBuilder setAgreementPartyUInfo(String info) {
ensureJwe().setAgreementPartyUInfo(info);
return this;
}
@Override
public DynamicHeaderBuilder setAgreementPartyVInfo(byte[] info) {
ensureJwe().setAgreementPartyVInfo(info);
return this;
}
@Override
public DynamicHeaderBuilder setAgreementPartyVInfo(String info) {
ensureJwe().setAgreementPartyVInfo(info);
return this;
}
@Override
public DynamicHeaderBuilder setPbes2Count(int count) {
ensureJwe().setPbes2Count(count);
return this;
}
@Override
public Header<?> build() {
if (this.x509Builder != null) {
this.x509Builder.apply();
}
return this.header;
}
}

View File

@ -18,13 +18,12 @@ package io.jsonwebtoken.impl;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.Fields;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
import java.util.Map;
import java.util.Set;
public abstract class AbstractHeader<T extends Header<T>> extends JwtMap implements Header<T> {
public class DefaultHeader extends FieldMap implements Header {
static final Field<String> TYPE = Fields.string(Header.TYPE, "Type");
static final Field<String> CONTENT_TYPE = Fields.string(Header.CONTENT_TYPE, "Content Type");
@ -34,14 +33,14 @@ public abstract class AbstractHeader<T extends Header<T>> extends JwtMap impleme
@Deprecated // TODO: remove for 1.0.0:
static final Field<String> DEPRECATED_COMPRESSION_ALGORITHM = Fields.string(Header.DEPRECATED_COMPRESSION_ALGORITHM, "Deprecated Compression Algorithm");
static final Set<Field<?>> FIELDS = Collections.<Field<?>>setOf(TYPE, CONTENT_TYPE, ALGORITHM, COMPRESSION_ALGORITHM, DEPRECATED_COMPRESSION_ALGORITHM);
static final Registry<String, Field<?>> FIELDS = Fields.registry(TYPE, CONTENT_TYPE, ALGORITHM, COMPRESSION_ALGORITHM, DEPRECATED_COMPRESSION_ALGORITHM);
protected AbstractHeader(Set<Field<?>> fieldSet) {
super(fieldSet);
public DefaultHeader(Map<String, ?> values) {
super(FIELDS, values);
}
protected AbstractHeader(Set<Field<?>> fieldSet, Map<String, ?> values) {
super(fieldSet, values);
protected DefaultHeader(Registry<String, Field<?>> fields, Map<String, ?> values) {
super(fields, values);
}
@Override
@ -49,56 +48,27 @@ public abstract class AbstractHeader<T extends Header<T>> extends JwtMap impleme
return "JWT header";
}
@SuppressWarnings("unchecked")
protected T tthis() {
return (T) this;
}
@Override
public String getType() {
return idiomaticGet(TYPE);
}
@Override
public T setType(String typ) {
put(TYPE, typ);
return tthis();
return get(TYPE);
}
@Override
public String getContentType() {
return idiomaticGet(CONTENT_TYPE);
}
@Override
public T setContentType(String cty) {
put(CONTENT_TYPE, cty);
return tthis();
return get(CONTENT_TYPE);
}
@Override
public String getAlgorithm() {
return idiomaticGet(ALGORITHM);
}
@Override
public T setAlgorithm(String alg) {
put(ALGORITHM, alg);
return tthis();
return get(ALGORITHM);
}
@Override
public String getCompressionAlgorithm() {
String s = idiomaticGet(COMPRESSION_ALGORITHM);
String s = get(COMPRESSION_ALGORITHM);
if (!Strings.hasText(s)) {
s = idiomaticGet(DEPRECATED_COMPRESSION_ALGORITHM);
s = get(DEPRECATED_COMPRESSION_ALGORITHM);
}
return s;
}
@Override
public T setCompressionAlgorithm(String compressionAlgorithm) {
put(COMPRESSION_ALGORITHM, compressionAlgorithm);
return tthis();
}
}

View File

@ -21,33 +21,27 @@ import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
public class DefaultJwe<P> extends DefaultJwt<JweHeader, P> implements Jwe<P> {
import java.security.MessageDigest;
public class DefaultJwe<P> extends DefaultProtectedJwt<JweHeader, P> implements Jwe<P> {
private static final String DIGEST_NAME = "tag";
private final byte[] iv;
private final byte[] aadTag;
public DefaultJwe(JweHeader header, P body, byte[] iv, byte[] aadTag) {
super(header, body);
super(header, body, aadTag, DIGEST_NAME);
this.iv = Assert.notEmpty(iv, "Initialization vector cannot be null or empty.");
this.aadTag = Assert.notEmpty(aadTag, "AAD tag cannot be null or empty.");
}
@Override
public byte[] getInitializationVector() {
return this.iv;
}
@Override
public byte[] getAadTag() {
return this.aadTag;
return this.iv.clone();
}
@Override
protected StringBuilder toStringBuilder() {
StringBuilder sb = super.toStringBuilder();
sb.append(",iv=").append(Encoders.BASE64URL.encode(this.iv));
sb.append(",tag=").append(Encoders.BASE64URL.encode(this.aadTag));
return sb;
return super.toStringBuilder().append(",iv=").append(Encoders.BASE64URL.encode(this.iv));
}
@Override
@ -57,15 +51,13 @@ public class DefaultJwe<P> extends DefaultJwt<JweHeader, P> implements Jwe<P> {
}
if (obj instanceof Jwe) {
Jwe<?> jwe = (Jwe<?>) obj;
return super.equals(jwe) &&
Objects.nullSafeEquals(iv, jwe.getInitializationVector()) &&
Objects.nullSafeEquals(aadTag, jwe.getAadTag());
return super.equals(jwe) && MessageDigest.isEqual(this.iv, jwe.getInitializationVector());
}
return false;
}
@Override
public int hashCode() {
return Objects.nullSafeHashCode(getHeader(), getPayload(), iv, aadTag);
return Objects.nullSafeHashCode(getHeader(), getPayload(), this.iv, this.digest);
}
}

View File

@ -16,6 +16,7 @@
package io.jsonwebtoken.impl;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.impl.lang.Bytes;
import io.jsonwebtoken.impl.lang.Converters;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.Fields;
@ -23,19 +24,18 @@ import io.jsonwebtoken.impl.lang.PositiveIntegerConverter;
import io.jsonwebtoken.impl.lang.RequiredBitLengthConverter;
import io.jsonwebtoken.impl.security.JwkConverter;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.PublicJwk;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
/**
* Header implementation satisfying JWE header parameter requirements.
*
* @since JJWT_RELEASE_VERSION
*/
public class DefaultJweHeader extends AbstractProtectedHeader<JweHeader> implements JweHeader {
public class DefaultJweHeader extends DefaultProtectedHeader implements JweHeader {
static final Field<String> ENCRYPTION_ALGORITHM = Fields.string("enc", "Encryption Algorithm");
@ -60,10 +60,18 @@ public class DefaultJweHeader extends AbstractProtectedHeader<JweHeader> impleme
public static final Field<Integer> P2C = Fields.builder(Integer.class)
.setConverter(PositiveIntegerConverter.INSTANCE).setId("p2c").setName("PBES2 Count").build();
static final Set<Field<?>> FIELDS = Collections.concat(AbstractProtectedHeader.FIELDS, ENCRYPTION_ALGORITHM, EPK, APU, APV, IV, TAG, P2S, P2C);
static final Registry<String, Field<?>> FIELDS =
Fields.registry(DefaultProtectedHeader.FIELDS, ENCRYPTION_ALGORITHM, EPK, APU, APV, IV, TAG, P2S, P2C);
public DefaultJweHeader() {
super(FIELDS);
static boolean isCandidate(FieldMap fields) {
return Strings.hasText(fields.get(ENCRYPTION_ALGORITHM)) || // MUST have at least an `enc` header
!Collections.isEmpty(fields.get(EPK)) ||
!Bytes.isEmpty(fields.get(APU)) ||
!Bytes.isEmpty(fields.get(APV)) ||
!Bytes.isEmpty(fields.get(IV)) ||
!Bytes.isEmpty(fields.get(TAG)) ||
!Bytes.isEmpty(fields.get(P2S)) ||
(fields.get(P2C) != null && fields.get(P2C) > 0);
}
public DefaultJweHeader(Map<String, ?> map) {
@ -77,70 +85,40 @@ public class DefaultJweHeader extends AbstractProtectedHeader<JweHeader> impleme
@Override
public String getEncryptionAlgorithm() {
return idiomaticGet(ENCRYPTION_ALGORITHM);
return get(ENCRYPTION_ALGORITHM);
}
@Override
public PublicJwk<?> getEphemeralPublicKey() {
return idiomaticGet(EPK);
return get(EPK);
}
@Override
public byte[] getAgreementPartyUInfo() {
return idiomaticGet(APU);
}
@Override
public JweHeader setAgreementPartyUInfo(byte[] info) {
put(APU, info);
return this;
}
@Override
public JweHeader setAgreementPartyUInfo(String info) {
byte[] bytes = Strings.hasText(info) ? info.getBytes(StandardCharsets.UTF_8) : null;
return setAgreementPartyUInfo(bytes);
return get(APU);
}
@Override
public byte[] getAgreementPartyVInfo() {
return idiomaticGet(APV);
}
@Override
public JweHeader setAgreementPartyVInfo(byte[] info) {
put(APV, info);
return this;
}
@Override
public JweHeader setAgreementPartyVInfo(String info) {
byte[] bytes = Strings.hasText(info) ? info.getBytes(StandardCharsets.UTF_8) : null;
return setAgreementPartyVInfo(bytes);
return get(APV);
}
@Override
public byte[] getInitializationVector() {
return idiomaticGet(IV);
return get(IV);
}
@Override
public byte[] getAuthenticationTag() {
return idiomaticGet(TAG);
return get(TAG);
}
public byte[] getPbes2Salt() {
return idiomaticGet(P2S);
return get(P2S);
}
@Override
public Integer getPbes2Count() {
return idiomaticGet(P2C);
}
@Override
public JweHeader setPbes2Count(int count) {
put(P2C, count);
return this;
return get(P2C);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 jsonwebtoken.io
* Copyright (C) 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.
@ -15,46 +15,33 @@
*/
package io.jsonwebtoken.impl;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.JweHeaderMutator;
import io.jsonwebtoken.security.X509Builder;
/**
* @param <T> return type for method chaining
* @since JJWT_RELEASE_VERSION
*/
public class DefaultJweHeaderBuilder extends AbstractProtectedHeaderBuilder<JweHeader, JweHeaderBuilder>
implements JweHeaderBuilder {
public class DefaultJweHeaderBuilder<T extends JweHeaderMutator<T> & X509Builder<T>>
extends DefaultJweHeaderMutator<T> implements X509Builder<T> {
@Override
protected JweHeader newHeader() {
return new DefaultJweHeader();
protected DefaultJweHeaderBuilder() {
super();
}
protected DefaultJweHeaderBuilder(DefaultJweHeaderMutator<?> src) {
super(src);
}
@Override
public JweHeaderBuilder setAgreementPartyUInfo(byte[] info) {
this.header.setAgreementPartyUInfo(info);
return this;
public T withX509Sha1Thumbprint(boolean enable) {
this.x509.withX509Sha1Thumbprint(enable);
return self();
}
@Override
public JweHeaderBuilder setAgreementPartyUInfo(String info) {
this.header.setAgreementPartyUInfo(info);
return this;
}
@Override
public JweHeaderBuilder setAgreementPartyVInfo(byte[] info) {
this.header.setAgreementPartyVInfo(info);
return this;
}
@Override
public JweHeaderBuilder setAgreementPartyVInfo(String info) {
this.header.setAgreementPartyVInfo(info);
return this;
}
@Override
public JweHeaderBuilder setPbes2Count(int count) {
this.header.setPbes2Count(count);
return this;
public T withX509Sha256Thumbprint(boolean enable) {
this.x509.withX509Sha256Thumbprint(enable);
return self();
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 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;
import io.jsonwebtoken.JweHeaderMutator;
import io.jsonwebtoken.impl.lang.DelegatingMapMutator;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.security.X509BuilderSupport;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.PublicJwk;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
/**
* @param <T> return type for method chaining
* @since JJWT_RELEASE_VERSION
*/
public class DefaultJweHeaderMutator<T extends JweHeaderMutator<T>>
extends DelegatingMapMutator<String, Object, FieldMap, T> implements JweHeaderMutator<T> {
protected X509BuilderSupport x509;
public DefaultJweHeaderMutator() {
// Any type of header can be created, but JWE fields reflect all potential standard ones, so we use those fields
// to catch any value being set, especially through generic 'put' or 'putAll' methods:
super(new FieldMap(DefaultJweHeader.FIELDS));
clear(); // initialize new X509Builder
}
public DefaultJweHeaderMutator(DefaultJweHeaderMutator<?> src) {
super(src.DELEGATE);
this.x509 = src.x509;
}
// =============================================================
// MapMutator methods
// =============================================================
private T put(Field<?> field, Object value) {
this.DELEGATE.put(field, value);
return self();
}
@Override
public void clear() {
super.clear();
this.x509 = new X509BuilderSupport(this.DELEGATE, IllegalStateException.class);
}
// =============================================================
// JWT Header methods
// =============================================================
@Override
public T setAlgorithm(String alg) {
return put(DefaultHeader.ALGORITHM, alg);
}
@Override
public T setContentType(String cty) {
return put(DefaultHeader.CONTENT_TYPE, cty);
}
@Override
public T setType(String typ) {
return put(DefaultHeader.TYPE, typ);
}
@Override
public T setCompressionAlgorithm(String zip) {
return put(DefaultHeader.COMPRESSION_ALGORITHM, zip);
}
// =============================================================
// Protected Header methods
// =============================================================
@Override
public T setJwkSetUrl(URI uri) {
return put(DefaultProtectedHeader.JKU, uri);
}
@Override
public T setJwk(PublicJwk<?> jwk) {
return put(DefaultProtectedHeader.JWK, jwk);
}
@Override
public T setKeyId(String kid) {
return put(DefaultProtectedHeader.KID, kid);
}
@Override
public T setCritical(Set<String> crit) {
return put(DefaultProtectedHeader.CRIT, crit);
}
// =============================================================
// X.509 methods
// =============================================================
@Override
public T setX509Url(URI uri) {
this.x509.setX509Url(uri);
return self();
}
@Override
public T setX509CertificateChain(List<X509Certificate> chain) {
this.x509.setX509CertificateChain(chain);
return self();
}
@Override
public T setX509CertificateSha1Thumbprint(byte[] thumbprint) {
this.x509.setX509CertificateSha1Thumbprint(thumbprint);
return self();
}
@Override
public T setX509CertificateSha256Thumbprint(byte[] thumbprint) {
this.x509.setX509CertificateSha256Thumbprint(thumbprint);
return self();
}
// =============================================================
// JWE Header methods
// =============================================================
@Override
public T setAgreementPartyUInfo(byte[] info) {
return put(DefaultJweHeader.APU, info);
}
@Override
public T setAgreementPartyUInfo(String info) {
return setAgreementPartyUInfo(Strings.utf8(Strings.clean(info)));
}
@Override
public T setAgreementPartyVInfo(byte[] info) {
return put(DefaultJweHeader.APV, info);
}
@Override
public T setAgreementPartyVInfo(String info) {
return setAgreementPartyVInfo(Strings.utf8(Strings.clean(info)));
}
@Override
public T setPbes2Count(int count) {
return put(DefaultJweHeader.P2C, count);
}
}

View File

@ -17,14 +17,16 @@ package io.jsonwebtoken.impl;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.lang.Objects;
import io.jsonwebtoken.io.Decoders;
public class DefaultJws<P> extends DefaultJwt<JwsHeader, P> implements Jws<P> {
public class DefaultJws<P> extends DefaultProtectedJwt<JwsHeader, P> implements Jws<P> {
private static final String DIGEST_NAME = "signature";
private final String signature;
public DefaultJws(JwsHeader header, P body, String signature) {
super(header, body);
super(header, body, Decoders.BASE64URL.decode(signature), DIGEST_NAME);
this.signature = signature;
}
@ -33,26 +35,4 @@ public class DefaultJws<P> extends DefaultJwt<JwsHeader, P> implements Jws<P> {
return this.signature;
}
@Override
protected StringBuilder toStringBuilder() {
return super.toStringBuilder().append(",signature=").append(signature);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Jws) {
Jws<?> jws = (Jws<?>) obj;
return super.equals(jws) &&
Objects.nullSafeEquals(signature, jws.getSignature());
}
return false;
}
@Override
public int hashCode() {
return Objects.nullSafeHashCode(getHeader(), getPayload(), signature);
}
}

View File

@ -17,17 +17,13 @@ package io.jsonwebtoken.impl;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.lang.Registry;
import java.util.Map;
import java.util.Set;
public class DefaultJwsHeader extends AbstractProtectedHeader<JwsHeader> implements JwsHeader {
public class DefaultJwsHeader extends DefaultProtectedHeader implements JwsHeader {
static final Set<Field<?>> FIELDS = AbstractProtectedHeader.FIELDS; //same
public DefaultJwsHeader() {
super(FIELDS);
}
static final Registry<String, Field<?>> FIELDS = DefaultProtectedHeader.FIELDS; //same
public DefaultJwsHeader(Map<String, ?> map) {
super(FIELDS, map);

View File

@ -21,7 +21,7 @@ import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
public class DefaultJwt<H extends Header<H>, P> implements Jwt<H, P> {
public class DefaultJwt<H extends Header, P> implements Jwt<H, P> {
private final H header;
private final P payload;

View File

@ -16,10 +16,8 @@
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.ClaimsBuilder;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.lang.Bytes;
@ -31,16 +29,15 @@ import io.jsonwebtoken.impl.security.DefaultAeadRequest;
import io.jsonwebtoken.impl.security.DefaultKeyRequest;
import io.jsonwebtoken.impl.security.DefaultSecureRequest;
import io.jsonwebtoken.impl.security.Pbes2HsAkwAlgorithm;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoder;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.io.SerializationException;
import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Objects;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.AeadAlgorithm;
import io.jsonwebtoken.security.AeadRequest;
import io.jsonwebtoken.security.AeadResult;
@ -66,15 +63,15 @@ import java.util.Map;
public class DefaultJwtBuilder implements JwtBuilder {
public static final String PUB_KEY_SIGN_MSG = "PublicKeys may not be used to create digital signatures. " +
"Only PrivateKeys may be used to create digital signatures, and PublicKeys are used to verify " +
"digital signatures.";
public static final String PUB_KEY_SIGN_MSG = "PublicKeys may not be used to create digital signatures. " + "Only PrivateKeys may be used to create digital signatures, and PublicKeys are used to verify " + "digital signatures.";
protected Provider provider;
protected SecureRandom secureRandom;
protected Header<?> header;
protected Claims claims;
private final DefaultJwtBuilderHeader headerBuilder = new DefaultJwtBuilderHeader(this);
private final ClaimsBuilder claimsBuilder = new DefaultClaimsBuilder();
protected byte[] content;
private SecureDigestAlgorithm<Key, ?> sigAlg = Jwts.SIG.NONE;
@ -93,7 +90,12 @@ public class DefaultJwtBuilder implements JwtBuilder {
protected Function<Map<String, ?>, byte[]> claimsSerializer;
protected Encoder<byte[], String> base64UrlEncoder = Encoders.BASE64URL;
protected CompressionCodec compressionCodec;
protected CompressionAlgorithm compressionAlgorithm;
@Override
public JwtBuilder.Header header() {
return this.headerBuilder;
}
@Override
public JwtBuilder setProvider(Provider provider) {
@ -143,50 +145,28 @@ public class DefaultJwtBuilder implements JwtBuilder {
}
@Override
public JwtBuilder setHeader(Header<?> header) {
return setHeader(Jwts.header().putAll(header));
}
@Override
public JwtBuilder setHeader(Map<String, ?> header) {
return setHeader(Jwts.header().putAll(header));
}
@Override
public JwtBuilder setHeader(Builder<? extends Header<?>> builder) {
Assert.notNull(builder, "Builder cannot be null.");
Header<?> header = builder.build();
this.header = Assert.notNull(header, "Builder cannot produce a null Header instance.");
public JwtBuilder setHeader(Map<String, ?> map) {
this.headerBuilder.clear();
this.headerBuilder.putAll(map);
return this;
}
@Override
public JwtBuilder setHeaderParams(Map<String, ?> params) {
if (!Collections.isEmpty(params)) {
Header<?> header = ensureHeader();
header.putAll(params);
}
this.headerBuilder.putAll(params);
return this;
}
protected Header<?> ensureHeader() {
if (this.header == null) {
this.header = new DefaultUnprotectedHeader();
}
return this.header;
}
@Override
public JwtBuilder setHeaderParam(String name, Object value) {
ensureHeader().put(name, value);
this.headerBuilder.put(name, value);
return this;
}
@SuppressWarnings("unchecked") // TODO: remove for 1.0
protected static <K extends Key> SecureDigestAlgorithm<K, ?> forSigningKey(K key) {
@SuppressWarnings("deprecation")
io.jsonwebtoken.SignatureAlgorithm alg = io.jsonwebtoken.SignatureAlgorithm.forSigningKey(key);
return (SecureDigestAlgorithm<K, ?>) Jwts.SIG.get(alg.getValue());
@SuppressWarnings("deprecation") io.jsonwebtoken.SignatureAlgorithm alg = io.jsonwebtoken.SignatureAlgorithm.forSigningKey(key);
return (SecureDigestAlgorithm<K, ?>) Jwts.SIG.get().forKey(alg.getValue());
}
@Override
@ -239,7 +219,7 @@ public class DefaultJwtBuilder implements JwtBuilder {
public JwtBuilder signWith(Key key, io.jsonwebtoken.SignatureAlgorithm alg) throws InvalidKeyException {
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
alg.assertValidSigningKey(key); //since 0.10.0 for https://github.com/jwtk/jjwt/issues/334
return signWith(key, (SecureDigestAlgorithm<? super Key, ?>) Jwts.SIG.get(alg.getValue()));
return signWith(key, (SecureDigestAlgorithm<? super Key, ?>) Jwts.SIG.get().forKey(alg.getValue()));
}
@SuppressWarnings("deprecation") // TODO: remove method for 1.0
@ -304,9 +284,10 @@ public class DefaultJwtBuilder implements JwtBuilder {
}
@Override
public JwtBuilder compressWith(CompressionCodec compressionCodec) {
Assert.notNull(compressionCodec, "compressionCodec cannot be null");
this.compressionCodec = compressionCodec;
public JwtBuilder compressWith(CompressionAlgorithm alg) {
Assert.notNull(alg, "CompressionAlgorithm cannot be null");
Assert.hasText(alg.getId(), "CompressionAlgorithm id cannot be null or empty.");
this.compressionAlgorithm = alg;
return this;
}
@ -327,87 +308,75 @@ public class DefaultJwtBuilder implements JwtBuilder {
Assert.notEmpty(content, "content byte array cannot be null or empty.");
Assert.hasText(cty, "Content Type String cannot be null or empty.");
cty = CompactMediaTypeIdConverter.INSTANCE.applyFrom(cty);
ensureHeader().setContentType(cty);
this.headerBuilder.setContentType(cty);
return setContent(content);
}
protected Claims ensureClaims() {
if (this.claims == null) {
this.claims = new DefaultClaims();
}
return this.claims;
}
@Override
public JwtBuilder setClaims(Claims claims) {
this.claims = claims;
return this;
Assert.notNull(claims, "Claims argument cannot be null.");
return setClaims((Map<String, ?>) claims);
}
@Override
public JwtBuilder setClaims(Map<String, ?> claims) {
this.claims = new DefaultClaims(claims);
Assert.notNull(claims, "Claims map cannot be null.");
this.claimsBuilder.empty();
this.claimsBuilder.set(claims);
return this;
}
@Override
public JwtBuilder addClaims(Map<String, ?> claims) {
ensureClaims().putAll(claims);
this.claimsBuilder.set(claims);
return this;
}
@Override
public JwtBuilder setIssuer(String iss) {
return claim(DefaultClaims.ISSUER.getId(), iss);
this.claimsBuilder.setIssuer(iss);
return this;
}
@Override
public JwtBuilder setSubject(String sub) {
return claim(DefaultClaims.SUBJECT.getId(), sub);
this.claimsBuilder.setSubject(sub);
return this;
}
@Override
public JwtBuilder setAudience(String aud) {
return claim(DefaultClaims.AUDIENCE.getId(), aud);
this.claimsBuilder.setAudience(aud);
return this;
}
@Override
public JwtBuilder setExpiration(Date exp) {
return claim(DefaultClaims.EXPIRATION.getId(), exp);
this.claimsBuilder.setExpiration(exp);
return this;
}
@Override
public JwtBuilder setNotBefore(Date nbf) {
return claim(DefaultClaims.NOT_BEFORE.getId(), nbf);
this.claimsBuilder.setNotBefore(nbf);
return this;
}
@Override
public JwtBuilder setIssuedAt(Date iat) {
return claim(DefaultClaims.ISSUED_AT.getId(), iat);
this.claimsBuilder.setIssuedAt(iat);
return this;
}
@Override
public JwtBuilder setId(String jti) {
return claim(DefaultClaims.JTI.getId(), jti);
this.claimsBuilder.setId(jti);
return this;
}
@Override
public JwtBuilder claim(String name, Object value) {
Assert.hasText(name, "Claim property name cannot be null or empty.");
if (value instanceof String && !Strings.hasText((String) value)) {
value = null;
}
if (this.claims == null) {
if (value != null) {
ensureClaims().put(name, value);
}
} else {
if (value == null) {
this.claims.remove(name);
} else {
this.claims.put(name, value);
}
}
this.claimsBuilder.set(name, value);
return this;
}
@ -421,6 +390,8 @@ public class DefaultJwtBuilder implements JwtBuilder {
throw new IllegalStateException(msg);
}
final Claims claims = this.claimsBuilder.build();
if (Objects.isEmpty(content) && Collections.isEmpty(claims)) {
if (jwe) { // JWE payload can never be empty:
String msg = "Encrypted JWTs must have either 'claims' or non-empty 'content'.";
@ -433,8 +404,6 @@ public class DefaultJwtBuilder implements JwtBuilder {
throw new IllegalStateException("Both 'content' and 'claims' cannot both be specified. Choose either one.");
}
Header<?> header = ensureHeader();
if (this.serializer == null) { // try to find one based on the services available
//noinspection unchecked
serializeToJsonWith(Services.loadFirst(Serializer.class));
@ -444,28 +413,33 @@ public class DefaultJwtBuilder implements JwtBuilder {
if (!Collections.isEmpty(claims)) {
payload = claimsSerializer.apply(claims);
}
if (!Objects.isEmpty(payload) && compressionCodec != null) {
payload = compressionCodec.compress(payload);
header.setCompressionAlgorithm(compressionCodec.getId());
if (!Objects.isEmpty(payload) && compressionAlgorithm != null) {
payload = compressionAlgorithm.compress(payload);
this.headerBuilder.setCompressionAlgorithm(compressionAlgorithm.getId());
}
if (jwe) {
JweHeader jweHeader = header instanceof JweHeader ? (JweHeader) header : new DefaultJweHeader(header);
return encrypt(jweHeader, payload);
return encrypt(payload);
} else {
return compact(header, payload);
return compact(payload);
}
}
private String compact(Header<?> header, byte[] payload) {
private io.jsonwebtoken.Header buildHeader() {
return new DefaultJwtHeaderBuilder(this.headerBuilder).build();
}
private String compact(byte[] payload) {
Assert.stateNotNull(sigAlg, "SignatureAlgorithm is required."); // invariant
if (this.key != null && !(header instanceof JwsHeader)) {
header = new DefaultJwsHeader(header);
}
// if (this.key != null && !(header instanceof JwsHeader)) {
// header = new DefaultJwsHeader(header);
// }
header.setAlgorithm(sigAlg.getId());
this.headerBuilder.setAlgorithm(sigAlg.getId());
final io.jsonwebtoken.Header header = buildHeader();
byte[] headerBytes = headerSerializer.apply(header);
String base64UrlEncodedHeader = base64UrlEncoder.encode(headerBytes);
@ -490,7 +464,7 @@ public class DefaultJwtBuilder implements JwtBuilder {
return jwt;
}
private String encrypt(JweHeader header, byte[] payload) {
private String encrypt(byte[] payload) {
Assert.stateNotNull(key, "Key is required."); // set by encryptWith*
Assert.stateNotNull(enc, "Encryption algorithm is required."); // set by encryptWith*
@ -499,14 +473,20 @@ public class DefaultJwtBuilder implements JwtBuilder {
Assert.stateNotNull(keyAlgFunction, "KeyAlgorithm function cannot be null.");
Assert.notEmpty(payload, "JWE payload bytes cannot be empty."); // JWE invariant (JWS can be empty however)
KeyRequest<Key> keyRequest = new DefaultKeyRequest<>(this.key, this.provider, this.secureRandom, header, enc);
//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<Key> keyRequest = new DefaultKeyRequest<>(this.key, this.provider, this.secureRandom, delegate, enc);
KeyResult keyResult = keyAlgFunction.apply(keyRequest);
Assert.stateNotNull(keyResult, "KeyAlgorithm must return a KeyResult.");
SecretKey cek = Assert.notNull(keyResult.getKey(), "KeyResult must return a content encryption key.");
byte[] encryptedCek = Assert.notNull(keyResult.getPayload(), "KeyResult must return an encrypted key byte array, even if empty.");
header.put(AbstractHeader.ALGORITHM.getId(), keyAlg.getId());
header.put(DefaultJweHeader.ENCRYPTION_ALGORITHM.getId(), enc.getId());
this.headerBuilder.setAlgorithm(keyAlg.getId());
this.headerBuilder.put(DefaultJweHeader.ENCRYPTION_ALGORITHM.getId(), enc.getId());
final io.jsonwebtoken.Header header = buildHeader();
byte[] headerBytes = this.headerSerializer.apply(header);
final String base64UrlEncodedHeader = base64UrlEncoder.encode(headerBytes);
@ -524,10 +504,22 @@ public class DefaultJwtBuilder implements JwtBuilder {
String base64UrlEncodedCiphertext = base64UrlEncoder.encode(ciphertext);
String base64UrlEncodedTag = base64UrlEncoder.encode(tag);
return base64UrlEncodedHeader + DefaultJwtParser.SEPARATOR_CHAR +
base64UrlEncodedEncryptedCek + DefaultJwtParser.SEPARATOR_CHAR +
base64UrlEncodedIv + DefaultJwtParser.SEPARATOR_CHAR +
base64UrlEncodedCiphertext + DefaultJwtParser.SEPARATOR_CHAR +
base64UrlEncodedTag;
return base64UrlEncodedHeader + DefaultJwtParser.SEPARATOR_CHAR + base64UrlEncodedEncryptedCek + DefaultJwtParser.SEPARATOR_CHAR + base64UrlEncodedIv + DefaultJwtParser.SEPARATOR_CHAR + base64UrlEncodedCiphertext + DefaultJwtParser.SEPARATOR_CHAR + base64UrlEncodedTag;
}
private static class DefaultJwtBuilderHeader extends DefaultJweHeaderBuilder<Header>
implements JwtBuilder.Header {
private final JwtBuilder builder;
public DefaultJwtBuilderHeader(JwtBuilder builder) {
super();
this.builder = Assert.notNull(builder, "JwtBuilder cannot be null.");
}
@Override
public JwtBuilder and() {
return builder;
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
/**
* @since JJWT_RELEASE_VERSION
*/
public class DefaultJwtHeaderBuilder extends DefaultJweHeaderBuilder<Jwts.HeaderBuilder> implements Jwts.HeaderBuilder {
@SuppressWarnings("unused") // accessed via reflection from the Jwts.header() method implementation
public DefaultJwtHeaderBuilder() {
}
public DefaultJwtHeaderBuilder(DefaultJweHeaderMutator<?> src) {
super(src);
}
@Override
public Header build() {
this.x509.apply(); // apply any X.509 values as necessary based on builder state
//Use a copy constructor to ensure subsequent changes to builder state do not change the constructed header
// Note: conditional sequence matters here: JWE has more specific requirements than JWS, so check that first:
if (DefaultJweHeader.isCandidate(this.DELEGATE)) {
return new DefaultJweHeader(this.DELEGATE);
} else if (DefaultProtectedHeader.isCandidate(this.DELEGATE)) {
return new DefaultJwsHeader(this.DELEGATE);
} else {
return new DefaultHeader(this.DELEGATE);
}
}
}

View File

@ -16,12 +16,11 @@
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ClaimsBuilder;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.IncorrectClaimException;
import io.jsonwebtoken.Jwe;
import io.jsonwebtoken.JweHeader;
@ -38,18 +37,16 @@ import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.MissingClaimException;
import io.jsonwebtoken.PrematureJwtException;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.UnprotectedHeader;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
import io.jsonwebtoken.impl.lang.Bytes;
import io.jsonwebtoken.impl.lang.Function;
import io.jsonwebtoken.impl.lang.IdRegistry;
import io.jsonwebtoken.impl.lang.LegacyServices;
import io.jsonwebtoken.impl.security.ConstantKeyLocator;
import io.jsonwebtoken.impl.security.DefaultAeadResult;
import io.jsonwebtoken.impl.security.DefaultDecryptionKeyRequest;
import io.jsonwebtoken.impl.security.DefaultVerifySecureDigestRequest;
import io.jsonwebtoken.impl.security.LocatingKeyResolver;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.io.Decoder;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.DecodingException;
@ -78,7 +75,6 @@ import java.security.Key;
import java.security.Provider;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Map;
@SuppressWarnings("unchecked")
@ -114,12 +110,12 @@ public class DefaultJwtParser implements JwtParser {
"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.2 for more information.";
private static final String UNSECURED_DISABLED_MSG_PREFIX = "Unsecured JWSs (those with an " +
AbstractHeader.ALGORITHM + " header value of '" + Jwts.SIG.NONE.getId() + "') are disallowed by " +
DefaultHeader.ALGORITHM + " header value of '" + Jwts.SIG.NONE.getId() + "') are disallowed by " +
"default as mandated by https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6. If you wish to " +
"allow them to be parsed, call the JwtParserBuilder.enableUnsecuredJws() method (but please read the " +
"security considerations covered in that method's JavaDoc before doing so). Header: ";
private static final String JWE_NONE_MSG = "JWEs do not support key management " + AbstractHeader.ALGORITHM +
private static final String JWE_NONE_MSG = "JWEs do not support key management " + DefaultHeader.ALGORITHM +
" header value '" + Jwts.SIG.NONE.getId() + "' per " +
"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1";
@ -127,7 +123,7 @@ public class DefaultJwtParser implements JwtParser {
Jwts.SIG.NONE.getId() + "' yet the compact JWS string contains a signature. This is not permitted " +
"per https://tools.ietf.org/html/rfc7518#section-3.6.";
private static final String UNPROTECTED_DECOMPRESSION_MSG = "The JWT header references compression algorithm " +
"'%s', but payload decompression for Unsecured JWTs (those with an " + AbstractHeader.ALGORITHM +
"'%s', but payload decompression for Unsecured JWTs (those with an " + DefaultHeader.ALGORITHM +
" header value of '" + Jwts.SIG.NONE.getId() + "') are " + "disallowed by default to protect " +
"against [Denial of Service attacks](" +
"https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf). If you " +
@ -135,29 +131,20 @@ public class DefaultJwtParser implements JwtParser {
"enableUnsecuredDecompression() method (but please read the security considerations covered in that " +
"method's JavaDoc before doing so).";
private static <I extends Identifiable> IdRegistry<I> newRegistry(String name, Collection<I> defaults, Collection<I> extras) {
Collection<I> all = new LinkedHashSet<>(Collections.size(extras) + defaults.size());
all.addAll(extras);
all.addAll(defaults);
return new IdRegistry<>(name, all);
}
private static Function<JwsHeader, SecureDigestAlgorithm<?, ?>> sigFn(Collection<SecureDigestAlgorithm<?, ?>> extras) {
String name = "JWS MAC or Signature Algorithm";
IdRegistry<SecureDigestAlgorithm<?, ?>> registry = newRegistry(name, Jwts.SIG.values(), extras);
return new IdLocator<>(AbstractHeader.ALGORITHM, MISSING_JWS_ALG_MSG, registry);
return new IdLocator<>(DefaultHeader.ALGORITHM, Jwts.SIG.get(), extras, MISSING_JWS_ALG_MSG);
}
private static Function<JweHeader, AeadAlgorithm> encFn(Collection<AeadAlgorithm> extras) {
String name = "JWE Encryption Algorithm";
IdRegistry<AeadAlgorithm> registry = newRegistry(name, Jwts.ENC.values(), extras);
return new IdLocator<>(DefaultJweHeader.ENCRYPTION_ALGORITHM, MISSING_ENC_MSG, registry);
return new IdLocator<>(DefaultJweHeader.ENCRYPTION_ALGORITHM, Jwts.ENC.get(), extras, MISSING_ENC_MSG);
}
private static Function<JweHeader, KeyAlgorithm<?, ?>> keyFn(Collection<KeyAlgorithm<?, ?>> extras) {
String name = "JWE Key Management Algorithm";
IdRegistry<KeyAlgorithm<?, ?>> registry = newRegistry(name, Jwts.KEY.values(), extras);
return new IdLocator<>(AbstractHeader.ALGORITHM, MISSING_JWE_ALG_MSG, registry);
return new IdLocator<>(DefaultHeader.ALGORITHM, Jwts.KEY.get(), extras, MISSING_JWE_ALG_MSG);
}
private static IdLocator<Header, CompressionAlgorithm> zipFn(Collection<CompressionAlgorithm> extras) {
return new IdLocator<>(DefaultHeader.COMPRESSION_ALGORITHM, Jwts.ZIP.get(), extras, null);
}
// TODO: make the following fields final for v1.0
@ -166,8 +153,6 @@ public class DefaultJwtParser implements JwtParser {
@SuppressWarnings("deprecation") // will remove for 1.0
private SigningKeyResolver signingKeyResolver;
private Locator<CompressionCodec> compressionCodecLocator;
private final boolean enableUnsecuredJws;
private final boolean enableUnsecuredDecompression;
@ -178,13 +163,15 @@ public class DefaultJwtParser implements JwtParser {
private final Function<JweHeader, KeyAlgorithm<?, ?>> keyAlgorithmLocator;
private Function<Header, CompressionAlgorithm> compressionAlgorithmLocator;
private final Locator<? extends Key> keyLocator;
private Decoder<String, byte[]> base64UrlDecoder = Decoders.BASE64URL;
private Deserializer<Map<String, ?>> deserializer;
private Claims expectedClaims = new DefaultClaims();
private ClaimsBuilder expectedClaims = Jwts.claims();
private Clock clock = DefaultClock.INSTANCE;
@ -202,7 +189,7 @@ public class DefaultJwtParser implements JwtParser {
this.signatureAlgorithmLocator = sigFn(Collections.<SecureDigestAlgorithm<?, ?>>emptyList());
this.keyAlgorithmLocator = keyFn(Collections.<KeyAlgorithm<?, ?>>emptyList());
this.encryptionAlgorithmLocator = encFn(Collections.<AeadAlgorithm>emptyList());
this.compressionCodecLocator = new DefaultCompressionCodecResolver();
this.compressionAlgorithmLocator = zipFn(Collections.<CompressionAlgorithm>emptyList());
this.enableUnsecuredJws = false;
this.enableUnsecuredDecompression = false;
}
@ -216,10 +203,11 @@ public class DefaultJwtParser implements JwtParser {
Locator<? extends Key> keyLocator,
Clock clock,
long allowedClockSkewMillis,
Claims expectedClaims,
DefaultClaims expectedClaims,
Decoder<String, byte[]> base64UrlDecoder,
Deserializer<Map<String, ?>> deserializer,
Locator<CompressionCodec> compressionCodecLocator,
CompressionCodecResolver compressionCodecResolver,
Collection<CompressionAlgorithm> extraZipAlgs,
Collection<SecureDigestAlgorithm<?, ?>> extraSigAlgs,
Collection<KeyAlgorithm<?, ?>> extraKeyAlgs,
Collection<AeadAlgorithm> extraEncAlgs) {
@ -230,13 +218,18 @@ public class DefaultJwtParser implements JwtParser {
this.keyLocator = Assert.notNull(keyLocator, "Key Locator cannot be null.");
this.clock = clock;
this.allowedClockSkewMillis = allowedClockSkewMillis;
this.expectedClaims = expectedClaims;
this.expectedClaims = Jwts.claims().set(expectedClaims);
this.base64UrlDecoder = base64UrlDecoder;
this.deserializer = deserializer;
this.signatureAlgorithmLocator = sigFn(extraSigAlgs);
this.keyAlgorithmLocator = keyFn(extraKeyAlgs);
this.encryptionAlgorithmLocator = encFn(extraEncAlgs);
this.compressionCodecLocator = Assert.notNull(compressionCodecLocator, "CompressionCodec locator cannot be null.");
if (compressionCodecResolver != null) {
this.compressionAlgorithmLocator = new CompressionCodecLocator(compressionCodecResolver);
} else {
this.compressionAlgorithmLocator = zipFn(extraZipAlgs);
}
}
@Override
@ -299,7 +292,7 @@ public class DefaultJwtParser implements JwtParser {
public JwtParser require(String claimName, Object value) {
Assert.hasText(claimName, "claim name cannot be null or empty.");
Assert.notNull(value, "The value cannot be null for claim name: " + claimName);
expectedClaims.put(claimName, value);
expectedClaims.set(claimName, value);
return this;
}
@ -347,9 +340,9 @@ public class DefaultJwtParser implements JwtParser {
@SuppressWarnings("deprecation")
@Override
public JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) {
Assert.notNull(compressionCodecResolver, "compressionCodecResolver cannot be null.");
this.compressionCodecLocator = new CompressionCodecLocator(compressionCodecResolver);
public JwtParser setCompressionCodecResolver(CompressionCodecResolver resolver) {
Assert.notNull(resolver, "CompressionCodecResolver cannot be null.");
this.compressionAlgorithmLocator = new CompressionCodecLocator(resolver);
return this;
}
@ -366,7 +359,7 @@ public class DefaultJwtParser implements JwtParser {
}
}
private static boolean hasContentType(Header<?> header) {
private static boolean hasContentType(Header header) {
return header != null && Strings.hasText(header.getContentType());
}
@ -522,7 +515,7 @@ public class DefaultJwtParser implements JwtParser {
// =============== Header =================
final byte[] headerBytes = base64UrlDecode(base64UrlHeader, "protected header");
Map<String, ?> m = readValue(headerBytes, "protected header");
Header<?> header;
Header header;
try {
header = tokenized.createHeader(m);
} catch (Exception e) {
@ -646,13 +639,13 @@ public class DefaultJwtParser implements JwtParser {
verifySignature(tokenized, ((JwsHeader) header), alg, new LocatingKeyResolver(this.keyLocator), null, null);
}
CompressionCodec compressionCodec = compressionCodecLocator.locate(header);
if (compressionCodec != null) {
CompressionAlgorithm compressionAlgorithm = compressionAlgorithmLocator.apply(header);
if (compressionAlgorithm != null) {
if (unsecured && !enableUnsecuredDecompression) {
String msg = String.format(UNPROTECTED_DECOMPRESSION_MSG, compressionCodec.getId());
String msg = String.format(UNPROTECTED_DECOMPRESSION_MSG, compressionAlgorithm.getId());
throw new UnsupportedJwtException(msg);
}
payload = compressionCodec.decompress(payload);
payload = compressionAlgorithm.decompress(payload);
}
Claims claims = null;
@ -757,11 +750,13 @@ public class DefaultJwtParser implements JwtParser {
return o;
}
private void validateExpectedClaims(Header<?> header, Claims claims) {
private void validateExpectedClaims(Header header, Claims claims) {
for (String expectedClaimName : expectedClaims.keySet()) {
final Claims expected = expectedClaims.build();
Object expectedClaimValue = normalize(expectedClaims.get(expectedClaimName));
for (String expectedClaimName : expected.keySet()) {
Object expectedClaimValue = normalize(expected.get(expectedClaimName));
Object actualClaimValue = normalize(claims.get(expectedClaimName));
if (expectedClaimValue instanceof Date) {
@ -813,28 +808,28 @@ public class DefaultJwtParser implements JwtParser {
} else {
Object body = jwt.getPayload();
if (body instanceof Claims) {
return handler.onClaimsJwt((Jwt<UnprotectedHeader, Claims>) jwt);
return handler.onClaimsJwt((Jwt<Header, Claims>) jwt);
} else {
return handler.onContentJwt((Jwt<UnprotectedHeader, byte[]>) jwt);
return handler.onContentJwt((Jwt<Header, byte[]>) jwt);
}
}
}
@Override
public Jwt<UnprotectedHeader, byte[]> parseContentJwt(String compact) {
return parse(compact, new JwtHandlerAdapter<Jwt<UnprotectedHeader, byte[]>>() {
public Jwt<Header, byte[]> parseContentJwt(String compact) {
return parse(compact, new JwtHandlerAdapter<Jwt<Header, byte[]>>() {
@Override
public Jwt<UnprotectedHeader, byte[]> onContentJwt(Jwt<UnprotectedHeader, byte[]> jwt) {
public Jwt<Header, byte[]> onContentJwt(Jwt<Header, byte[]> jwt) {
return jwt;
}
});
}
@Override
public Jwt<UnprotectedHeader, Claims> parseClaimsJwt(String compact) {
return parse(compact, new JwtHandlerAdapter<Jwt<UnprotectedHeader, Claims>>() {
public Jwt<Header, Claims> parseClaimsJwt(String compact) {
return parse(compact, new JwtHandlerAdapter<Jwt<Header, Claims>>() {
@Override
public Jwt<UnprotectedHeader, Claims> onClaimsJwt(Jwt<UnprotectedHeader, Claims> jwt) {
public Jwt<Header, Claims> onClaimsJwt(Jwt<Header, Claims> jwt) {
return jwt;
}
});

View File

@ -15,17 +15,17 @@
*/
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ClaimsBuilder;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Locator;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
import io.jsonwebtoken.impl.lang.Services;
import io.jsonwebtoken.impl.security.ConstantKeyLocator;
import io.jsonwebtoken.io.CompressionAlgorithm;
import io.jsonwebtoken.io.Decoder;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Deserializer;
@ -71,21 +71,22 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
@SuppressWarnings("deprecation") //TODO: remove for 1.0
private SigningKeyResolver signingKeyResolver = null;
private Locator<CompressionCodec> compressionCodecLocator;
private final Collection<AeadAlgorithm> extraEncAlgs = new LinkedHashSet<>();
private final Collection<AeadAlgorithm> extraEncryptionAlgorithms = new LinkedHashSet<>();
private final Collection<KeyAlgorithm<?, ?>> extraKeyAlgs = new LinkedHashSet<>();
private final Collection<KeyAlgorithm<?, ?>> extraKeyAlgorithms = new LinkedHashSet<>();
private final Collection<SecureDigestAlgorithm<?, ?>> extraSigAlgs = new LinkedHashSet<>();
private final Collection<SecureDigestAlgorithm<?, ?>> extraDigestAlgorithms = new LinkedHashSet<>();
private final Collection<CompressionAlgorithm> extraZipAlgs = new LinkedHashSet<>();
private final Collection<CompressionCodec> extraCompressionCodecs = new LinkedHashSet<>();
@SuppressWarnings("deprecation")
private CompressionCodecResolver compressionCodecResolver;
private Decoder<String, byte[]> base64UrlDecoder = Decoders.BASE64URL;
private Deserializer<Map<String, ?>> deserializer;
private final Claims expectedClaims = new DefaultClaims();
private final ClaimsBuilder expectedClaims = Jwts.claims();
private Clock clock = DefaultClock.INSTANCE;
@ -172,7 +173,7 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
public JwtParserBuilder require(String claimName, Object value) {
Assert.hasText(claimName, "claim name cannot be null or empty.");
Assert.notNull(value, "The value cannot be null for claim name: " + claimName);
expectedClaims.put(claimName, value);
expectedClaims.set(claimName, value);
return this;
}
@ -221,30 +222,30 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
}
@Override
public JwtParserBuilder addCompressionCodecs(Collection<? extends CompressionCodec> codecs) {
Assert.notEmpty(codecs, "Additional CompressionCodec collection cannot be null or empty.");
this.extraCompressionCodecs.addAll(codecs);
public JwtParserBuilder addCompressionAlgorithms(Collection<? extends CompressionAlgorithm> algs) {
Assert.notEmpty(algs, "Additional CompressionAlgorithm collection cannot be null or empty.");
this.extraZipAlgs.addAll(algs);
return this;
}
@Override
public JwtParserBuilder addEncryptionAlgorithms(Collection<? extends AeadAlgorithm> encAlgs) {
Assert.notEmpty(encAlgs, "Additional AeadAlgorithm collection cannot be null or empty.");
this.extraEncryptionAlgorithms.addAll(encAlgs);
this.extraEncAlgs.addAll(encAlgs);
return this;
}
@Override
public JwtParserBuilder addSignatureAlgorithms(Collection<? extends SecureDigestAlgorithm<?, ?>> sigAlgs) {
Assert.notEmpty(sigAlgs, "Additional SignatureAlgorithm collection cannot be null or empty.");
this.extraDigestAlgorithms.addAll(sigAlgs);
this.extraSigAlgs.addAll(sigAlgs);
return this;
}
@Override
public JwtParserBuilder addKeyAlgorithms(Collection<? extends KeyAlgorithm<?, ?>> keyAlgs) {
Assert.notEmpty(keyAlgs, "Additional KeyAlgorithm collection cannot be null or empty.");
this.extraKeyAlgorithms.addAll(keyAlgs);
this.extraKeyAlgs.addAll(keyAlgs);
return this;
}
@ -262,16 +263,10 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
return this;
}
@Override
public JwtParserBuilder setCompressionCodecLocator(Locator<CompressionCodec> locator) {
this.compressionCodecLocator = Assert.notNull(locator, "CompressionCodec locator cannot be null.");
return this;
}
@SuppressWarnings("deprecation")
@Override
public JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver resolver) {
Assert.notNull(resolver, "compressionCodecResolver cannot be null.");
this.compressionCodecLocator = new CompressionCodecLocator(resolver);
this.compressionCodecResolver = Assert.notNull(resolver, "CompressionCodecResolver cannot be null.");
return this;
}
@ -305,19 +300,17 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
"due to their security implications.";
throw new IllegalStateException(msg);
}
if (this.compressionCodecLocator != null && !Collections.isEmpty(extraCompressionCodecs)) {
String msg = "Both 'addCompressionCodecs' and 'compressionCodecLocator' " +
"(or 'compressionCodecResolver') cannot be specified. Choose either.";
if (this.compressionCodecResolver != null && !Collections.isEmpty(extraZipAlgs)) {
String msg = "Both 'addCompressionAlgorithms' and 'compressionCodecResolver' " +
"cannot be specified. Choose either.";
throw new IllegalStateException(msg);
}
if (this.compressionCodecLocator == null) {
this.compressionCodecLocator = new DefaultCompressionCodecResolver(extraCompressionCodecs);
}
// Invariants. If these are ever violated, it's an error in this class implementation
// (we default to non-null instances, and the setters should never allow null):
Assert.stateNotNull(this.keyLocator, "Key locator should never be null.");
Assert.stateNotNull(this.compressionCodecLocator, "CompressionCodec Locator should never be null.");
final DefaultClaims expClaims = (DefaultClaims) this.expectedClaims.build();
return new ImmutableJwtParser(new DefaultJwtParser(
provider,
@ -327,13 +320,14 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
keyLocator,
clock,
allowedClockSkewMillis,
expectedClaims,
expClaims,
base64UrlDecoder,
new JwtDeserializer<>(deserializer),
compressionCodecLocator,
extraDigestAlgorithms,
extraKeyAlgorithms,
extraEncryptionAlgorithms
compressionCodecResolver,
extraZipAlgs,
extraSigAlgs,
extraKeyAlgs,
extraEncAlgs
));
}
}

View File

@ -0,0 +1,152 @@
/*
* 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;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.security.PublicJwk;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
public class DefaultMutableJweHeader extends DefaultJweHeaderMutator<DefaultMutableJweHeader> implements JweHeader {
public DefaultMutableJweHeader(DefaultJweHeaderMutator<?> src) {
super(src);
}
private <T> T get(Field<T> field) {
return this.DELEGATE.get(field);
}
// =============================================================
// JWT Header methods
// =============================================================
@Override
public String getAlgorithm() {
return get(DefaultHeader.ALGORITHM);
}
@Override
public String getContentType() {
return get(DefaultHeader.CONTENT_TYPE);
}
@Override
public String getType() {
return get(DefaultHeader.TYPE);
}
@Override
public String getCompressionAlgorithm() {
return get(DefaultHeader.COMPRESSION_ALGORITHM);
}
// =============================================================
// Protected Header methods
// =============================================================
@Override
public URI getJwkSetUrl() {
return get(DefaultProtectedHeader.JKU);
}
@Override
public PublicJwk<?> getJwk() {
return get(DefaultProtectedHeader.JWK);
}
@Override
public String getKeyId() {
return get(DefaultProtectedHeader.KID);
}
@Override
public Set<String> getCritical() {
return get(DefaultProtectedHeader.CRIT);
}
// =============================================================
// X.509 methods
// =============================================================
@Override
public URI getX509Url() {
return get(DefaultProtectedHeader.X5U);
}
@Override
public List<X509Certificate> getX509CertificateChain() {
return get(DefaultProtectedHeader.X5C);
}
@Override
public byte[] getX509CertificateSha1Thumbprint() {
return get(DefaultProtectedHeader.X5T);
}
@Override
public byte[] getX509CertificateSha256Thumbprint() {
return get(DefaultProtectedHeader.X5T_S256);
}
// =============================================================
// JWE Header methods
// =============================================================
@Override
public byte[] getAgreementPartyUInfo() {
return get(DefaultJweHeader.APU);
}
@Override
public byte[] getAgreementPartyVInfo() {
return get(DefaultJweHeader.APV);
}
@Override
public Integer getPbes2Count() {
return get(DefaultJweHeader.P2C);
}
@Override
public String getEncryptionAlgorithm() {
return get(DefaultJweHeader.ENCRYPTION_ALGORITHM);
}
@Override
public PublicJwk<?> getEphemeralPublicKey() {
return get(DefaultJweHeader.EPK);
}
@Override
public byte[] getInitializationVector() {
return get(DefaultJweHeader.IV);
}
@Override
public byte[] getAuthenticationTag() {
return get(DefaultJweHeader.TAG);
}
@Override
public byte[] getPbes2Salt() {
return get(DefaultJweHeader.P2S);
}
}

View File

@ -15,13 +15,17 @@
*/
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.ProtectedHeader;
import io.jsonwebtoken.impl.lang.Bytes;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.Fields;
import io.jsonwebtoken.impl.security.AbstractAsymmetricJwk;
import io.jsonwebtoken.impl.security.AbstractJwk;
import io.jsonwebtoken.impl.security.JwkConverter;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
import io.jsonwebtoken.security.PublicJwk;
import java.net.URI;
@ -34,10 +38,9 @@ import java.util.Set;
* Header implementation satisfying shared JWS and JWE header parameter requirements. Header parameters specific to
* either JWE or JWS will be defined in respective subclasses.
*
* @param <T> specific header type to return from mutation/setter methods for method chaining
* @since JJWT_RELEASE_VERSION
*/
public abstract class AbstractProtectedHeader<T extends ProtectedHeader<T>> extends AbstractHeader<T> implements ProtectedHeader<T> {
public class DefaultProtectedHeader extends DefaultHeader implements ProtectedHeader {
static final Field<URI> JKU = Fields.uri("jku", "JWK Set URL");
@ -47,86 +50,73 @@ public abstract class AbstractProtectedHeader<T extends ProtectedHeader<T>> exte
.setConverter(JwkConverter.PUBLIC_JWK).build();
static final Field<Set<String>> CRIT = Fields.stringSet("crit", "Critical");
static final Set<Field<?>> FIELDS = Collections.concat(AbstractHeader.FIELDS, CRIT, JKU, JWK, AbstractJwk.KID,
AbstractAsymmetricJwk.X5U, AbstractAsymmetricJwk.X5C, AbstractAsymmetricJwk.X5T, AbstractAsymmetricJwk.X5T_S256);
static final Field<String> KID = AbstractJwk.KID;
protected AbstractProtectedHeader(Set<Field<?>> fieldSet) {
super(fieldSet);
static final Field<URI> X5U = AbstractAsymmetricJwk.X5U;
static final Field<List<X509Certificate>> X5C = AbstractAsymmetricJwk.X5C;
static final Field<byte[]> X5T = AbstractAsymmetricJwk.X5T;
static final Field<byte[]> X5T_S256 = AbstractAsymmetricJwk.X5T_S256;
static final Registry<String, Field<?>> FIELDS =
Fields.registry(DefaultHeader.FIELDS, CRIT, JKU, JWK, KID, X5U, X5C, X5T, X5T_S256);
static boolean isCandidate(FieldMap fields) {
String id = fields.get(DefaultHeader.ALGORITHM);
return (Strings.hasText(id) && !Jwts.SIG.NONE.equals(Jwts.SIG.get().get(id))) ||
fields.get(JKU) != null ||
fields.get(JWK) != null ||
!Collections.isEmpty(fields.get(CRIT)) ||
Strings.hasText(fields.get(KID)) ||
fields.get(X5U) != null ||
!Collections.isEmpty(fields.get(X5C)) ||
!Bytes.isEmpty(fields.get(X5T)) ||
!Bytes.isEmpty(fields.get(X5T_S256));
}
protected AbstractProtectedHeader(Set<Field<?>> fieldSet, Map<String, ?> values) {
super(fieldSet, values);
protected DefaultProtectedHeader(Registry<String, Field<?>> fields, Map<String, ?> values) {
super(fields, values);
}
@Override
public String getKeyId() {
return idiomaticGet(AbstractJwk.KID);
}
public T setKeyId(String kid) {
put(AbstractJwk.KID, kid);
return tthis();
return get(KID);
}
@Override
public URI getJwkSetUrl() {
return idiomaticGet(JKU);
}
public T setJwkSetUrl(URI uri) {
put(JKU, uri);
return tthis();
return get(JKU);
}
@Override
public PublicJwk<?> getJwk() {
return idiomaticGet(JWK);
}
public T setJwk(PublicJwk<?> jwk) {
put(JWK, jwk);
return tthis();
return get(JWK);
}
@Override
public URI getX509Url() {
return idiomaticGet(AbstractAsymmetricJwk.X5U);
}
public T setX509Url(URI uri) {
put(AbstractAsymmetricJwk.X5U, uri);
return tthis();
return get(AbstractAsymmetricJwk.X5U);
}
@Override
public List<X509Certificate> getX509CertificateChain() {
return idiomaticGet(AbstractAsymmetricJwk.X5C);
}
public T setX509CertificateChain(List<X509Certificate> chain) {
put(AbstractAsymmetricJwk.X5C, chain);
return tthis();
return get(X5C);
}
@Override
public byte[] getX509CertificateSha1Thumbprint() {
return idiomaticGet(AbstractAsymmetricJwk.X5T);
}
public T setX509CertificateSha1Thumbprint(byte[] thumbprint) {
put(AbstractAsymmetricJwk.X5T, thumbprint);
return tthis();
return get(X5T);
}
@Override
public byte[] getX509CertificateSha256Thumbprint() {
return idiomaticGet(AbstractAsymmetricJwk.X5T_S256);
}
public T setX509CertificateSha256Thumbprint(byte[] thumbprint) {
put(AbstractAsymmetricJwk.X5T_S256, thumbprint);
return tthis();
return get(X5T_S256);
}
@Override
public Set<String> getCritical() {
return idiomaticGet(CRIT);
}
public T setCritical(Set<String> crit) {
put(CRIT, crit);
return tthis();
return get(CRIT);
}
}

View File

@ -0,0 +1,65 @@
/*
* 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;
import io.jsonwebtoken.ProtectedHeader;
import io.jsonwebtoken.ProtectedJwt;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
import java.security.MessageDigest;
public class DefaultProtectedJwt<H extends ProtectedHeader, P> extends DefaultJwt<H, P> implements ProtectedJwt<H, P> {
protected final byte[] digest;
private final String digestName;
public DefaultProtectedJwt(H header, P payload, byte[] digest, String digestName) {
super(header, payload);
this.digest = Assert.notEmpty(digest, "Digest byte array cannot be null or empty.");
this.digestName = Assert.hasText(digestName, "digestName cannot be null or empty.");
}
@Override
public byte[] getDigest() {
return this.digest.clone();
}
@Override
protected StringBuilder toStringBuilder() {
String b64Url = Encoders.BASE64URL.encode(this.digest);
return super.toStringBuilder().append(',').append(this.digestName).append('=').append(b64Url);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof DefaultProtectedJwt) {
DefaultProtectedJwt<?, ?> pjwt = (DefaultProtectedJwt<?, ?>) obj;
return super.equals(pjwt) && MessageDigest.isEqual(this.digest, pjwt.digest);
}
return false;
}
@Override
public int hashCode() {
return Objects.nullSafeHashCode(getHeader(), getPayload(), this.digest);
}
}

View File

@ -41,7 +41,7 @@ class DefaultTokenizedJwe extends DefaultTokenizedJwt implements TokenizedJwe {
}
@Override
public Header<?> createHeader(Map<String, ?> m) {
public Header createHeader(Map<String, ?> m) {
return new DefaultJweHeader(m);
}
}

View File

@ -49,10 +49,10 @@ class DefaultTokenizedJwt implements TokenizedJwt {
}
@Override
public Header<?> createHeader(Map<String, ?> m) {
public Header createHeader(Map<String, ?> m) {
if (Strings.hasText(getDigest())) {
return new DefaultJwsHeader(m);
}
return new DefaultUnprotectedHeader(m);
return new DefaultHeader(m);
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.UnprotectedHeader;
import java.util.Map;
public class DefaultUnprotectedHeader extends AbstractHeader<UnprotectedHeader> implements UnprotectedHeader {
public DefaultUnprotectedHeader() {
super(AbstractHeader.FIELDS);
}
public DefaultUnprotectedHeader(Map<String, ?> values) {
super(AbstractHeader.FIELDS, values);
}
}

View File

@ -17,40 +17,69 @@ package io.jsonwebtoken.impl;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.FieldReadable;
import io.jsonwebtoken.impl.lang.Fields;
import io.jsonwebtoken.impl.lang.Nameable;
import io.jsonwebtoken.impl.lang.RedactedSupplier;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Objects;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
import java.lang.reflect.Array;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
public class FieldMap implements Map<String, Object>, FieldReadable, Nameable {
protected final Map<String, Field<?>> FIELDS;
protected final Registry<String, ? extends Field<?>> FIELDS;
protected final Map<String, Object> values; // canonical values formatted per RFC requirements
protected final Map<String, Object> idiomaticValues; // the values map with any RFC values converted to Java type-safe values where possible
public JwtMap(Set<Field<?>> fieldSet) {
Assert.notEmpty(fieldSet, "Fields cannot be null or empty.");
Map<String, Field<?>> fields = new LinkedHashMap<>();
for (Field<?> field : fieldSet) {
fields.put(field.getId(), field);
}
this.FIELDS = java.util.Collections.unmodifiableMap(fields);
this.values = new LinkedHashMap<>();
this.idiomaticValues = new LinkedHashMap<>();
private final boolean initialized;
private final boolean mutable;
public FieldMap(Set<Field<?>> fields) {
this(Fields.registry(fields));
}
public JwtMap(Set<Field<?>> fieldSet, Map<String, ?> values) {
this(fieldSet);
Assert.notNull(values, "Map argument cannot be null.");
putAll(values);
public FieldMap(Registry<String, ? extends Field<?>> fields) { // mutable constructor
this(fields, null, true);
}
/**
* Copy constructor producing an immutable instance.
*
* @param fields registry fields
* @param values field values
*/
public FieldMap(Registry<String, ? extends Field<?>> fields, Map<String, ?> values) {
this(fields, Assert.notNull(values, "Map argument cannot be null."), false);
}
protected FieldMap(Registry<String, ? extends Field<?>> fields, Map<String, ?> values, boolean mutable) {
Assert.notNull(fields, "Field registry cannot be null.");
Assert.notEmpty(fields.values(), "Field registry cannot be empty.");
this.FIELDS = fields;
this.values = new LinkedHashMap<>();
this.idiomaticValues = new LinkedHashMap<>();
if (!Collections.isEmpty(values)) {
putAll(values);
}
this.mutable = mutable;
this.initialized = true;
}
private void assertMutable() {
if (initialized && !mutable) {
String msg = getName() + " instance is immutable and may not be modified.";
throw new UnsupportedOperationException(msg);
}
}
@Override
@ -66,15 +95,6 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
(v.getClass().isArray() && Array.getLength(v) == 0);
}
protected Object idiomaticGet(String key) {
return this.idiomaticValues.get(key);
}
protected <T> T idiomaticGet(Field<T> field) {
Object value = this.idiomaticValues.get(field.getId());
return field.cast(value);
}
@Override
public <T> T get(Field<T> field) {
Assert.notNull(field, "Field cannot be null.");
@ -122,6 +142,7 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
@Override
public Object put(String name, Object value) {
assertMutable();
name = Assert.notNull(Strings.clean(name), "Member name cannot be null or empty.");
if (value instanceof String) {
value = Strings.clean((String) value);
@ -141,7 +162,7 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
}
}
protected Object nullSafePut(String name, Object value) {
private Object nullSafePut(String name, Object value) {
if (isReducibleToNull(value)) {
return remove(name);
} else {
@ -150,7 +171,7 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
}
}
protected <T> Object apply(Field<T> field, Object rawValue) {
private <T> Object apply(Field<T> field, Object rawValue) {
final String id = field.getId();
@ -187,6 +208,7 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
@Override
public Object remove(Object key) {
assertMutable();
this.idiomaticValues.remove(key);
return this.values.remove(key);
}
@ -204,23 +226,24 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
@Override
public void clear() {
assertMutable();
this.values.clear();
this.idiomaticValues.clear();
}
@Override
public Set<String> keySet() {
return values.keySet();
return new KeySet();
}
@Override
public Collection<Object> values() {
return values.values();
return new ValueSet();
}
@Override
public Set<Entry<String, Object>> entrySet() {
return values.entrySet();
return new EntrySet();
}
@Override
@ -238,4 +261,86 @@ public class JwtMap implements Map<String, Object>, FieldReadable, Nameable {
public boolean equals(Object obj) {
return values.equals(obj);
}
private abstract class FieldMapSet<T> extends AbstractSet<T> {
@Override
public int size() {
return FieldMap.this.size();
}
}
private class KeySet extends FieldMapSet<String> {
@Override
public Iterator<String> iterator() {
return new KeyIterator();
}
}
private class ValueSet extends FieldMapSet<Object> {
@Override
public Iterator<Object> iterator() {
return new ValueIterator();
}
}
private class EntrySet extends FieldMapSet<Map.Entry<String, Object>> {
@Override
public Iterator<Entry<String, Object>> iterator() {
return new EntryIterator();
}
}
private abstract class FieldMapIterator<T> implements Iterator<T> {
final Iterator<Map.Entry<String, Object>> i;
transient Map.Entry<String, Object> current;
FieldMapIterator() {
this.i = FieldMap.this.values.entrySet().iterator();
this.current = null;
}
@Override
public boolean hasNext() {
return i.hasNext();
}
protected Map.Entry<String, Object> nextEntry() {
current = i.next();
return current;
}
@Override
public void remove() {
if (current == null) {
throw new IllegalStateException();
}
String key = current.getKey();
FieldMap.this.remove(key);
}
}
private class ValueIterator extends FieldMapIterator<Object> {
@Override
public Object next() {
return nextEntry().getValue();
}
}
private class KeyIterator extends FieldMapIterator<String> {
@Override
public String next() {
return nextEntry().getKey();
}
}
private class EntryIterator extends FieldMapIterator<Map.Entry<String, Object>> {
@Override
public Entry<String, Object> next() {
return nextEntry();
}
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.HeaderMutator;
import io.jsonwebtoken.lang.Builder;
import io.jsonwebtoken.lang.MapMutator;
// TODO: move this concept to the API when Java 8 is supported. Do we even need it?
public interface HeaderBuilder<H extends Header<H>, T extends HeaderBuilder<H, T>> extends Builder<H>, MapMutator<String, Object, T>, HeaderMutator<T> {
}

View File

@ -16,31 +16,43 @@
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Identifiable;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Locator;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.lang.Field;
import io.jsonwebtoken.impl.lang.Function;
import io.jsonwebtoken.impl.lang.IdRegistry;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Collections;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.lang.Strings;
public class IdLocator<H extends Header<H>, R> implements Function<H, R> {
import java.util.Collection;
import java.util.LinkedHashSet;
private final Field<String> headerField;
public class IdLocator<H extends Header, R extends Identifiable> implements Locator<R>, Function<H, R> {
private final Field<String> field;
private final String requiredMsg;
private final boolean headerValueRequired;
private final boolean valueRequired;
private final Function<String, R> registry;
private final Registry<String, R> registry;
public IdLocator(Field<String> field, String requiredMsg, Function<String, R> registry) {
this.headerField = Assert.notNull(field, "Header field cannot be null.");
this.registry = Assert.notNull(registry, "Registry cannot be null.");
this.headerValueRequired = Strings.hasText(requiredMsg);
this.requiredMsg = requiredMsg;
public IdLocator(Field<String> field, Registry<String, R> registry, Collection<R> extras, String requiredExceptionMessage) {
this.field = Assert.notNull(field, "Header field cannot be null.");
this.requiredMsg = Strings.clean(requiredExceptionMessage);
this.valueRequired = Strings.hasText(this.requiredMsg);
Assert.notEmpty(registry, "Registry cannot be null or empty.");
Collection<R> all = new LinkedHashSet<>(Collections.size(registry) + Collections.size(extras));
all.addAll(extras);
all.addAll(registry.values());
this.registry = new IdRegistry<>(field.getName(), all);
}
private static String type(Header<?> header) {
private static String type(Header header) {
if (header instanceof JweHeader) {
return "JWE";
} else if (header instanceof JwsHeader) {
@ -51,24 +63,29 @@ public class IdLocator<H extends Header<H>, R> implements Function<H, R> {
}
@Override
public R apply(H header) {
public R locate(Header header) {
Assert.notNull(header, "Header argument cannot be null.");
Object val = header.get(this.headerField.getId());
String id = val != null ? String.valueOf(val) : null;
Object val = header.get(this.field.getId());
String id = val != null ? val.toString() : null;
if (this.headerValueRequired && !Strings.hasText(id)) {
throw new MalformedJwtException(requiredMsg);
if (!Strings.hasText(id)) {
if (this.valueRequired) {
throw new MalformedJwtException(requiredMsg);
}
return null; // otherwise header value not required, so short circuit
}
R instance = registry.apply(id);
if (this.headerValueRequired && instance == null) {
String msg = "Unrecognized " + type(header) + " " + this.headerField + " header value: " + id;
throw new UnsupportedJwtException(msg);
try {
return registry.forKey(id);
} catch (Exception e) {
String msg = "Unrecognized " + type(header) + " " + this.field + " header value: " + id;
throw new UnsupportedJwtException(msg, e);
}
}
return instance;
@Override
public R apply(H header) {
return locate(header);
}
}

View File

@ -19,6 +19,7 @@ import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwe;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwt;
@ -27,7 +28,6 @@ import io.jsonwebtoken.JwtHandler;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.UnprotectedHeader;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.io.Decoder;
import io.jsonwebtoken.io.Deserializer;
@ -40,6 +40,7 @@ import java.util.Map;
/**
* This JwtParser implementation exists as a stop gap until the mutable methods are removed from JwtParser.
* TODO: remove this class BEFORE 1.0
*
* @since 0.11.0
*/
class ImmutableJwtParser implements JwtParser {
@ -146,7 +147,7 @@ class ImmutableJwtParser implements JwtParser {
}
@Override
public Jwt<?,?> parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
public Jwt<?, ?> parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
return this.jwtParser.parse(jwt);
}
@ -156,12 +157,12 @@ class ImmutableJwtParser implements JwtParser {
}
@Override
public Jwt<UnprotectedHeader, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
public Jwt<Header, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
return this.jwtParser.parseContentJwt(jwt);
}
@Override
public Jwt<UnprotectedHeader, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
public Jwt<Header, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException {
return this.jwtParser.parseClaimsJwt(jwt);
}

View File

@ -1,30 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.JweHeader;
import io.jsonwebtoken.JweHeaderMutator;
// TODO: move this concept to the API when Java 8 is supported so we can have JweHeader.builder() --> returns JweHeaderBuilder
/**
* A builder to create {@link JweHeader} instances.
*
* @since JJWT_RELEASE_VERSION
*/
public interface JweHeaderBuilder extends
ProtectedHeaderBuilder<JweHeader, JweHeaderBuilder>, JweHeaderMutator<JweHeaderBuilder> {
}

View File

@ -1,27 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.JwsHeader;
// TODO: move this concept to the API when Java 8 is supported so we can have JwsHeader.builder() --> returns JwsHeaderBuilder
/**
* A builder to create {@link JwsHeader} instances.
*
* @since JJWT_RELEASE_VERSION
*/
public interface JwsHeaderBuilder extends ProtectedHeaderBuilder<JwsHeader, JwsHeaderBuilder> {
}

View File

@ -1,26 +0,0 @@
/*
* Copyright (C) 2021 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;
import io.jsonwebtoken.ProtectedHeader;
import io.jsonwebtoken.ProtectedHeaderMutator;
import io.jsonwebtoken.security.X509Builder;
// TODO: move this concept to the API when Java 8 is supported. Do we even need it?
public interface ProtectedHeaderBuilder<H extends ProtectedHeader<H>, T extends ProtectedHeaderBuilder<H,T>>
extends HeaderBuilder<H,T>, ProtectedHeaderMutator<T>, X509Builder<T> {
}

View File

@ -48,5 +48,5 @@ public interface TokenizedJwt {
* @param m the header state
* @return a new header instance.
*/
Header<?> createHeader(Map<String, ?> m);
Header createHeader(Map<String, ?> m);
}

Some files were not shown because too many files have changed in this diff Show More