Skip to content

Commit

Permalink
Merge 18fbc69 into aecf2c1
Browse files Browse the repository at this point in the history
  • Loading branch information
lhazlewood committed Jun 2, 2022
2 parents aecf2c1 + 18fbc69 commit aebdeef
Show file tree
Hide file tree
Showing 491 changed files with 28,149 additions and 5,176 deletions.
1 change: 0 additions & 1 deletion .gitignore
@@ -1,4 +1,3 @@
*.class
.DS_Store

# Mobile Tools for Java (J2ME)
Expand Down
4 changes: 3 additions & 1 deletion .lift/config.toml
@@ -1,4 +1,6 @@
ignoreRules = ["MissingOverride"]
ignoreRules = ["MissingOverride", "MissingSummary", "InconsistentCapitalization", "JavaUtilDate",
"TypeParameterUnusedInFormals", "JavaLangClash", "InlineFormatString"]
ignoreFiles = '''
impl/**
**/test/**
'''
38 changes: 38 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,43 @@
## Release Notes

### JJWT_RELEASE_VERSION

* The `io.jsonwebtoken.SignatureAlgorithm` enum has been deprecated in favor of a new
`io.jsonwebtoken.security.SignatureAlgorithm` interface. Also, a new `io.jsonwebtoken.security.SignatureAlgorithms`
static helper class enumerates all the standard JWA 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.

* Similarly, as the `io.jsonwebtoken.security.Keys#secretKeyFor` and `io.jsonwebtoken.security.Keys#keyPairFor` methods
accepted the now-deprecated `io.jsonwebtoken.SignatureAlgorithm` enum, they have also been deprecated in favor of
calling new `keyBuilder()` or `keyPairBuilder()` methods on `SignatureAlgorithm` instances directly. The builders
allow for customization of the JCA `Provider` and `SecureRandom` during Key or KeyPair generation if desired, whereas
the old enum-based static utility methods did not.

* `io.jsonwebtoken.CompressionCodec` now inherits `io.jsonwebtoken.Identifiable` and `getId()` is preferred over
the now-deprecated `getAlgorithmName()` method. This was to guarantee API congruence with all other JWT-identifiable
algorithm names that can be set as a header value.

#### Backwards Compatibility Breaking Changes

* Parsing of unsecured JWTs (`alg` header of `none`) are now disabled by default as mandated by
[RFC 7518, Section 3.6](https://datatracker.ietf.org/doc/html/rfc7518#section-3.6). If you require parsing of
unsecured JWTs, you may call the `enableUnsecuredJws` method on the `JwtParserBuilder`, but note the security
implications of doing so as mentioned in that method's JavaDoc before doing so.

* `io.jsonwebtoken.JwtHandlerAdapter` has been changed to add the `abstract` modifier. This class was never intended
to be instantiated directly, and is provided for subclassing benefits. The missing modifier has been added to ensure
the class is used as it had always been intended.

* `io.jsonwebtokne.gson.io.GsonSerializer` now requires `Gson` instances that have a registered
`GsonSupplierSerializer` type adapter, for example:
```java
new GsonBuilder()
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
.disableHtmlEscaping().create();
```

### 0.11.5

This patch release adds additional security guards against an ECDSA bug in Java SE versions 15-15.0.6, 17-17.0.2, and 18
Expand Down
93 changes: 74 additions & 19 deletions README.md
Expand Up @@ -69,6 +69,7 @@ enforcement.
* [Custom Clock](#jws-read-clock-custom)
* [Decompression](#jws-read-decompression)
<!-- * [Error Handling](#jws-read-errors) -->
* [Encrypted JWTs](#jwe)
* [Compression](#compression)
* [Custom Compression Codec](#compression-custom)
* [JSON Processor](#json)
Expand Down Expand Up @@ -106,11 +107,40 @@ enforcement.
* PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384<sup>1</sup>
* PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512<sup>1</sup>

<sup>1. Requires JDK 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
<sup>1. Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
* Creating, parsing and decrypting encrypted compact JWTs (aka JWEs) with all standard JWE encryption algorithms:
* A128CBC-HS256: AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm, as defined in [RFC 7518, Section 5.2.3](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.3)
* A192CBC-HS384: AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm, as defined in [RFC 7518, Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.4)
* A256CBC-HS512: AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm, as defined in [RFC 7518, Section 5.2.5](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.5)
* A128GCM: AES GCM using 128-bit key<sup>2</sup>
* A192GCM: AES GCM using 192-bit key<sup>2</sup>
* A256GCM: AES GCM using 256-bit key<sup>2</sup>

<sup>2. Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
* All Key Management Algorithms for obtaining JWE encryption and decryption keys:
* RSA1_5: RSAES-PKCS1-v1_5
* RSA-OAEP: RSAES OAEP using default parameters
* RSA-OAEP-256: RSAES OAEP using SHA-256 and MGF1 with SHA-256
* A128KW: AES Key Wrap with default initial value using 128-bit key
* A192KW: AES Key Wrap with default initial value using 192-bit key
* A256KW: AES Key Wrap with default initial value using 256-bit key
* dir: Direct use of a shared symmetric key as the CEK
* ECDH-ES: Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF
* ECDH-ES+A128KW: ECDH-ES using Concat KDF and CEK wrapped with "A128KW"
* ECDH-ES+A192KW: ECDH-ES using Concat KDF and CEK wrapped with "A192KW"
* ECDH-ES+A256KW: ECDH-ES using Concat KDF and CEK wrapped with "A256KW"
* A128GCMKW: Key wrapping with AES GCM using 128-bit key<sup>3</sup>
* A192GCMKW: Key wrapping with AES GCM using 192-bit key<sup>3</sup>
* A256GCMKW: Key wrapping with AES GCM using 256-bit key<sup>3</sup>
* PBES2-HS256+A128KW: PBES2 with HMAC SHA-256 and "A128KW" wrapping<sup>3</sup>
* PBES2-HS384+A192KW: PBES2 with HMAC SHA-384 and "A192KW" wrapping<sup>3</sup>
* PBES2-HS512+A256KW: PBES2 with HMAC SHA-512 and "A256KW" wrapping<sup>3</sup>

<sup>3. Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
* Convenience enhancements beyond the specification such as
* Body compression for any large JWT, not just JWEs
* Claims assertions (requiring specific values)
* Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson)
* Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson)
* Secure Key generation based on desired JWA algorithms
* and more...

Expand Down Expand Up @@ -370,13 +400,13 @@ Most complexity is hidden behind a convenient and readable builder-based [fluent

```java
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.SignatureAlgorithms;
import io.jsonwebtoken.security.Keys;
import java.security.Key;

// We need a signing key, so we'll create one just for this example. Usually
// the key would be read from your application configuration instead.
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
SecretKey key = SignatureAlgorithms.HS256.keyBuilder().build();

String jws = Jwts.builder().setSubject("Joe").signWith(key).compact();
```
Expand Down Expand Up @@ -526,7 +556,7 @@ key algorithms - identified by the following names:
* `PS384`: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
* `PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512

These are all represented in the `io.jsonwebtoken.SignatureAlgorithm` enum.
These are all represented in the `io.jsonwebtoken.security.SignatureAlgorithms` enum class.

What's really important about these algorithms - other than their security properties - is that the JWT specification
[RFC 7518, Sections 3.2 through 3.5](https://tools.ietf.org/html/rfc7518#section-3)
Expand Down Expand Up @@ -594,21 +624,28 @@ JWT Elliptic Curve signature algorithms `ES256`, `ES384`, and `ES512` all requir
#### Creating Safe Keys

If you don't want to think about bit length requirements or just want to make your life easier, JJWT has
provided the `io.jsonwebtoken.security.Keys` utility class that can generate sufficiently secure keys for any given
provided convenient builder classes that can generate sufficiently secure keys for any given
JWT signature algorithm you might want to use.

<a name="jws-key-create-secret"></a>
##### Secret Keys

If you want to generate a sufficiently strong `SecretKey` for use with the JWT HMAC-SHA algorithms, use the
`Keys.secretKeyFor(SignatureAlgorithm)` helper method:
If you want to generate a sufficiently strong `SecretKey` for use with the JWT HMAC-SHA algorithms, use the respective
algorithm's `keyBuilder()` method:

```java
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //or HS384 or HS512
SecretKey key = SignatureAlgorithms.HS256.keyBuilder().build(); //or HS384.keyBuilder() or HS512.keyBuilder()
```

Under the hood, JJWT uses the JCA provider's `KeyGenerator` to create a secure-random key with the correct minimum
length for the given algorithm.
Under the hood, JJWT uses the JCA default provider's `KeyGenerator` to create a secure-random key with the correct
minimum length for the given algorithm.

If you want to specify a specific JCA `Provider` or `SecureRandom` to use during key generation, you may specify those
as builder arguments. For example:

```java
SecretKey key = SignatureAlgorithms.HS256.keyBuilder().setProvider(aProvider).setRandom(aSecureRandom).build();
```

If you need to save this new `SecretKey`, you can Base64 (or Base64URL) encode it:

Expand All @@ -624,14 +661,14 @@ further encrypt it, etc, before saving to disk (for example).
##### Asymmetric Keys

If you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with JWT ECDSA or RSA
algorithms, use the `Keys.keyPairFor(SignatureAlgorithm)` helper method:
algorithms, use an algorithm's respective `keyPairBuilder()` method:

```java
KeyPair keyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
KeyPair keyPair = SignatureAlgorithms.RS256.keyPairBuilder().build(); //or RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
```

You use the private key (`keyPair.getPrivate()`) to create a JWS and the public key (`keyPair.getPublic()`) to
parse/verify a JWS.
Once you've generated a `KeyPair`, you can use the private key (`keyPair.getPrivate()`) to create a JWS and the
public key (`keyPair.getPublic()`) to parse/verify a JWS.

**NOTE: The `PS256`, `PS384`, and `PS512` algorithms require JDK 11 or a compatible JCA Provider
(like BouncyCastle) in the runtime classpath.** If you are using JDK 10 or earlier and you want to use them, see
Expand Down Expand Up @@ -1181,7 +1218,10 @@ how to resolve your `CompressionCodec` to decompress the JWT.

Please see the [Compression](#compression) section below to see how to decompress JWTs during parsing.

<!-- TODO: ## Encrypted JWTs -->
<a name="jwe"></a>
## Encrypted JWTs

TODO: NOTE: A128GCM, A192GCM, A256GCM algorithms require JDK 8 or BouncyCastle.

<a name="compression"></a>
## Compression
Expand Down Expand Up @@ -1421,11 +1461,15 @@ all that is required, no code or config is necessary.
If you're curious, JJWT will automatically create an internal default Gson instance for its own needs as follows:

```java
new GsonBuilder().disableHtmlEscaping().create();
new GsonBuilder()
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
.disableHtmlEscaping().create();
```

The `registerTypeHierarchyAdapter` builder call is required to serialize JWKs with secret or private values.

However, if you prefer to use a different Gson instance instead of JJWT's default, you can configure JJWT to use your
own.
own - just don't forget to register the necessary JJWT type hierarchy adapter.

You do this by declaring the `io.jsonwebtoken:jjwt-gson` dependency with **compile** scope (not runtime
scope which is the typical JJWT default). That is:
Expand Down Expand Up @@ -1453,7 +1497,10 @@ And then you can specify the `GsonSerializer` using your own `Gson` instance on

```java

Gson gson = getGson(); //implement me
Gson gson = new GsonBuilder()
// don't forget this line!:
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
.disableHtmlEscaping().create();

String jws = Jwts.builder()

Expand All @@ -1474,6 +1521,14 @@ Jwts.parser()
// ... etc ...
```

Again, as shown above, it is critical to create your `Gson` instance using the `GsonBuilder` and include the line:

```java
.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)
```

to ensure JWK serialization works as expected.

<a name="base64"></a>
## Base64 Support

Expand Down
57 changes: 52 additions & 5 deletions api/src/main/java/io/jsonwebtoken/ClaimJwtException.java
Expand Up @@ -16,36 +16,83 @@
package io.jsonwebtoken;

/**
* ClaimJwtException is a subclass of the {@link JwtException} that is thrown after a validation of an JTW claim failed.
* ClaimJwtException is a subclass of the {@link JwtException} that is thrown after a validation of an JWT claim failed.
*
* @since 0.5
*/
public abstract class ClaimJwtException extends JwtException {

/**
* Deprecated as this is an implementation detail accidentally exposed in the JJWT 0.5 public API. It is no
* longer referenced anywhere in JJWT's implementation and will be removed in a future release.
*
* @deprecated will be removed in a future release.
*/
@Deprecated
public static final String INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE = "Expected %s claim to be: %s, but was: %s.";

/**
* Deprecated as this is an implementation detail accidentally exposed in the JJWT 0.5 public API. It is no
* longer referenced anywhere in JJWT's implementation and will be removed in a future release.
*
* @deprecated will be removed in a future release.
*/
@Deprecated
public static final String MISSING_EXPECTED_CLAIM_MESSAGE_TEMPLATE = "Expected %s claim to be: %s, but was not present in the JWT claims.";

private final Header header;
/**
* The header associated with the Claims that failed validation.
*/
private final Header<?> header;

/**
* The Claims that failed validation.
*/
private final Claims claims;

protected ClaimJwtException(Header header, Claims claims, String message) {
/**
* Creates a new instance with the specified header, claims and exception message.
*
* @param header the header inspected
* @param claims the claims obtained
* @param message the exception message
*/
protected ClaimJwtException(Header<?> header, Claims claims, String message) {
super(message);
this.header = header;
this.claims = claims;
}

protected ClaimJwtException(Header header, Claims claims, String message, Throwable cause) {
/**
* Creates a new instance with the specified header, claims and exception message as a result of encountering
* the specified {@code cause}.
*
* @param header the header inspected
* @param claims the claims obtained
* @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) {
super(message, cause);
this.header = header;
this.claims = claims;
}

/**
* Returns the {@link Claims} that failed validation.
*
* @return the {@link Claims} that failed validation.
*/
public Claims getClaims() {
return claims;
}

public Header getHeader() {
/**
* Returns the header associated with the {@link #getClaims() claims} that failed validation.
*
* @return the header associated with the {@link #getClaims() claims} that failed validation.
*/
public Header<?> getHeader() {
return header;
}
}
21 changes: 10 additions & 11 deletions api/src/main/java/io/jsonwebtoken/Claims.java
Expand Up @@ -24,12 +24,11 @@
* <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>Because this interface extends {@code Map&lt;String, Object&gt;}, if you would like to add your own properties,
* <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>
*
* <pre>
* claims.{@link Map#put(Object, Object) put}("someKey", "someValue");
* </pre>
* <blockquote><pre>
* claims.{@link Map#put(Object, Object) put}("someKey", "someValue");</pre></blockquote>
*
* <h2>Creation</h2>
*
Expand All @@ -41,25 +40,25 @@
public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {

/** JWT {@code Issuer} claims parameter name: <code>"iss"</code> */
public static final String ISSUER = "iss";
String ISSUER = "iss";

/** JWT {@code Subject} claims parameter name: <code>"sub"</code> */
public static final String SUBJECT = "sub";
String SUBJECT = "sub";

/** JWT {@code Audience} claims parameter name: <code>"aud"</code> */
public static final String AUDIENCE = "aud";
String AUDIENCE = "aud";

/** JWT {@code Expiration} claims parameter name: <code>"exp"</code> */
public static final String EXPIRATION = "exp";
String EXPIRATION = "exp";

/** JWT {@code Not Before} claims parameter name: <code>"nbf"</code> */
public static final String NOT_BEFORE = "nbf";
String NOT_BEFORE = "nbf";

/** JWT {@code Issued At} claims parameter name: <code>"iat"</code> */
public static final String ISSUED_AT = "iat";
String ISSUED_AT = "iat";

/** JWT {@code JWT ID} claims parameter name: <code>"jti"</code> */
public static final String ID = "jti";
String ID = "jti";

/**
* Returns the JWT <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-4.1.1">
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/jsonwebtoken/ClaimsMutator.java
Expand Up @@ -25,7 +25,7 @@
* @see io.jsonwebtoken.Claims
* @since 0.2
*/
public interface ClaimsMutator<T extends ClaimsMutator> {
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">
Expand Down

0 comments on commit aebdeef

Please sign in to comment.