diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d6ba5eaa..7e31264d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -73,6 +73,27 @@ jobs:
if [ "$JDK_MAJOR_VERSION" == "7" ]; then export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m"; fi
${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
+ # ensure all of our files have the correct/updated license header
+ license-check:
+ runs-on: 'ubuntu-latest'
+ env:
+ MVN_CMD: ./mvnw --no-transfer-progress -B
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0 # avoid license plugin history warnings (plus it needs full history)
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'zulu'
+ java-version: '8'
+ cache: 'maven'
+ check-latest: true
+ - name: License Check
+ # This adds about 1 minute to any build, which is why we don't want to do it on every other build:
+ run: |
+ ${{env.MVN_CMD}} license:check
+
code-coverage:
# (commented out for now - see the comments in 'Wait to start' below for why. Keeping this here as a placeholder
# as it may be better to use instead of an artificial delay once we no longer need to build on JDK 7):
@@ -107,4 +128,4 @@ jobs:
${{env.MVN_CMD}} -pl . clover:clover clover:check coveralls:report \
-DrepoToken="${{ secrets.GITHUB_TOKEN }}" \
-DserviceName=github \
- -DserviceBuildNumber="${{ env.GITHUB_RUN_ID }}"
\ No newline at end of file
+ -DserviceBuildNumber="${{ env.GITHUB_RUN_ID }}"
diff --git a/.gitignore b/.gitignore
index bc177c84..436d64e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-*.class
.DS_Store
# Mobile Tools for Java (J2ME)
diff --git a/.lift/config.toml b/.lift/config.toml
index a833985e..22b51e7c 100644
--- a/.lift/config.toml
+++ b/.lift/config.toml
@@ -1,4 +1,21 @@
-ignoreRules = ["MissingOverride"]
-ignoreFiles = '''
+#
+# Copyright © 2022 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.
+#
+
+ignoreRules = ["MissingOverride", "MissingSummary", "InconsistentCapitalization", "JavaUtilDate", "TypeParameterUnusedInFormals", "JavaLangClash", "InlineFormatString"]
+ignoreFiles = """
+impl/**
**/test/**
-'''
+"""
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3d016ce..077b6bf1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,215 @@
### JJWT_RELEASE_VERSION
-* Adds a simplified "starter" jar that automatically pulls in `jjwt-api`, `jjwt-impl` and `jjwt-jackson`, useful when
- upgrading from the older `io.jsonwebtoken:jjwt:*` to the project's current flexible module structure.
+This is a big release! JJWT now fully supports Encrypted JSON Web Tokens (JWE) and JSON Web Keys (JWK)! See the
+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.
+
+#### Simplified Starter Jar
+
+Those upgrading to new modular JJWT versions from old single-jar versions will transparently obtain everything
+they need in their Maven, Gradle or Android projects.
+
+JJWT's early releases had one and only one .jar: `jjwt.jar`. Later releases moved to a modular design with 'api' and
+'impl' jars including 'plugin' jars for Jackson, GSON, org.json, etc. Some users upgrading from the earlier single
+jar to JJWT's later versions have been frustrated by being forced to learn how to configure the more modular .jars.
+
+This release re-introduces the `jjwt.jar` artifact again, but this time it is simply an empty .jar with Maven
+metadata that will automatically transitively download the following into a project, retaining the old single-jar
+behavior:
+* `jjwt-api.jar`
+* `jjwt-impl.jar`
+* `jjwt-jackson.jar`
+
+Naturally, developers are still encouraged to configure the modular .jars as described in JJWT's documentation for
+greater control and to enable their preferred JSON parser, but this stop-gap should help those unaware when upgrading.
+
+#### JSON Web Encryption (JWE) Support!
+
+This has been a long-awaited feature for JJWT, years in the making, and it is quite extensive - so many encryption
+algorithms and key management algorithms are defined by the JWA specification, and new API concepts had to be
+introduced for all of them, as well as extensive testing with RFC-defined test vectors. The wait is over!
+All JWA-defined encryption algorithms and key management algorithms are fully implemented and supported and
+available immediately. For example:
+
+```java
+AeadAlgorithm enc = Jwts.ENC.A256GCM;
+SecretKey key = enc.keyBuilder().build();
+String compact = Jwts.builder().setSubject("Joe").encryptWith(key, enc).compact();
+
+Jwe 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. Because this interface extends {@code Map<String, Object>}, if you would like to add your own properties,
+ * Because this interface extends "zip" identifier {@code CompressionCodec} extends {@code Identifiable}; the value returned from
+ * {@link Identifiable#getId() getId()} will be used as the JWT
+ * A128CBC‑HS256
| [AES_128_CBC_HMAC_SHA_256](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3) authenticated encryption algorithm |
+ | `A192CBC-HS384` | [AES_192_CBC_HMAC_SHA_384](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.4) authenticated encryption algorithm |
+ | `A256CBC-HS512` | [AES_256_CBC_HMAC_SHA_512](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5) authenticated encryption algorithm |
+ | `A128GCM` | AES GCM using 128-bit key3 |
+ | `A192GCM` | AES GCM using 192-bit key3 |
+ | `A256GCM` | AES GCM using 256-bit key3 |
+
+ 3. Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
+ * All Key Management Algorithms for obtaining JWE encryption and decryption keys:
+
+ | Identifier | Key Management Algorithm |
+ |----------------------|-------------------------------------------------------------------------------|
+ | `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 key4 |
+ | `A192GCMKW` | Key wrapping with AES GCM using 192-bit key4 |
+ | `A256GCMKW` | Key wrapping with AES GCM using 256-bit key4 |
+ | `PBES2-HS256+A128KW` | PBES2 with HMAC SHA-256 and "A128KW" wrapping4 |
+ | `PBES2-HS384+A192KW` | PBES2 with HMAC SHA-384 and "A192KW" wrapping4 |
+ | PBES2‑HS512+A256KW
| PBES2 with HMAC SHA-512 and "A256KW" wrapping4 |
+
+ 4. Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
+ * Creating, parsing and verifying JSON Web Keys (JWKs) in all standard JWA key formats using native Java `Key` types:
+
+ | JWK Key Format | Java `Key` Type | JJWT `Jwk` Type |
+ |----------------------------|------------------------------------|-------------------|
+ | Symmetric Key | `SecretKey` | `SecretJwk` |
+ | Elliptic Curve Public Key | `ECPublicKey` | `EcPublicJwk` |
+ | Elliptic Curve Private Key | `ECPrivateKey` | `EcPrivateJwk` |
+ | RSA Public Key | `RSAPublicKey` | `RsaPublicJwk` |
+ | RSA Private Key | `RSAPrivateKey` | `RsaPrivateJwk` |
+ | XDH Private Key | `XECPublicKey`5 | `OctetPublicJwk` |
+ | XDH Private Key | `XECPrivateKey`5 | `OctetPrivateJwk` |
+ | EdDSA Public Key | `EdECPublicKey`6 | `OctetPublicJwk` |
+ | EdDSA Private Key | `EdECPublicKey`6 | `OctetPrivateJwk` |
+
+ 5. Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
+ 6. Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
* Convenience enhancements beyond the specification such as
- * Body compression for any large JWT, not just JWEs
+ * Payload 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 unmarshalling when using a compatible JSON parser (e.g. Jackson)
* Secure Key generation based on desired JWA algorithms
* and more...
@@ -118,9 +245,8 @@ enforcement.
### Currently Unsupported Features
* [Non-compact](https://tools.ietf.org/html/rfc7515#section-7.2) serialization and parsing.
-* JWE (Encryption for JWT)
-These features will be implemented in a future release. Community contributions are welcome!
+This feature may be implemented in a future release. Community contributions are welcome!
## Community
@@ -129,7 +255,8 @@ These features will be implemented in a future release. Community contributions
### Getting Help
If you have trouble using JJWT, please first read the documentation on this page before asking questions. We try
-very hard to ensure JJWT's documentation is robust, categorized with a table of contents, and up to date for each release.
+very hard to ensure JJWT's documentation is robust, categorized with a table of contents, and up to date for each
+release.
#### Questions
@@ -152,7 +279,8 @@ usability question, instead please
[ask your question here](https://stackoverflow.com/questions/ask?tags=jjwt&guided=false), or try Slack or Gittr as
described above.
-**If a GitHub Issue is created that does not represent actionable work for JJWT's codebase, it will be promptly closed.**
+**If a GitHub Issue is created that does not represent actionable work for JJWT's codebase, it will be promptly
+closed.**
#### Bugs and Feature Requests
@@ -176,7 +304,7 @@ However, if you want or feel the need to change JJWT's functionality or core cod
without [creating a new JJWT issue](https://github.com/jwtk/jjwt/issues/new) and discussing your desired
changes **first**, _before you start working on it_.
-It would be a shame to reject your earnest and genuinely appreciated pull request if it might not align with the
+It would be a shame to reject your earnest and genuinely-appreciated pull request if it might not align with the
project's goals, design expectations or planned functionality. We've sadly had to reject large PRs in the past because
they were out of sync with project or design expectations - all because the PR author didn't first check in with
the team first before working on a solution.
@@ -198,51 +326,198 @@ to discuss or ask questions first if you're not sure. :)
## What is a JSON Web Token?
-Don't know what a JSON Web Token is? Read on. Otherwise, jump on down to the [Installation](#Installation) section.
+JSON Web Token (JWT) is a _general-purpose_ text-based messaging format for transmitting information in a
+compact and secure way. Contrary to popular belief, JWT is not just useful for sending and receiving identity tokens
+on the web - even if that is the most common use case. JWTs can be used as messages for _any_ type of data.
-JWT is a means of transmitting information between two parties in a compact, verifiable form.
+A JWT in its simplest form contains two parts:
-The bits of information encoded in the body of a JWT are called `claims`. The expanded form of the JWT is in a JSON format, so each `claim` is a key in the JSON object.
-
-JWTs can be cryptographically signed (making it a [JWS](https://tools.ietf.org/html/rfc7515)) or encrypted (making it a [JWE](https://tools.ietf.org/html/rfc7516)).
+ 1. The primary data within the JWT, called the `payload`, and
+ 2. A JSON `Object` with name/value pairs that represent metadata about the `payload` and the
+ message itself, called the `header`.
-This adds a powerful layer of verifiability to the user of JWTs. The receiver has a high degree of confidence that the JWT has not been tampered with by verifying the signature, for instance.
+A JWT `payload` can be absolutely anything at all - anything that can be represented as a byte array, such as Strings,
+images, documents, etc.
-The compact representation of a signed JWT is a string that has three parts, each separated by a `.`:
+But because a JWT `header` is a JSON `Object`, it would make sense that a JWT `payload` could also be a JSON
+`Object` as well. In many cases, developers like the `payload` to be JSON that
+represents data about a user or computer or similar identity concept. When used this way, the `payload` is called a
+JSON `Claims` object, and each name/value pair within that object is called a `claim` - each piece of information
+within 'claims' something about an identity.
+
+And while it is useful to 'claim' something about an identity, really anyone can do that. What's important is that you
+_trust_ the claims by verifying they come from a person or computer you trust.
+
+A nice feature of JWTs is that they can be secured in various ways. A JWT can be cryptographically signed (making it
+what we call a [JWS](https://tools.ietf.org/html/rfc7515)) or encrypted (making it a
+[JWE](https://tools.ietf.org/html/rfc7516)). This adds a powerful layer of verifiability to the JWT - a
+JWS or JWE recipient can have a high degree of confidence it comes from someone they trust
+by verifying a signature or decrypting it. It is this feature of verifiability that makes JWT a good choice
+for sending and receiving secure information, like identity claims.
+
+Finally, JSON with whitespace for human readability is nice, but it doesn't make for a very efficient message
+format. Therefore, JWTs can be _compacted_ (and even compressed) to a minimal representation - basically
+Base64URL-encoded strings - so they can be transmitted around the web more efficiently, such as in HTTP headers or URLs.
+
+
+### JWT Example
+
+Once you have a `payload` and `header`, how are they compacted for web transmission, and what does the final JWT
+actually look like? Let's walk through a simplified version of the process with some pseudocode:
+
+1. Assume we have a JWT with a JSON `header` and a simple text message payload:
+
+ **header**
+ ```
+ {
+ "alg": "none"
+ }
+ ```
+
+ **payload**
+ ```
+ The true sign of intelligence is not knowledge but imagination.
+ ```
+
+2. Remove all unnecessary whitespace in the JSON:
+
+ ```groovy
+ String header = '{"alg":"none"}'
+ String payload = 'The true sign of intelligence is not knowledge but imagination.'
+ ```
+
+3. Get the UTF-8 bytes and Base64URL-encode each:
+
+ ```groovy
+ String encodedHeader = base64URLEncode( header.getBytes("UTF-8") )
+ String encodedPayload = base64URLEncode( payload.getBytes("UTF-8") )
+ ```
+
+4. Join the encoded header and claims with period ('.') characters:
+
+ ```groovy
+ String compact = encodedHeader + '.' + encodedPayload + '.'
+ ```
+
+The final concatenated `compact` JWT String looks like this:
```
-eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY
+eyJhbGciOiJub25lIn0.VGhlIHRydWUgc2lnbiBvZiBpbnRlbGxpZ2VuY2UgaXMgbm90IGtub3dsZWRnZSBidXQgaW1hZ2luYXRpb24u.
```
-Each part is [Base64URL](https://en.wikipedia.org/wiki/Base64)-encoded. The first part is the header, which at a
-minimum needs to specify the algorithm used to sign the JWT. The second part is the body. This part has all
-the claims of this JWT encoded in it. The final part is the signature. It's computed by passing a combination of
-the header and body through the algorithm specified in the header.
-
-If you pass the first two parts through a base 64 url decoder, you'll get the following (formatting added for
-clarity):
+This is called an 'unprotected' JWT because no security was involved - no digital signatures or encryption to
+'protect' the JWT to ensure it cannot be changed by 3rd parties.
+
+If we wanted to digitally sign the compact form so that we could at least guarantee that no-one changes the data
+without us detecting it, we'd have to perform a few more steps, shown next.
+
+
+### JWS Example
+
+Instead of a plain text payload, the next example will use probably the most common type of payload - a JSON claims
+`Object` containing information about a particular identity. We'll also digitally sign the JWT to ensure it
+cannot be changed by a 3rd party without us knowing.
+
+1. Assume we have a JSON `header` and a claims `payload`:
+
+ **header**
+ ```json
+ {
+ "alg": "HS256"
+ }
+ ```
+
+ **payload**
+ ```json
+ {
+ "sub": "Joe"
+ }
+ ```
+
+ In this case, the `header` indicates that the `HS256` (HMAC using SHA-256) algorithm will be used to cryptographically sign
+ the JWT. Also, the `payload` JSON object has a single claim, `sub` with value `Joe`.
+
+ There are a number of standard claims, called [Registered Claims](https://tools.ietf.org/html/rfc7519#section-4.1),
+ in the specification and `sub` (for 'Subject') is one of them.
+
+2. Remove all unnecessary whitespace in both JSON objects:
+
+ ```groovy
+ String header = '{"alg":"HS256"}'
+ String claims = '{"sub":"Joe"}'
+ ```
+
+3. Get their UTF-8 bytes and Base64URL-encode each:
+
+ ```groovy
+ String encodedHeader = base64URLEncode( header.getBytes("UTF-8") )
+ String encodedClaims = base64URLEncode( claims.getBytes("UTF-8") )
+ ```
+
+4. Concatenate the encoded header and claims with a period character '.' delimiter:
+
+ ```groovy
+ String concatenated = encodedHeader + '.' + encodedClaims
+ ```
+
+5. Use a sufficiently-strong cryptographic secret or private key, along with a signing algorithm of your choice
+ (we'll use HMAC-SHA-256 here), and sign the concatenated string:
+
+ ```groovy
+ SecretKey key = getMySecretKey()
+ byte[] signature = hmacSha256( concatenated, key )
+ ```
+
+6. Because signatures are always byte arrays, Base64URL-encode the signature and join it to the `concatenated` string
+ with a period character '.' delimiter:
+
+ ```groovy
+ String compact = concatenated + '.' + base64URLEncode( signature )
+ ```
+
+And there you have it, the final `compact` String looks like this:
-`header`
```
-{
- "alg": "HS256"
-}
+eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4
```
-`body`
+This is called a 'JWS' - short for _signed_ JWT.
+
+Of course, no one would want to do this manually in code, and worse, if you get anything wrong, you could introduce
+serious security problems and weaknesses. As a result, JJWT was created to handle all of this for you: JJWT completely
+automates both the creation of JWSs and the parsing and verification of JWSs for you.
+
+
+### JWE Example
+
+So far we have seen an unprotected JWT and a cryptographically signed JWT (called a 'JWS'). One of the things
+that is inherent to both of these two is that all the information within them can be seen by anyone - all the data in
+both the header and the payload is publicly visible. JWS just ensures the data hasn't been changed by anyone -
+it doesn't prevent anyone from seeing it. Many times, this is just fine because the data within them is not
+sensitive information.
+
+But what if you needed to represent information in a JWT that _is_ considered sensitive information - maybe someone's
+postal address or social security number or bank account number?
+
+In these cases, we'd want a fully-encrypted JWT, called a 'JWE' for short. A JWE uses cryptography to ensure that the
+payload remains fully encrypted _and_ authenticated so unauthorized parties cannot see data within, nor change the data
+without being detected. Specifically, the JWE specification requires that
+[Authenticated Encryption with Associated Data](https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_(AEAD))
+algorithms are used to fully encrypt and protect data.
+
+A full overview of AEAD algorithms are out of scope for this documentation, but here's an example of a final compact
+JWE that utilizes these algorithms (line breaks are for readability only):
+
```
-{
- "sub": "Joe"
-}
+eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.
+6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.
+AxY8DCtDaGlsbGljb3RoZQ.
+KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.
+U0m_YmjN04DJvceFICbCVQ
```
-In this case, the information we have is that the HMAC using SHA-256 algorithm was used to sign the JWT. And, the
-body has a single claim, `sub` with value `Joe`.
-
-There are a number of standard claims, called [Registered Claims](https://tools.ietf.org/html/rfc7519#section-4.1),
-in the specification and `sub` (for subject) is one of them.
-
-To compute the signature, you need a secret key to sign it. We'll cover keys and algorithms later.
+Next we'll cover how to install JJWT in your project, and then we'll see how to use JJWT's nice fluent API instead
+of risky string manipulation to quickly and safely build JWTs, JWSs, and JWEs.
## Installation
@@ -264,22 +539,25 @@ If you're building a (non-Android) JDK project, you will want to define the foll
A128CBC‑HS256
| 256 | [AES_128_CBC_HMAC_SHA_256](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3) authenticated encryption algorithm |
+| `A192CBC-HS384` | 384 | [AES_192_CBC_HMAC_SHA_384](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.4) authenticated encryption algorithm |
+| `A256CBC-HS512` | 512 | [AES_256_CBC_HMAC_SHA_512](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5) authenticated encryption algorithm |
+| `A128GCM` | 128 | AES GCM using 128-bit key1 |
+| `A192GCM` | 192 | AES GCM using 192-bit key1 |
+| `A256GCM` | 256 | AES GCM using 256-bit key1 |
+
+1. Requires Java 8+ or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
+These are all represented as constants in the `io.jsonwebtoken.Jwts.ENC` registry singleton as
+implementations of the `io.jsonwebtoken.security.AeadAlgorithm` interface.
+
+As shown in the table above, each algorithm requires a key of sufficient length. The JWT specification
+[RFC 7518, Sections 5.2.3 through 5.3](https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3)
+_requires_ (mandates) that you MUST use keys that are sufficiently strong for a chosen algorithm. This means that
+JJWT - a specification-compliant library - will also enforce that you use sufficiently strong keys
+for the algorithms you choose. If you provide a weak key for a given algorithm, JJWT will reject it and throw an
+exception.
+
+The reason why the JWT specification, and consequently JJWT, mandates key lengths is that the security model of a
+particular algorithm can completely break down if you don't adhere to the mandatory key properties of the algorithm,
+effectively having no security at all.
+
+
+#### Symmetric Ciphers
+
+You might have noticed something about the above Authenticated Encryption algorithms: they're all variants of the
+AES algorithm, and AES always uses a symmetric (secret) key to perform encryption and decryption. That's kind of
+strange, isn't it?
+
+What about RSA and Elliptic Curve asymmetric key cryptography? And Diffie-Hellman key exchange? What about
+password-based key derivation algorithms? Surely any of those could be desirable depending on the use case, no?
+
+Yes, they definitely can, and the JWT specifications do support them, albeit indirectly: those other
+algorithms _are_ indeed supported and used, but they aren't used to encrypt the JWT `payload` directly. They are
+used to _produce_ the actual key used to encrypt the `JWT` payload.
+
+This is all done via the JWT specification's concept of a Key Management Algorithm, covered next. After we cover that,
+we'll show you how to encrypt and parse your own JWTs with the `JwtBuilder` and `JwtParserBuilder`.
+
+
+### JWE Key Management Algorithms
+
+As stated above, all standard JWA Encryption Algorithms are AES-based authenticated encryption algorithms. So what
+about RSA and Elliptic Curve cryptography? And password-based key derivation, or Diffie-Hellman exchange?
+
+All of those are supported as well, but they are not used directly for encryption. They are used to _produce_ the
+key that will be used to directly encrypt the JWT `payload`.
+
+That is, JWT encryption can be thought of as a two-step process, shown in the following pseudocode:
+
+```groovy
+Key algorithmKey = getKeyManagementAlgorithmKey(); // PublicKey, SecretKey, or Password
+
+SecretKey contentEncryptionKey = keyManagementAlgorithm.produceEncryptionKey(algorithmKey); // 1
+
+byte[] ciphertext = encryptionAlgorithm.encrypt(payload, contentEncryptionKey); // 2
+```
+
+Steps:
+
+1. Use the `algorithmKey` to produce the actual key that will be used to encrypt the payload. The JWT specifications
+ call this result the 'Content Encryption Key'.
+2. Take the resulting Content Encryption Key and use it directly with the Authenticated Encryption algorithm to
+ actually encrypt the JWT `payload`.
+
+So why the indirection? Why not just use any `PublicKey`, `SecretKey` or `Password` to encrypt the `payload`
+_directly_ ?
+
+There are quite a few reasons for this.
+
+1. Asymmetric key encryption (like RSA and Elliptic Curve) tends to be slow. Like _really_ slow. Symmetric key
+ cipher algorithms in contrast are _really fast_. This matters a lot in production applications that could be
+ handling a JWT on every HTTP request, which could be thousands per second.
+2. RSA encryption (for example) can only encrypt a relatively small amount of data. A 2048-bit RSA key can only
+ encrypt up to a maximum of 245 bytes. A 4096-bit RSA key can only encrypt up to a maximum of 501 bytes. There are
+ plenty of JWTs that can exceed 245 bytes, and that would make RSA unusable.
+3. Passwords usually make for very poor encryption keys - they often have poor entropy, or they themselves are
+ often too short to be used directly with algorithms that mandate minimum key lengths to help ensure safety.
+
+For these reasons and more, using one secure algorithm to generate or encrypt a key used for another (very fast) secure
+algorithm has been proven to be a great way to increase security through many more secure algorithms while
+also still resulting in very fast and secure output. This is after all how TLS (for https encryption) works -
+two parties can use more complex cryptography (like RSA or Elliptic Curve) to negotiate a small, fast encryption key.
+This fast encryption key is produced during the 'TLS handshake' and is called the TLS 'session key'.
+
+So the JWT specifications work much in the same way: one key from any number of various algorithm types can be used
+to produce a final symmetric key, and that symmetric key is used to encrypt the JWT `payload`.
+
+
+#### JWE Standard Key Management Algorithms
+
+The JWT specification defines 17 standard Key Management Algorithms used to produce the JWE
+Content Encryption Key (CEK):
+
+| Identifier | Key Management Algorithm |
+| --- |-------------------------------------------------------------------------------|
+| `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 Content Encryption Key |
+| `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 key3 |
+| `A192GCMKW` | Key wrapping with AES GCM using 192-bit key3 |
+| `A256GCMKW` | Key wrapping with AES GCM using 256-bit key3 |
+| `PBES2-HS256+A128KW` | PBES2 with HMAC SHA-256 and "A128KW" wrapping3 |
+| `PBES2-HS384+A192KW` | PBES2 with HMAC SHA-384 and "A192KW" wrapping3 |
+| PBES2‑HS512+A256KW
| PBES2 with HMAC SHA-512 and "A256KW" wrapping3 |
+
+3. Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
+
+These are all represented as constants in the `io.jsonwebtoken.Jwts.KEY` registry singleton as
+implementations of the `io.jsonwebtoken.security.KeyAlgorithm` interface.
+
+But 17 algorithms are a lot to choose from. When would you use them? The sections below describe when you might
+choose each category of algorithms and how they behave.
+
+
+##### RSA Key Encryption
+
+The JWT RSA key management algorithms `RSA1_5`, `RSA-OAEP`, and `RSA-OAEP-256` are used when you want to use the
+JWE recipient's RSA _public_ key during encryption. This ensures that only the JWE recipient can decrypt
+and read the JWE (using their RSA `private` key).
+
+During JWE creation, these algorithms:
+
+* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired [encryption algorithm](#jwe-enc).
+* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.
+* Encrypt the CEK itself with the specified RSA key wrap algorithm using the JWE recipient's RSA public key.
+* Embed the payload ciphertext and encrypted CEK in the resulting JWE.
+
+During JWE decryption, these algorithms:
+
+* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.
+* Decrypt the encrypted CEK with the discovered RSA key unwrap algorithm using the JWE recipient's RSA private key,
+ producing the decrypted Content Encryption Key (CEK).
+* Decrypt the JWE ciphertext payload with the JWE's identified [encryption algorithm](#jwe-enc) using the decrypted CEK.
+
+> **Warning**
+>
+> RFC 7518 Sections [4.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.2) and
+> [4.3](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.3) _require_ (mandate) that RSA keys >= 2048 bits
+> MUST be used with these algorithms. JJWT will throw an exception if it detects weaker keys being used.
+
+
+##### AES Key Encryption
+
+The JWT AES key management algorithms `A128KW`, `A192KW`, `A256KW`, `A128GCMKW`, `A192GCMKW`, and `A256GCMKW` are
+used when you have a symmetric secret key, but you don't want to use that secret key to directly
+encrypt/decrypt the JWT.
+
+Instead, a new secure-random key is generated each time a JWE is created, and that new/random key is used to directly
+encrypt/decrypt the JWT payload. The secure-random key is itself encrypted with your symmetric secret key
+using the AES Wrap algorithm, and the encrypted key is embedded in the resulting JWE.
+
+This allows the JWE to be encrypted with a random short-lived key, reducing material exposure of the potentially
+longer-lived symmetric secret key.
+
+Because these particular algorithms use a symmetric secret key, they are best suited when the JWE creator and
+receiver are the same, ensuring the secret key does not need to be shared with multiple parties.
+
+During JWE creation, these algorithms:
+
+* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired [encryption algorithm](#jwe-enc).
+* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.
+* Encrypt the CEK itself with the specified AES key algorithm (either AES Key Wrap or AES with GCM encryption),
+ producing the encrypted CEK.
+* Embed the payload ciphertext and encrypted CEK in the resulting JWE.
+
+During JWE decryption, these algorithms:
+
+* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.
+* Decrypt the encrypted CEK with the discovered AES key algorithm using the symmetric secret key.
+* Decrypt the JWE ciphertext payload with the JWE's identified [encryption algorithm](#jwe-enc) using the decrypted CEK.
+
+> **Warning**
+>
+> The symmetric key used for the AES key algorithms MUST be 128, 192 or 256 bits as required by the specific AES
+> key algorithm. JJWT will throw an exception if it detects weaker keys than what is required.
+
+
+##### Direct Key Encryption
+
+The JWT `dir` (direct) key management algorithm is used when you have a symmetric secret key, and you want to use it
+to directly encrypt the JWT payload.
+
+Because this algorithm uses a symmetric secret key, it is best suited when the JWE creator and receiver are the
+same, ensuring the secret key does not need to be shared with multiple parties.
+
+This is the simplest key algorithm for direct encryption that does not perform any key encryption. It is essentially
+a 'no op' key algorithm, allowing the shared key to be used to directly encrypt the JWT payload.
+
+During JWE creation, this algorithm:
+
+* Encrypts the JWE payload with the desired encryption algorithm directly using the symmetric secret key,
+ producing the JWE payload ciphertext.
+* Embeds the payload ciphertext in the resulting JWE.
+
+Note that because this algorithm does not produce an encrypted key value, an encrypted CEK is _not_ embedded in the
+resulting JWE.
+
+During JWE decryption, this algorithm decrypts the JWE ciphertext payload with the JWE's
+identified [encryption algorithm](#jwe-enc) directly using the symmetric secret key. No encrypted CEK is used.
+
+> **Warning**
+>
+> The symmetric secret key MUST be 128, 192 or 256 bits as required by the associated
+> [AEAD encryption algorithm](#jwe-enc) used to encrypt the payload. JJWT will throw an exception if it detects
+> weaker keys than what is required.
+
+
+##### Password-Based Key Encryption
+
+The JWT password-based key encryption algorithms `PBES2-HS256+A128KW`, `PBES2-HS384+A192KW`, and `PBES2-HS512+A256KW`
+are used when you want to use a password (character array) to encrypt and decrypt a JWT.
+
+However, because passwords are usually too weak or problematic to use directly in cryptographic contexts, these
+algorithms utilize key derivation techniques with work factors (e.g. computation iterations) and secure-random salts
+to produce stronger cryptographic keys suitable for cryptographic operations.
+
+This allows the payload to be encrypted with a random short-lived cryptographically-stronger key, reducing the need to
+expose the longer-lived (and potentially weaker) password.
+
+Because these algorithms use a secret password, they are best suited when the JWE creator and receiver are the
+same, ensuring the secret password does not need to be shared with multiple parties.
+
+During JWE creation, these algorithms:
+
+* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired [encryption algorithm](#jwe-enc).
+* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.
+* Derive a 'key encryption key' (KEK) with the desired "PBES2 with HMAC SHA" algorithm using the password, a suitable
+ number of computational iterations, and a secure-random salt value.
+* Encrypt the generated CEK with the corresponding AES Key Wrap algorithm using the password-derived KEK.
+* Embed the payload ciphertext and encrypted CEK in the resulting JWE.
+
+> **Note**
+>
+> **Secure defaults**: When using these algorithms, if you do not specify a work factor (i.e. number of computational
+> iterations), JJWT will automatically use an
+> [OWASP PBKDF2 recommended](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2)
+> default appropriate for the specified `PBES2` algorithm.
+
+During JWE decryption, these algorithms:
+
+* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.
+* Derive the 'key encryption key' (KEK) with the discovered "PBES2 with HMAC SHA" algorithm using the password and the
+ number of computational iterations and secure-random salt value discovered in the JWE header.
+* Decrypt the encrypted CEK with the corresponding AES Key Unwrap algorithm using the password-derived KEK.
+* Decrypt the JWE ciphertext payload with the JWE's identified [encryption algorithm](#jwe-enc) using the decrypted CEK.
+
+
+##### Elliptic Curve Diffie-Hellman Ephemeral Static Key Agreement
+
+The JWT Elliptic Curve Diffie-Hellman Ephemeral Static key agreement algorithms `ECDH-ES`, `ECDH-ES+A128KW`,
+`ECDH-ES+A192KW`, and `ECDH-ES+A256KW` are used when you want to use the JWE recipient's Elliptic Curve _public_ key
+during encryption. This ensures that only the JWE recipient can decrypt and read the JWE (using their Elliptic Curve
+_private_ key).
+
+During JWE creation, these algorithms:
+
+* Obtain the Content Encryption Key (CEK) used to encrypt the JWE payload as follows:
+ * Inspect the JWE recipient's Elliptic Curve public key and determine its Curve.
+ * Generate a new secure-random ephemeral Ellipic Curve public/private key pair on this same Curve.
+ * Add the ephemeral EC public key to the JWE
+ [epk header](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1) for inclusion in the final JWE.
+ * Produce an ECDH shared secret with the ECDH Key Agreement algorithm using the JWE recipient's EC public key
+ and the ephemeral EC private key.
+ * Derive a symmetric secret key with the Concat Key Derivation Function
+ ([NIST.800-56A](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf), Section 5.8.1) using
+ this ECDH shared secret and any provided
+ [PartyUInfo](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2) and/or
+ [PartyVInfo](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3).
+ * If the key algorithm is `ECDH-ES`:
+ * Use the Concat KDF-derived symmetric secret key directly as the Content Encryption Key (CEK). No encrypted key
+ is created, nor embedded in the resulting JWE.
+ * Otherwise, if the key algorithm is `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, or `ECDH-ES+A256KW`:
+ * Generate a new secure-random Content Encryption Key (CEK) suitable for the desired [encryption algorithm](#jwe-enc).
+ * Encrypt this new CEK with the corresponding AES Key Wrap algorithm using the Concat KDF-derived secret key,
+ producing the encrypted CEK.
+ * Embed the encrypted CEK in the resulting JWE.
+* Encrypt the JWE payload with the desired encryption algorithm using the obtained CEK, producing the JWE payload
+ ciphertext.
+* Embed the payload ciphertext in the resulting JWE.
+
+During JWE decryption, these algorithms:
+
+* Obtain the Content Encryption Key (CEK) used to decrypt the JWE payload as follows:
+ * Retrieve the required ephemeral Elliptic Curve public key from the JWE's
+ [epk header](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1).
+ * Ensure the ephemeral EC public key exists on the same curve as the JWE recipient's EC private key.
+ * Produce the ECDH shared secret with the ECDH Key Agreement algorithm using the JWE recipient's EC private key
+ and the ephemeral EC public key.
+ * Derive a symmetric secret key with the Concat Key Derivation Function
+ ([NIST.800-56A](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf), Section 5.8.1) using
+ this ECDH shared secret and any
+ [PartyUInfo](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2) and/or
+ [PartyVInfo](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3) found in the JWE header.
+ * If the key algorithm is `ECDH-ES`:
+ * Use the Concat KDF-derived secret key directly as the Content Encryption Key (CEK). No encrypted key is used.
+ * Otherwise, if the key algorithm is `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, or `ECDH-ES+A256KW`:
+ * Obtain the encrypted key ciphertext embedded in the JWE.
+ * Decrypt the encrypted key ciphertext with the associated AES Key Unwrap algorithm using the Concat KDF-derived
+ secret key, producing the unencrypted Content Encryption Key (CEK).
+* Decrypt the JWE payload ciphertext with the JWE's discovered encryption algorithm using the obtained CEK.
+
+
+### Creating a JWE
+
+Now that we know the difference between a JWE Encryption Algorithm and a JWE Key Management Algorithm, how do we use
+them to encrypt a JWT?
+
+You create an encrypted JWT (called a 'JWE') as follows:
+
+1. Use the `Jwts.builder()` method to create a `JwtBuilder` instance.
+2. Call `JwtBuilder` methods to set the `payload` content or claims and any [header](#jws-create-header) parameters as desired.
+3. Call the `encryptWith` method, specifying the Key, Key Algorithm, and Encryption Algorithm you want to use.
+4. Finally, call the `compact()` method to compact and encrypt, producing the final jwe.
+
+For example:
+
+```java
+String jwe = Jwts.builder() // (1)
+
+ .setSubject("Bob") // (2)
+
+ .encryptWith(key, keyAlgorithm, encryptionAlgorithm) // (3)
+
+ .compact(); // (4)
+```
+
+Before calling `compact()`, you may set any [header](#jws-create-header) parameters and [claims](#jws-create-claims)
+exactly the same way as described for JWS.
+
+
+#### JWE Compression
+
+If your JWT payload or Claims set is large (contains a lot of data), you might want to compress the JWE to reduce
+its size. Please see the main [Compression](#compression) section to see how to compress and decompress JWTs.
+
+
+### Reading a JWE
+
+You read (parse) a JWE as follows:
+
+1. Use the `Jwts.parserBuilder()` method to create a `JwtParserBuilder` instance.
+2. Call either [setKeyLocator](#key-locator) or `decryptWith` methods to determine the key used to decrypt the JWE.
+4. Call the `JwtParserBuilder`'s `build()` method to create a thread-safe `JwtParser`.
+5. Parse the jwe string with the `JwtParser`'s `parseClaimsJwe` or `parseContentJwe` method.
+6. Wrap the entire call is in a try/catch block in case decryption or integrity verification fails.
+
+For example:
+
+```java
+JweMap<String, Object>
, if you would like to add your own properties,
* you simply use map methods, for example:
- * claims.{@link Map#put(Object, Object) put}("someKey", "someValue");
- *
+ *
*
*
+ * claims.{@link Map#put(Object, Object) put}("someKey", "someValue");
Creation
*
@@ -41,25 +40,25 @@ import java.util.Map;
public interface Claims extends Map"iss"
*/
- public static final String ISSUER = "iss";
+ String ISSUER = "iss";
/** JWT {@code Subject} claims parameter name: "sub"
*/
- public static final String SUBJECT = "sub";
+ String SUBJECT = "sub";
/** JWT {@code Audience} claims parameter name: "aud"
*/
- public static final String AUDIENCE = "aud";
+ String AUDIENCE = "aud";
/** JWT {@code Expiration} claims parameter name: "exp"
*/
- public static final String EXPIRATION = "exp";
+ String EXPIRATION = "exp";
/** JWT {@code Not Before} claims parameter name: "nbf"
*/
- public static final String NOT_BEFORE = "nbf";
+ String NOT_BEFORE = "nbf";
/** JWT {@code Issued At} claims parameter name: "iat"
*/
- public static final String ISSUED_AT = "iat";
+ String ISSUED_AT = "iat";
/** JWT {@code JWT ID} claims parameter name: "jti"
*/
- public static final String ID = "jti";
+ String ID = "jti";
/**
* Returns the JWT
diff --git a/api/src/main/java/io/jsonwebtoken/ClaimsMutator.java b/api/src/main/java/io/jsonwebtoken/ClaimsMutator.java
index 66528d8f..34333d99 100644
--- a/api/src/main/java/io/jsonwebtoken/ClaimsMutator.java
+++ b/api/src/main/java/io/jsonwebtoken/ClaimsMutator.java
@@ -25,7 +25,7 @@ import java.util.Date;
* @see io.jsonwebtoken.Claims
* @since 0.2
*/
-public interface ClaimsMutatorzip
header value.zip
header value.
*
- * @return the compression algorithm name to use as the JWT's {@code zip} header value.
+ * @return the algorithm name to use as the JWT
+ * zip
header value.
+ * @deprecated since JJWT_RELEASE_VERSION in favor of {@link Identifiable#getId()} to ensure congruence with
+ * all other identifiable algorithms.
*/
+ @SuppressWarnings("DeprecatedIsStillUsed")
+ @Deprecated
String getAlgorithmName();
/**
- * Compresses the specified byte array according to the compression {@link #getAlgorithmName() algorithm}.
+ * Compresses the specified byte array, returning the compressed byte array result.
*
- * @param payload bytes to compress
+ * @param content bytes to compress
* @return compressed bytes
- * @throws CompressionException if the specified byte array cannot be compressed according to the compression
- * {@link #getAlgorithmName() algorithm}.
+ * @throws CompressionException if the specified byte array cannot be compressed.
*/
- byte[] compress(byte[] payload) throws CompressionException;
+ byte[] compress(byte[] content) throws CompressionException;
/**
- * Decompresses the specified compressed byte array according to the compression
- * {@link #getAlgorithmName() algorithm}. The specified byte array must already be in compressed form
- * according to the {@link #getAlgorithmName() algorithm}.
+ * 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 according to the compression
- * {@link #getAlgorithmName() algorithm}.
+ * @throws CompressionException if the specified byte array cannot be decompressed.
*/
byte[] decompress(byte[] compressed) throws CompressionException;
}
\ No newline at end of file
diff --git a/api/src/main/java/io/jsonwebtoken/CompressionCodecResolver.java b/api/src/main/java/io/jsonwebtoken/CompressionCodecResolver.java
index 50ebe08f..ba61ff55 100644
--- a/api/src/main/java/io/jsonwebtoken/CompressionCodecResolver.java
+++ b/api/src/main/java/io/jsonwebtoken/CompressionCodecResolver.java
@@ -30,7 +30,11 @@ package io.jsonwebtoken;
* {@link io.jsonwebtoken.JwtParser#setCompressionCodecResolver(CompressionCodecResolver) parsing} JWTs.
It is easiest to create a {@code Header} instance by calling one of the - * {@link Jwts#header() JWTs.header()} factory methods.
+ *It is easiest to create a {@code Header} instance by using {@link Jwts#header()}.
* * @since 0.1 */ -public interface Header"JWT"
*/
- public static final String JWT_TYPE = "JWT";
-
- /** JWT {@code Type} header parameter name: "typ"
*/
- public static final String TYPE = "typ";
-
- /** JWT {@code Content Type} header parameter name: "cty"
*/
- public static final String CONTENT_TYPE = "cty";
-
- /** JWT {@code Compression Algorithm} header parameter name: "zip"
*/
- public static final String COMPRESSION_ALGORITHM = "zip";
-
- /** JJWT legacy/deprecated compression algorithm header parameter name: "calg"
- * @deprecated use {@link #COMPRESSION_ALGORITHM} instead. */
- @Deprecated
- public static final String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
+public interface Headertyp
(type) header value or {@code null} if not present.
+ * JWT {@code Type} (typ) value: "JWT"
+ *
+ * @deprecated since JJWT_RELEASE_VERSION - this constant is never used within the JJWT codebase.
+ */
+ @Deprecated
+ String JWT_TYPE = "JWT";
+
+ /**
+ * JWT {@code Type} header parameter name: "typ"
+ */
+ String TYPE = "typ";
+
+ /**
+ * JWT {@code Content Type} header parameter name: "cty"
+ */
+ String CONTENT_TYPE = "cty";
+
+ /**
+ * JWT {@code Algorithm} header parameter name: "alg"
.
+ *
+ * @see JWS Algorithm Header
+ * @see JWE Algorithm Header
+ */
+ String ALGORITHM = "alg";
+
+ /**
+ * JWT {@code Compression Algorithm} header parameter name: "zip"
+ */
+ String COMPRESSION_ALGORITHM = "zip";
+
+ /**
+ * JJWT legacy/deprecated compression algorithm header parameter name: "calg"
+ *
+ * @deprecated use {@link #COMPRESSION_ALGORITHM} instead.
+ */
+ @SuppressWarnings("DeprecatedIsStillUsed")
+ @Deprecated
+ String DEPRECATED_COMPRESSION_ALGORITHM = "calg";
+
+ /**
+ * Returns the
+ * typ
(Type) header value or {@code null} if not present.
*
* @return the {@code typ} header value or {@code null} if not present.
*/
String getType();
/**
- * Sets the JWT
- * typ
(Type) header value. A {@code null} value will remove the property from the JSON map.
+ * Returns the
+ * cty
(Content Type) header value or {@code null} if not present.
*
- * @param typ the JWT JOSE {@code typ} header value or {@code null} to remove the property from the JSON map.
- * @return the {@code Header} instance for method chaining.
- */
- T setType(String typ);
-
- /**
- * Returns the
- * cty
(Content Type) header value or {@code null} if not present.
+ * The cty
(Content Type) Header Parameter is used by applications to declare the
+ * IANA MediaType of the content
+ * (the payload). This is intended for use by the application when more than
+ * one kind of object could be present in the Payload; the application can use this value to disambiguate among
+ * the different kinds of objects that might be present. It will typically not be used by applications when
+ * the kind of object is already known. This parameter is ignored by JWT implementations (like JJWT); any
+ * processing of this parameter is performed by the JWS application. Use of this Header Parameter is OPTIONAL.
In the normal case where nested signing or encryption operations are not employed (i.e. a compact - * serialization JWT), the use of this header parameter is NOT RECOMMENDED. In the case that nested - * signing or encryption is employed, this Header Parameter MUST be present; in this case, the value MUST be - * {@code JWT}, to indicate that a Nested JWT is carried in this JWT. While media type names are not - * case-sensitive, it is RECOMMENDED that {@code JWT} always be spelled using uppercase characters for - * compatibility with legacy implementations. See - * JWT Appendix A.2 for - * an example of a Nested JWT.
+ *To keep messages compact in common situations, it is RECOMMENDED that producers omit an
+ * application/
prefix of a media type value in a {@code cty} Header Parameter when
+ * no other '/' appears in the media type value. A recipient using the media type value MUST
+ * treat it as if application/
were prepended to any {@code cty} value not containing a
+ * '/'. For instance, a {@code cty} value of example
SHOULD be used to
+ * represent the application/example
media type, whereas the media type
+ * application/example;part="1/2"
cannot be shortened to
+ * example;part="1/2"
.
cty
(Content Type) header parameter value. A {@code null} value will remove the property from
- * the JSON map.
+ * Returns the JWT {@code alg} (Algorithm) header value or {@code null} if not present.
*
- * In the normal case where nested signing or encryption operations are not employed (i.e. a compact - * serialization JWT), the use of this header parameter is NOT RECOMMENDED. In the case that nested - * signing or encryption is employed, this Header Parameter MUST be present; in this case, the value MUST be - * {@code JWT}, to indicate that a Nested JWT is carried in this JWT. While media type names are not - * case-sensitive, it is RECOMMENDED that {@code JWT} always be spelled using uppercase characters for - * compatibility with legacy implementations. See - * JWT Appendix A.2 for - * an example of a Nested JWT.
+ *alg
(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)}
+ * to convert this string value to a type-safe {@code SecureDigestAlgorithm} instance.alg
(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 alg
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
+ * to a type-safe {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} instance.zip
(Compression Algorithm) header value or {@code null} if not present.
+ * Returns the JWT zip
+ * (Compression Algorithm) header parameter value or {@code null} if not present.
+ *
+ * Compatibility Note
+ * + *While the JWT family of specifications only defines the zip
header in the JWE
+ * (JSON Web Encryption) specification, JJWT will also support compression for JWS as well if you choose to use it.
+ * However, be aware that if you use compression when creating a JWS token, other libraries may not be able to
+ * parse the JWS. However, compression when creating JWE tokens should be universally accepted for any library
+ * that supports JWE.
zip
(Compression Algorithm) header parameter value. A {@code null} value will remove
- * the property from the JSON map.
- *
- * The compression algorithm is NOT part of the JWT specification - * and must be used carefully since, is not expected that other libraries (including previous versions of this one) - * be able to deserialize a compressed JTW body correctly.
- * - * @param zip the JWT compression algorithm {@code zip} value or {@code null} to remove the property from the JSON map. - * @return the {@code Header} instance for method chaining. - * @since 0.6.0 - */ - T setCompressionAlgorithm(String zip); - } diff --git a/api/src/main/java/io/jsonwebtoken/HeaderMutator.java b/api/src/main/java/io/jsonwebtoken/HeaderMutator.java new file mode 100644 index 00000000..0f6ec36f --- /dev/null +++ b/api/src/main/java/io/jsonwebtoken/HeaderMutator.java @@ -0,0 +1,100 @@ +/* + * 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; + +/** + * Mutation (modifications) to a {@link Header Header} instance. + * + * @paramtyp
(Type) header value. A {@code null} value will remove the property from the JSON map.
+ *
+ * @param typ the JWT JOSE {@code typ} header value or {@code null} to remove the property from the JSON map.
+ * @return the {@code Header} instance for method chaining.
+ */
+ T setType(String typ);
+
+ /**
+ * Sets the JWT
+ * cty
(Content Type) header parameter value. A {@code null} value will remove the property from
+ * the JSON map.
+ *
+ * The cty
(Content Type) Header Parameter is used by applications to declare the
+ * IANA MediaType of the content
+ * (the payload). This is intended for use by the application when more than
+ * one kind of object could be present in the Payload; the application can use this value to disambiguate among
+ * the different kinds of objects that might be present. It will typically not be used by applications when
+ * the kind of object is already known. This parameter is ignored by JWT implementations (like JJWT); any
+ * processing of this parameter is performed by the JWS application. Use of this Header Parameter is OPTIONAL.
To keep messages compact in common situations, it is RECOMMENDED that producers omit an
+ * application/
prefix of a media type value in a {@code cty} Header Parameter when
+ * no other '/' appears in the media type value. A recipient using the media type value MUST
+ * treat it as if application/
were prepended to any {@code cty} value not containing a
+ * '/'. For instance, a {@code cty} value of example
SHOULD be used to
+ * represent the application/example
media type, whereas the media type
+ * application/example;part="1/2"
cannot be shortened to
+ * example;part="1/2"
.
alg
(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 alg
value does not represent a
+ * supported algorithm, or if the recipient does not have a key that can be used with that algorithm.zip
+ * (Compression Algorithm) header parameter value. A {@code null} value will remove
+ * the property from the JSON map.
+ *
+ * Compatibility Note
+ * + *While the JWT family of specifications only defines the zip
header in the JWE
+ * (JSON Web Encryption) specification, JJWT will also support compression for JWS as well if you choose to use it.
+ * However, be aware that if you use compression when creating a JWS token, other libraries may not be able to
+ * parse the JWS. However, Compression when creating JWE tokens should be universally accepted for any library
+ * that supports JWE.
All JWT concepts that have a + * JWA 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.
+ * + *JJWT Type | + *How {@link #getId()} is Used | + *
---|---|
{@link io.jsonwebtoken.security.SignatureAlgorithm SignatureAlgorithm} | + *JWS protected header's + * {@code alg} (Algorithm) parameter value. | + *
{@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} | + *JWE protected header's + * {@code alg} (Key Management Algorithm) + * parameter value. | + *
{@link io.jsonwebtoken.security.AeadAlgorithm AeadAlgorithm} | + *JWE protected header's + * {@code enc} (Encryption Algorithm) + * parameter value. | + *
{@link io.jsonwebtoken.security.Jwk Jwk} | + *JWK's {@code kid} (Key ID) + * parameter value. | + *
The JWE {@code enc} (encryption algorithm) Header Parameter identifies the content encryption algorithm + * used to perform authenticated encryption on the plaintext to produce the ciphertext and the JWE + * {@code Authentication Tag}.
+ * + *Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by + * supplying an {@link AeadAlgorithm} to a {@link JwtBuilder} via one of its + * {@link JwtBuilder#encryptWith(SecretKey, AeadAlgorithm) encryptWith(SecretKey, AeadAlgorithm)} or + * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)} + * methods. JJWT will then set this {@code enc} header value automatically to the {@code AeadAlgorithm}'s + * {@link AeadAlgorithm#getId() getId()} value during encryption.
+ * + * @return the JWE {@code enc} (Encryption Algorithm) header value or {@code null} if not present. This will + * always be {@code non-null} on validly-constructed JWE instances, but could be {@code null} during construction. + * @see JwtBuilder#encryptWith(SecretKey, AeadAlgorithm) + * @see JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) + */ + String getEncryptionAlgorithm(); + + /** + * Returns the {@code epk} (Ephemeral + * Public Key) header value created by the JWE originator for use with key agreement algorithms, or + * {@code null} if not present. + * + *Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by + * supplying an ECDH-ES {@link KeyAlgorithm} to a {@link JwtBuilder} via its + * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)} + * method. The ECDH-ES {@code KeyAlgorithm} implementation will then set this {@code epk} header value + * automatically when producing the encryption key.
+ * + * @return the {@code epk} (Ephemeral + * Public Key) 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 + */ + PublicJwk> getEphemeralPublicKey(); + + /** + * Returns any information about the JWE producer for use with key agreement algorithms, or {@code null} if not + * present. + * + * @return any information about the JWE producer for use with key agreement algorithms, or {@code null} if not + * present. + * @see JWEapu
(Agreement PartyUInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ byte[] getAgreementPartyUInfo();
+
+ /**
+ * Returns any information about the JWE recipient for use with key agreement algorithms, or {@code null} if not
+ * present.
+ *
+ * @return any information about the JWE recipient for use with key agreement algorithms, or {@code null} if not
+ * present.
+ * @see JWE apv
(Agreement PartyVInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ byte[] getAgreementPartyVInfo();
+
+ /**
+ * Returns the 96-bit "iv"
+ * (Initialization Vector) generated during key encryption, or {@code null} if not present.
+ * Set by AES GCM {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} implementations.
+ *
+ * Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by + * supplying an AES GCM Wrap {@link KeyAlgorithm} to a {@link JwtBuilder} via its + * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)} + * method. The AES GCM Wrap {@code KeyAlgorithm} implementation will then set this {@code iv} header value + * automatically when producing the encryption key.
+ * + * @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 + */ + byte[] getInitializationVector(); + + /** + * Returns the 128-bit "tag" + * (Authentication Tag) resulting from key encryption, or {@code null} if not present. + * + *Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by + * supplying an AES GCM Wrap {@link KeyAlgorithm} to a {@link JwtBuilder} via its + * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)} + * method. The AES GCM Wrap {@code KeyAlgorithm} implementation will then set this {@code tag} header value + * automatically when producing the encryption key.
+ * + * @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 + */ + byte[] getAuthenticationTag(); + + /** + * Returns the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, or {@code null} + * if not present. Used with password-based {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm}s. + * + * @return the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, or {@code null} + * if not present. + * @see JWEp2c
(PBES2 Count) Header Parameter
+ * @see StandardKeyAlgorithms#PBES2_HS256_A128KW
+ * @see StandardKeyAlgorithms#PBES2_HS384_A192KW
+ * @see StandardKeyAlgorithms#PBES2_HS512_A256KW
+ */
+ Integer getPbes2Count();
+
+ /**
+ * Returns the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption, or
+ * {@code null} if not present.
+ *
+ * Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by + * supplying a password-based {@link KeyAlgorithm} to a {@link JwtBuilder} via its + * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)} + * method. The password-based {@code KeyAlgorithm} implementation will then set this {@code p2s} header value + * automatically when producing the encryption key.
+ * + * @return the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption, or + * {@code null} if not present. + * @see JWEp2s
(PBES2 Salt Input) Header Parameter
+ * @see StandardKeyAlgorithms#PBES2_HS256_A128KW
+ * @see StandardKeyAlgorithms#PBES2_HS384_A192KW
+ * @see StandardKeyAlgorithms#PBES2_HS512_A256KW
+ */
+ byte[] getPbes2Salt();
+}
diff --git a/api/src/main/java/io/jsonwebtoken/JweHeaderMutator.java b/api/src/main/java/io/jsonwebtoken/JweHeaderMutator.java
new file mode 100644
index 00000000..07e3e775
--- /dev/null
+++ b/api/src/main/java/io/jsonwebtoken/JweHeaderMutator.java
@@ -0,0 +1,146 @@
+/*
+ * 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.security.KeyAlgorithm;
+import io.jsonwebtoken.security.StandardKeyAlgorithms;
+
+/**
+ * Mutation (modifications) to a {@link JweHeader} instance.
+ *
+ * @param 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.
+// * +// * @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. + * + * @param info information about the JWE producer to use with key agreement algorithms. + * @return the header for method chaining. + * @see JWEapu
(Agreement PartyUInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ T setAgreementPartyUInfo(byte[] info);
+
+ /**
+ * Sets any information about the JWE producer for use with key agreement algorithms. A {@code null} value removes
+ * the property from the JSON map.
+ *
+ * If not {@code null}, this is a convenience method that calls the equivalent of the following:
+ *+ * + * @param info information about the JWE producer to use with key agreement algorithms. + * @return the header for method chaining. + * @see JWE+ * {@link #setAgreementPartyUInfo(byte[]) setAgreementPartyUInfo}(info.getBytes(StandardCharsets.UTF_8))
apu
(Agreement PartyUInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ T setAgreementPartyUInfo(String info);
+
+ /**
+ * Sets any information about the JWE recipient for use with key agreement algorithms. A {@code null} value removes
+ * the property from the JSON map.
+ *
+ * @param info information about the JWE recipient to use with key agreement algorithms.
+ * @return the header for method chaining.
+ * @see JWE apv
(Agreement PartyVInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ T setAgreementPartyVInfo(byte[] info);
+
+ /**
+ * Sets any information about the JWE recipient for use with key agreement algorithms. A {@code null} value removes
+ * the property from the JSON map.
+ *
+ * If not {@code null}, this is a convenience method that calls the equivalent of the following:
+ *+ * + * @param info information about the JWE recipient to use with key agreement algorithms. + * @return the header for method chaining. + * @see JWE+ * {@link #setAgreementPartyVInfo(byte[]) setAgreementPartVUInfo}(info.getBytes(StandardCharsets.UTF_8))
apv
(Agreement PartyVInfo) Header Parameter
+ * @see StandardKeyAlgorithms#ECDH_ES
+ * @see StandardKeyAlgorithms#ECDH_ES_A128KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A192KW
+ * @see StandardKeyAlgorithms#ECDH_ES_A256KW
+ */
+ T setAgreementPartyVInfo(String info);
+
+ /**
+ * Sets the number of PBKDF2 iterations necessary to derive the key used during JWE encryption. If this value
+ * is not set when a password-based {@link KeyAlgorithm} is used, JJWT will automatically choose a suitable
+ * number of iterations based on
+ * OWASP PBKDF2 Iteration Recommendations.
+ *
+ * Minimum Count
+ * + *{@code IllegalArgumentException} will be thrown during encryption if a specified {@code count} is + * less than 1000 (one thousand), which is the + * minimum number recommended by the + * JWA specification. Anything less is susceptible to security attacks so the default PBKDF2 + * {@code KeyAlgorithm} implementations reject such values.
+ * + * @param count the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, must be + * greater than or equal to 1000 (one thousand). + * @return the header for method chaining + * @see JWEp2c
(PBES2 Count) Header Parameter
+ * @see StandardKeyAlgorithms#PBES2_HS256_A128KW
+ * @see StandardKeyAlgorithms#PBES2_HS384_A192KW
+ * @see StandardKeyAlgorithms#PBES2_HS512_A256KW
+ * @see OWASP PBKDF2 Iteration Recommendations
+ */
+ 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 JWE p2s
(PBES2 Salt Input) Header Parameter
+// * @see Jwts.KEY#PBES2_HS256_A128KW
+// * @see Jwts.KEY#PBES2_HS384_A192KW
+// * @see Jwts.KEY#PBES2_HS512_A256KW
+// */
+// JweHeader setPbes2Salt(byte[] salt);
+
+}
diff --git a/api/src/main/java/io/jsonwebtoken/Jws.java b/api/src/main/java/io/jsonwebtoken/Jws.java
index 1be5fb39..8264386e 100644
--- a/api/src/main/java/io/jsonwebtoken/Jws.java
+++ b/api/src/main/java/io/jsonwebtoken/Jws.java
@@ -19,10 +19,14 @@ package io.jsonwebtoken;
* An expanded (not compact/serialized) Signed JSON Web Token.
*
* @param the type of the JWS body contents, either a String or a {@link Claims} instance.
- *
* @since 0.1
*/
-public interface Jws extends Jwt"alg"
*/
- public static final String ALGORITHM = "alg";
-
- /** JWS {@code JWT Set URL} header parameter name: "jku"
*/
- public static final String JWK_SET_URL = "jku";
-
- /** JWS {@code JSON Web Key} header parameter name: "jwk"
*/
- public static final String JSON_WEB_KEY = "jwk";
-
- /** JWS {@code Key ID} header parameter name: "kid"
*/
- public static final String KEY_ID = "kid";
-
- /** JWS {@code X.509 URL} header parameter name: "x5u"
*/
- public static final String X509_URL = "x5u";
-
- /** JWS {@code X.509 Certificate Chain} header parameter name: "x5c"
*/
- public static final String X509_CERT_CHAIN = "x5c";
-
- /** JWS {@code X.509 Certificate SHA-1 Thumbprint} header parameter name: "x5t"
*/
- public static final String X509_CERT_SHA1_THUMBPRINT = "x5t";
-
- /** JWS {@code X.509 Certificate SHA-256 Thumbprint} header parameter name: "x5t#S256"
*/
- public static final String X509_CERT_SHA256_THUMBPRINT = "x5t#S256";
-
- /** JWS {@code Critical} header parameter name: "crit"
*/
- public static final String CRITICAL = "crit";
+public interface JwsHeader extends ProtectedHeaderalg
(algorithm) header value or {@code null} if not present.
- *
- * The algorithm header parameter identifies the cryptographic algorithm used to secure the JWS. Consider - * using {@link io.jsonwebtoken.SignatureAlgorithm#forName(String) SignatureAlgorithm.forName} to convert this - * string value to a type-safe enum instance.
- * - * @return the JWS {@code alg} header value or {@code null} if not present. This will always be - * {@code non-null} on validly constructed JWS instances, but could be {@code null} during construction. + * JWS Algorithm Header name: the string literalalg
*/
- String getAlgorithm();
+ String ALGORITHM = "alg";
/**
- * Sets the JWT
- * alg
(Algorithm) header value. A {@code null} value will remove the property from the JSON map.
- *
- * The algorithm header parameter identifies the cryptographic algorithm used to secure the JWS. Consider - * using a type-safe {@link io.jsonwebtoken.SignatureAlgorithm SignatureAlgorithm} instance and using its - * {@link io.jsonwebtoken.SignatureAlgorithm#getValue() value} as the argument to this method.
- * - * @param alg the JWS {@code alg} header value or {@code null} to remove the property from the JSON map. - * @return the {@code Header} instance for method chaining. + * JWS JWK Set URL Header name: the string literaljku
*/
- T setAlgorithm(String alg);
+ String JWK_SET_URL = "jku";
/**
- * Returns the JWS
- * kid
(Key ID) header value or {@code null} if not present.
- *
- * The keyId header parameter is a hint indicating which key was used to secure the JWS. This parameter allows - * originators to explicitly signal a change of key to recipients. The structure of the keyId value is - * unspecified.
- * - *When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.
- * - * @return the JWS {@code kid} header value or {@code null} if not present. + * JWS JSON Web Key Header name: the string literaljwk
*/
- String getKeyId();
+ String JSON_WEB_KEY = "jwk";
/**
- * Sets the JWT
- * kid
(Key ID) header value. A {@code null} value will remove the property from the JSON map.
- *
- * The keyId header parameter is a hint indicating which key was used to secure the JWS. This parameter allows - * originators to explicitly signal a change of key to recipients. The structure of the keyId value is - * unspecified.
- * - *When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.
- * - * @param kid the JWS {@code kid} header value or {@code null} to remove the property from the JSON map. - * @return the {@code Header} instance for method chaining. + * JWS Key ID Header name: the string literalkid
*/
- T setKeyId(String kid);
+ String KEY_ID = "kid";
+
+ /**
+ * JWS X.509 URL Header name: the string literal x5u
+ */
+ String X509_URL = "x5u";
+
+ /**
+ * JWS X.509 Certificate Chain Header name: the string literal x5c
+ */
+ String X509_CERT_CHAIN = "x5c";
+
+ /**
+ * JWS X.509 Certificate SHA-1 Thumbprint Header name: the string literal x5t
+ */
+ String X509_CERT_SHA1_THUMBPRINT = "x5t";
+
+ /**
+ * JWS X.509 Certificate SHA-256 Thumbprint Header name: the string literal x5t#S256
+ */
+ String X509_CERT_SHA256_THUMBPRINT = "x5t#S256";
+
+ /**
+ * JWS Critical Header name: the string literal crit
+ */
+ String CRITICAL = "crit";
}
diff --git a/api/src/main/java/io/jsonwebtoken/Jwt.java b/api/src/main/java/io/jsonwebtoken/Jwt.java
index 1de5c6e3..839b7b23 100644
--- a/api/src/main/java/io/jsonwebtoken/Jwt.java
+++ b/api/src/main/java/io/jsonwebtoken/Jwt.java
@@ -18,11 +18,10 @@ package io.jsonwebtoken;
/**
* An expanded (not compact/serialized) JSON Web Token.
*
- * @param the type of the JWT body contents, either a String or a {@link Claims} instance.
- *
+ * @param the type of the JWT payload, either a byte array or a {@link Claims} instance.
* @since 0.1
*/
-public interface Jwt This is a convenience method that is effectively the same as: If you want the JWT payload to be JSON, use the
+ * {@link #setClaims(Claims)} or {@link #setClaims(java.util.Map)} methods instead. The payload and claims properties are mutually exclusive - only one of the two may be used. Content Type Recommendation Unless you are confident that the JWT recipient will always know how to use
+ * the given byte array without additional metadata, it is strongly recommended to use the
+ * {@link #setContent(byte[], String)} method instead of this one. That method ensures that a JWT recipient
+ * can inspect the {@code cty} header to know how to handle the byte array without ambiguity. Note that the content and claims properties are mutually exclusive - only one of the two may be used. Compact Media Type Identifier As a convenience, this method will automatically trim any 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. If you want the JWT payload to be JSON claims, use the {@link #setClaims(Claims)} or
+ * {@link #setClaims(java.util.Map)} methods instead. Note that the content and claims properties are mutually exclusive - only one of the two may be used. The payload and claims properties are mutually exclusive - only one of the two may be used. The payload* and claims* properties are mutually exclusive - only one of the two may be used. The payload and claims properties are mutually exclusive - only one of the two may be used. The payload and claims properties are mutually exclusive - only one of the two may be used. This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* code like this: This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* code like this: This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* code like this: A JWT obtained after this timestamp should not be used. This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* you to write code like this: A JWT obtained before this timestamp should not be used. This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* you to write code like this: The value is the timestamp when the JWT was created. This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set
+ * 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
* you to write code like this:
+ *
+ *
+ * {@link #setContent(byte[]) setPayload}(payload.getBytes(StandardCharsets.UTF_8));
application/
prefix from the
+ * {@code cty} string if possible according to the
+ * JWT specification recommendations.iss
(issuer) value. A {@code null} value will remove the property from the Claims.
*
- * sub
(subject) value. A {@code null} value will remove the property from the Claims.
*
- * aud
(audience) value. A {@code null} value will remove the property from the Claims.
*
- *
This is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set + *
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 * you to write code like this:
* @@ -322,7 +426,7 @@ public interface JwtBuilder extends ClaimsMutatorThis is a convenience method. It will first ensure a Claims instance exists as the JWT body and then set the + *
This is a convenience method. It will first ensure a Claims instance exists as the JWT payload and then set the * named property on the Claims instance using the Claims {@link Claims#put(Object, Object) put} method. This allows * you to write code like this:
* @@ -345,20 +449,144 @@ public interface JwtBuilder extends ClaimsMutatorIf you are looking to invoke this method with a byte array that you are confident may be used for HMAC-SHA * algorithms, consider using {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to * convert the byte array into a valid {@code Key}.
* + *Recommended Signature Algorithm
+ * + *The recommended signature algorithm used with a given key is chosen based on the following:
+ *If the Key is a: | + *And: | + *With a key size of: | + *The SignatureAlgorithm used will be: | + *
---|---|---|---|
{@link SecretKey} | + *{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA256") 1 |
+ * 256 <= size <= 383 2 | + *{@link StandardSecureDigestAlgorithms#HS256 HS256} | + *
{@link SecretKey} | + *{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA384") 1 |
+ * 384 <= size <= 511 | + *{@link StandardSecureDigestAlgorithms#HS384 HS384} | + *
{@link SecretKey} | + *{@link Key#getAlgorithm() getAlgorithm()}.equals("HmacSHA512") 1 |
+ * 512 <= size | + *{@link StandardSecureDigestAlgorithms#HS512 HS512} | + *
{@link ECKey} | + *instanceof {@link PrivateKey} |
+ * 256 <= size <= 383 3 | + *{@link StandardSecureDigestAlgorithms#ES256 ES256} | + *
{@link ECKey} | + *instanceof {@link PrivateKey} |
+ * 384 <= size <= 520 4 | + *{@link StandardSecureDigestAlgorithms#ES384 ES384} | + *
{@link ECKey} | + *instanceof {@link PrivateKey} |
+ * 521 <= size 4 | + *{@link StandardSecureDigestAlgorithms#ES512 ES512} | + *
{@link RSAKey} | + *instanceof {@link PrivateKey} |
+ * 2048 <= size <= 3071 5,6 | + *{@link StandardSecureDigestAlgorithms#RS256 RS256} | + *
{@link RSAKey} | + *instanceof {@link PrivateKey} |
+ * 3072 <= size <= 4095 6 | + *{@link StandardSecureDigestAlgorithms#RS384 RS384} | + *
{@link RSAKey} | + *instanceof {@link PrivateKey} |
+ * 4096 <= size 5 | + *{@link StandardSecureDigestAlgorithms#RS512 RS512} | + *
EdECKey7 | + *instanceof {@link PrivateKey} |
+ * 256 | + *{@link StandardSecureDigestAlgorithms#Ed25519 Ed25519} | + *
EdECKey7 | + *instanceof {@link PrivateKey} |
+ * 456 | + *{@link StandardSecureDigestAlgorithms#Ed448 Ed448} | + *
Notes:
+ *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 + * 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.
+ * + *Finally, this method will throw an {@link InvalidKeyException} for any key that does not match the + * heuristics and requirements documented above, since that inevitably means the Key is either insufficient, + * unsupported, or explicitly disallowed by the JWT specification.
+ * * @param key the key to use for signing * @return the builder instance for method chaining. - * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as - * described by {@link SignatureAlgorithm#forSigningKey(Key)}. - * @see #signWith(Key, SignatureAlgorithm) + * @throws InvalidKeyException if the Key is insufficient, unsupported, or explicitly disallowed by the JWT + * specification as described above in recommended signature algorithms. + * @see Jwts#SIG + * @see #signWith(Key, SecureDigestAlgorithm) * @since 0.10.0 */ JwtBuilder signWith(Key key) throws InvalidKeyException; @@ -369,17 +597,19 @@ public interface JwtBuilder extends ClaimsMutatorDeprecation Notice: Deprecated as of 0.10.0
* *Use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to - * obtain the {@code Key} and then invoke {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)}.
+ * obtain the {@code Key} and then invoke {@link #signWith(Key)} or + * {@link #signWith(Key, SecureDigestAlgorithm)}. * *This method will be removed in the 1.0 release.
* * @param alg the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS. * @param secretKey the algorithm-specific signing key to use to digitally sign the JWT. * @return the builder for method chaining. - * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as - * described by {@link SignatureAlgorithm#forSigningKey(Key)}. + * @throws InvalidKeyException if the Key is insufficient for the specified algorithm or explicitly disallowed by + * the JWT specification. * @deprecated as of 0.10.0: use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to - * obtain the {@code Key} and then invoke {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)}. + * obtain the {@code Key} and then invoke {@link #signWith(Key)} or + * {@link #signWith(Key, SecureDigestAlgorithm)}. * This method will be removed in the 1.0 release. */ @Deprecated @@ -403,7 +633,7 @@ public interface JwtBuilder extends ClaimsMutator{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}
* *However, a non-trivial number of JJWT users were confused by the method signature and attempted to - * use raw password strings as the key argument - for example {@code signWith(HS256, myPassword)} - which is + * use raw password strings as the key argument - for example {@code with(HS256, myPassword)} - which is * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.
* *See this
@@ -415,7 +645,7 @@ public interface JwtBuilder extends ClaimsMutator This method will be removed in the 1.0 release. Deprecation Notice This has been deprecated since JJWT_RELEASE_VERSION. Use
+ * {@link #signWith(Key, SecureDigestAlgorithm)} instead. Standard JWA algorithms
+ * are represented as instances of this new interface in the {@link Jwts#SIG}
+ * algorithm registry. Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS. It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.
* However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if
@@ -465,11 +702,97 @@ public interface JwtBuilder extends ClaimsMutator The {@link Jwts#SIG} registry makes available all standard signature
+ * algorithms defined in the JWA specification. It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.
+ * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if
+ * you want explicit control over the signature algorithm used with the specified key. This method is a convenience method that delegates to
+ * {@link #encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}
+ * based on the {@code key} argument: This behavior can be illustrated by the following pseudocode, a rough example of what happens during
+ * {@link #compact() compact}ion: Most application developers will reference one of the JWA
+ * {@link Jwts#KEY standard key algorithms} and {@link Jwts#ENC standard encryption algorithms}
+ * when invoking this method, but custom implementations are also supported. 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
@@ -477,7 +800,7 @@ public interface JwtBuilder extends ClaimsMutator Compatibility Warning The JWT family of specifications defines compression only for JWE (Json Web Encryption)
+ * The JWT family of specifications defines compression only for JWE (JSON Web Encryption)
* tokens. Even so, JJWT will also support compression for JWS tokens as well if you choose to use it.
* However, be aware that if you use compression when creating a JWS token, other libraries may not be able to
* parse that JWS token. When using compression for JWS tokens, be sure that all parties accessing the
@@ -507,7 +830,7 @@ public interface JwtBuilder extends ClaimsMutator If this method is not called, JJWT will use whatever serializer it can find at runtime, checking for the
* presence of well-known implementations such Jackson, Gson, and org.json. If one of these is not found
diff --git a/api/src/main/java/io/jsonwebtoken/JwtException.java b/api/src/main/java/io/jsonwebtoken/JwtException.java
index c25aa223..e3990dab 100644
--- a/api/src/main/java/io/jsonwebtoken/JwtException.java
+++ b/api/src/main/java/io/jsonwebtoken/JwtException.java
@@ -22,10 +22,21 @@ package io.jsonwebtoken;
*/
public class JwtException extends RuntimeException {
+ /**
+ * Creates a new instance with the specified explanation message.
+ *
+ * @param message the message explaining why the exception is thrown.
+ */
public JwtException(String message) {
super(message);
}
+ /**
+ * Creates a new instance with the specified explanation message and underlying cause.
+ *
+ * @param message the message explaining why the exception is thrown.
+ * @param cause the underlying cause that resulted in this exception being thrown.
+ */
public JwtException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/api/src/main/java/io/jsonwebtoken/JwtHandler.java b/api/src/main/java/io/jsonwebtoken/JwtHandler.java
index 0e23f833..a9778607 100644
--- a/api/src/main/java/io/jsonwebtoken/JwtHandler.java
+++ b/api/src/main/java/io/jsonwebtoken/JwtHandler.java
@@ -17,7 +17,7 @@ package io.jsonwebtoken;
/**
* A JwtHandler is invoked by a {@link io.jsonwebtoken.JwtParser JwtParser} after parsing a JWT to indicate the exact
- * type of JWT or JWS parsed.
+ * type of JWT, JWS or JWE parsed.
*
* @param This method will only be invoked if the cryptographic signature can be successfully verified. This method will only be invoked if the cryptographic signature can be successfully verified. This method will only be invoked if the content JWE can be successfully decrypted. This method will only be invoked if the Claims JWE can be successfully decrypted. All of the methods in this implementation throw exceptions: overridden methods represent
- * scenarios expected by calling code in known situations. It would be unexpected to receive a JWS or JWT that did
+ * scenarios expected by calling code in known situations. It would be unexpected to receive a JWT that did
* not match parsing expectations, so all non-overridden methods throw exceptions to indicate that the JWT
* input was unexpected. NOTE: this method will be removed before version 1.0
@@ -259,7 +268,7 @@ public interface JwtParser {
* @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signature verification key to use to validate
* any discovered JWS digital signature.
* @return the parser for method chaining.
- * @deprecated see {@link JwtParserBuilder#setSigningKey(String)}.
+ * @deprecated in favor of {@link JwtParserBuilder#verifyWith(Key)}.
* To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an
* immutable JwtParser.
* NOTE: this method will be removed before version 1.0
@@ -279,7 +288,7 @@ public interface JwtParser {
* @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital
* signature.
* @return the parser for method chaining.
- * @deprecated see {@link JwtParserBuilder#setSigningKey(Key)}.
+ * @deprecated in favor of {@link JwtParserBuilder#verifyWith(Key)}.
* To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an
* immutable JwtParser.
* NOTE: this method will be removed before version 1.0
@@ -292,7 +301,7 @@ public interface JwtParser {
* a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
*
* Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing
- * the JWT and the JWT header or payload (plaintext body or Claims) must be inspected first to determine how to
+ * the JWT and the JWT header or payload (content byte array or Claims) must be inspected first to determine how to
* look up the signing key. Once returned by the resolver, the JwtParser will then verify the JWS signature with the
* returned key. For example: NOTE: this method will be removed before version 1.0
*/
+ @SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
+ // TODO: remove for 1.0
JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
/**
* 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.
+ * decompress the JWT payload. If the parsed JWT is not compressed, this resolver is not used.
*
- * NOTE: Compression is not defined by the JWT Specification, and it is not expected that other libraries
- * (including JJWT versions < 0.6.0) are able to consume a compressed JWT body correctly. This method is only
- * useful if the compact JWT was compressed with JJWT >= 0.6.0 or another library that you know implements
- * the same behavior. NOTE: Compression is not defined by the JWS Specification - only the JWE Specification - and it is
+ * not expected that other libraries (including JJWT versions < 0.6.0) are able to consume a compressed JWS
+ * payload correctly. This method is only useful if the compact JWT was compressed with JJWT >= 0.6.0 or another
+ * library that you know implements the same behavior. Default Support NOTE: this method will be removed before version 1.0
@@ -397,42 +408,45 @@ public interface JwtParser {
* Note that if you are reasonably sure that the token is signed, it is more efficient to attempt to
* parse the token (and catching exceptions if necessary) instead of calling this method first before parsing. This method returns a JWT or JWS based on the parsed string. Because it may be cumbersome to determine if it
- * is a JWT or JWS, or if the body/payload is a Claims or String with {@code instanceof} checks, the
- * {@link #parse(String, JwtHandler) parse(String,JwtHandler)} method allows for a type-safe callback approach that
- * may help reduce code or instanceof checks. This method returns a JWT, JWS, or JWE based on the parsed string. Because it may be cumbersome to
+ * determine if it is a JWT, JWS or JWE, or if the payload is a Claims or byte array with {@code instanceof} checks,
+ * the {@link #parse(String, JwtHandler) parse(String,JwtHandler)} method allows for a type-safe callback approach
+ * that may help reduce code or instanceof checks. If you are confident of the format of the JWT before parsing, you can create an anonymous subclass using the
* {@link io.jsonwebtoken.JwtHandlerAdapter JwtHandlerAdapter} and override only the methods you know are relevant
@@ -453,145 +467,220 @@ public interface JwtParser {
* following convenience methods instead of this one: This is a convenience method that is usable if you are confident that the compact string argument reflects an
- * unsigned plaintext JWT. An unsigned plaintext JWT has a String (non-JSON) body payload and it is not
- * cryptographically signed. If the compact string presented does not reflect an unsigned plaintext JWT with non-JSON string body,
+ * If the compact string presented does not reflect an unprotected content JWT with byte array payload,
* an {@link UnsupportedJwtException} will be thrown. This is a convenience method that is usable if you are confident that the compact string argument reflects an
- * unsigned Claims JWT. An unsigned Claims JWT has a {@link Claims} body and it is not cryptographically
- * signed. If the compact string presented does not reflect an unsigned Claims JWT, an
+ * If the compact string presented does not reflect an unprotected Claims JWT, an
* {@link UnsupportedJwtException} will be thrown. This is a convenience method that is usable if you are confident that the compact string argument reflects a
- * plaintext JWS. A plaintext JWS is a JWT with a String (non-JSON) body (payload) that has been
- * cryptographically signed. If the compact string presented does not reflect a plaintext JWS, an {@link UnsupportedJwtException}
+ * If the compact string presented does not reflect a content JWS, an {@link UnsupportedJwtException}
* will be thrown. This is a convenience method that is usable if you are confident that the compact string argument reflects a
- * Claims JWS. A Claims JWS is a JWT with a {@link Claims} body that has been cryptographically signed.
*
*
* byte[] keyBytes = {@link Decoders Decoders}.{@link Decoders#BASE64 BASE64}.{@link Decoder#decode(Object) decode(base64EncodedSecretKey)};
* Key key = {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(keyBytes)};
- * jwtBuilder.signWith(key); //or {@link #signWith(Key, SignatureAlgorithm)}
+ * jwtBuilder.with(key); //or {@link #signWith(Key, SignatureAlgorithm)}
*
+ *
+ *
+ * @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}.
+ * @return the JWE builder for method chaining.
+ * @see Jwts#ENC
+ */
+ JwtBuilder encryptWith(SecretKey key, AeadAlgorithm enc);
+
+ /**
+ * Encrypts the constructed JWT using the specified {@code enc} algorithm with the symmetric key produced by the
+ * {@code keyAlg} when invoked with the given {@code key}, producing a JWE.
+ *
+ *
+ *
+ * SecretKey encryptionKey = keyAlg.getEncryptionKey(key); // (1)
+ * byte[] jweCiphertext = enc.encrypt(payloadBytes, encryptionKey); // (2)
+ *
+ *
+ *
- *
*
- * @param jwt the compact serialized JWT to parse
+ * @param jwt the compact serialized JWT to parse
* @param handler the handler to invoke when encountering a specific type of JWT
- * @param
If the compact string presented does not reflect a Claims JWS, an {@link UnsupportedJwtException} will be * thrown.
* - * @param claimsJws a compact serialized Claims JWS string. + * @param jws a compact serialized Claims JWS string. * @return the {@link Jws Jws} instance that reflects the specified compact Claims JWS string. * @throws UnsupportedJwtException if the {@code claimsJws} argument does not represent an Claims JWS * @throws MalformedJwtException if the {@code claimsJws} string is not a valid JWS * @throws SignatureException if the {@code claimsJws} JWS signature validation fails + * @throws SecurityException if the {@code jws} string is actually a JWE and decryption fails * @throws ExpiredJwtException if the specified JWT is a Claims JWT and the Claims has an expiration time * before the time this method is invoked. * @throws IllegalArgumentException if the {@code claimsJws} string is {@code null} or empty or only whitespace - * @see #parsePlaintextJwt(String) + * @see #parseContentJwt(String) + * @see #parseContentJws(String) + * @see #parseContentJwe(String) * @see #parseClaimsJwt(String) - * @see #parsePlaintextJws(String) + * @see #parseClaimsJwe(String) * @see #parse(String, JwtHandler) * @see #parse(String) * @since 0.2 */ - JwsThis is a convenience method that is usable if you are confident that the compact string argument reflects a + * content JWE. A content JWE is a JWT with a byte array payload that has been encrypted.
+ * + *If the compact string presented does not reflect a content JWE, an {@link UnsupportedJwtException} + * will be thrown.
+ * + * @param jwe a compact serialized JWE string. + * @return the {@link Jwe Jwe} instance that reflects the specified compact JWE string. + * @throws UnsupportedJwtException if the {@code jwe} argument does not represent a content JWE + * @throws MalformedJwtException if the {@code jwe} string is not a valid JWE + * @throws SecurityException if the {@code jwe} JWE decryption fails + * @throws IllegalArgumentException if the {@code jwe} string is {@code null} or empty or only whitespace + * @see #parseContentJwt(String) + * @see #parseContentJws(String) + * @see #parseClaimsJwt(String) + * @see #parseClaimsJws(String) + * @see #parseClaimsJwe(String) + * @see #parse(String, JwtHandler) + * @see #parse(String) + * @since JJWT_RELEASE_VERSION + */ + JweThis is a convenience method that is usable if you are confident that the compact string argument reflects a + * Claims JWE. A Claims JWE is a JWT with a {@link Claims} payload that has been encrypted.
+ * + *If the compact string presented does not reflect a Claims JWE, an {@link UnsupportedJwtException} will be + * thrown.
+ * + * @param jwe a compact serialized Claims JWE string. + * @return the {@link Jwe Jwe} instance that reflects the specified compact Claims JWE string. + * @throws UnsupportedJwtException if the {@code claimsJwe} argument does not represent a Claims JWE + * @throws MalformedJwtException if the {@code claimsJwe} string is not a valid JWE + * @throws SignatureException if the {@code claimsJwe} JWE decryption fails + * @throws ExpiredJwtException if the specified JWT is a Claims JWE and the Claims has an expiration time + * before the time this method is invoked. + * @throws IllegalArgumentException if the {@code claimsJwe} string is {@code null} or empty or only whitespace + * @see #parseContentJwt(String) + * @see #parseContentJws(String) + * @see #parseContentJwe(String) + * @see #parseClaimsJwt(String) + * @see #parseClaimsJws(String) + * @see #parse(String, JwtHandler) + * @see #parse(String) + * @since JJWT_RELEASE_VERSION + */ + Jwe{@code * Jwts.parserBuilder() - * .setSigningKey(...) * .requireIssuer("https://issuer.example.com") + * .verifyWith(...) * .build() * .parse(jwtString) * }@@ -35,7 +43,65 @@ import java.util.Map; * @since 0.11.0 */ @SuppressWarnings("JavadocLinkAsPlainText") -public interface JwtParserBuilder { +public interface JwtParserBuilder extends Builder
If this method is not called, Unsecured JWSs are disabled by default as mandated by + * RFC 7518, Section + * 3.6.
+ * + * @return the builder for method chaining. + * @see Unsecured JWS Security Considerations + * @see Using the Algorithm "none" + * @see StandardSecureDigestAlgorithms#NONE + * @see #enableUnsecuredDecompression() + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder enableUnsecuredJws(); + + /** + * If {@link #enableUnsecuredJws() enabledUnsecuredJws} is enabled, calling this method additionally enables + * payload decompression of Unsecured JWSs (JWTs with an 'alg' (Algorithm) header value of 'none') that also have + * a 'zip' (Compression) header. This behavior is disabled by default because using compression + * algorithms with data from unverified (unauthenticated) parties can be susceptible to Denial of Service attacks + * and other data integrity problems as described in + * In the + * Compression Hornet’s Nest: A Security Study of Data Compression in Network Services. + * + *Because this behavior is only relevant if {@link #enableUnsecuredJws() enabledUnsecuredJws} is specified, + * calling this method without also calling {@code enableUnsecuredJws()} will result in a build exception, as the + * incongruent state could reflect a misunderstanding of both behaviors which should be remedied by the + * application developer.
+ * + * As is the case for {@link #enableUnsecuredJws()}, be careful when calling this method - one should fully + * understand + * Unsecured JWS Security Considerations + * before enabling this feature. + * + * @return the builder for method chaining. + * @see Unsecured JWS Security Considerations + * @see In the + * Compression Hornet’s Nest: A Security Study of Data Compression in Network Services + * @see StandardSecureDigestAlgorithms#NONE + * @see #enableUnsecuredJws() + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder enableUnsecuredDecompression(); + + /** + * Sets the JCA Provider to use during cryptographic signature and decryption operations, or {@code null} if the + * JCA subsystem preferred provider should be used. + * + * @param provider the JCA Provider to use during cryptographic signature and decryption operations, or {@code null} + * if the JCA subsystem preferred provider should be used. + * @return the builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder setProvider(Provider provider); /** * Ensures that the specified {@code jti} exists in the parsed JWT. If missing or if the parsed @@ -156,8 +222,17 @@ public interface JwtParserBuilder { JwtParserBuilder setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException; /** - * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not - * a JWS (no signature), this key is not used. + *Deprecation Notice
+ * + *This method has been deprecated since JJWT_RELEASE_VERSION and will be removed before 1.0. It was not + * readily obvious to many JJWT users that this method was for bytes that pertained only to HMAC + * {@code SecretKey}s, and could be confused with keys of other types. It is better to obtain a type-safe + * {@link Key} instance and call the {@link #verifyWith(Key)} instead.
+ * + *Previous Documentation
+ * + *Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not + * a JWS (no signature), this key is not used.
* *Note that this key MUST be a valid key for the signature algorithm found in the JWT header * (as the {@code alg} header parameter).
@@ -167,21 +242,13 @@ public interface JwtParserBuilder { * @param key the algorithm-specific signature verification key used to validate any discovered JWS digital * signature. * @return the parser builder for method chaining. + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #verifyWith(Key)} for type safety and name congruence + * with the {@link #decryptWith(Key)} method. */ + @Deprecated JwtParserBuilder setSigningKey(byte[] key); /** - * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not - * a JWS (no signature), this key is not used. - * - *Note that this key MUST be a valid key for the signature algorithm found in the JWT header - * (as the {@code alg} header parameter).
- * - *This method overwrites any previously set key.
- * - *This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting - * byte array is used to invoke {@link #setSigningKey(byte[])}.
- * *Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0
* *This method has been deprecated because the {@code key} argument for this method can be confusing: keys for @@ -202,39 +269,140 @@ public interface JwtParserBuilder { * StackOverflow answer explaining why raw (non-base64-encoded) strings are almost always incorrect for * signature operations.
* - *Finally, please use the {@link #setSigningKey(Key) setSigningKey(Key)} instead, as this method (and likely the - * {@code byte[]} variant) will be removed before the 1.0.0 release.
+ *Finally, please use the {@link #verifyWith(Key)} method instead, as this method (and likely + * {@link #setSigningKey(byte[])}) will be removed before the 1.0.0 release.
* - * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signature verification key to use to validate - * any discovered JWS digital signature. + *Previous JavaDoc
+ * + *This is a convenience method that equates to the following:
+ * + *+ * + * @param base64EncodedSecretKey BASE64-encoded HMAC-SHA key bytes used to create a Key which will be used to + * verify all encountered JWS digital signatures. * @return the parser builder for method chaining. - * @deprecated in favor of {@link #setSigningKey(Key)} as explained in the above Deprecation Notice, + * @deprecated in favor of {@link #verifyWith(Key)} as explained in the above Deprecation Notice, * and will be removed in 1.0.0. */ @Deprecated JwtParserBuilder setSigningKey(String base64EncodedSecretKey); /** - * Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not - * a JWS (no signature), this key is not used. + *+ * byte[] bytes = Decoders.{@link io.jsonwebtoken.io.Decoders#BASE64 BASE64}.decode(base64EncodedSecretKey); + * Key key = Keys.{@link io.jsonwebtoken.security.Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor}(bytes); + * return {@link #verifyWith(Key) verifyWith}(key);
Deprecation Notice
* - *Note that this key MUST be a valid key for the signature algorithm found in the JWT header - * (as the {@code alg} header parameter).
+ *This method is being renamed to accurately reflect its purpose - the key is not technically a signing key, + * it is a signature verification key, and the two concepts can be different, especially with asymmetric key + * cryptography. The method has been deprecated since JJWT_RELEASE_VERSION in favor of + * {@link #verifyWith(Key)} for type safety, to reflect accurate naming of the concept, and for name congruence + * with the {@link #decryptWith(Key)} method.
* - *This method overwrites any previously set key.
+ *This method merely delegates directly to {@link #verifyWith(Key)}.
* - * @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital - * signature. + * @param key the algorithm-specific signature verification key to use to verify all encountered JWS digital + * signatures. * @return the parser builder for method chaining. + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #verifyWith(Key)} for naming congruence with the + * {@link #decryptWith(Key)} method. */ + @Deprecated JwtParserBuilder setSigningKey(Key key); /** - * Sets the {@link SigningKeyResolver} used to acquire thesigning key
that should be used to verify
- * a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
+ * Sets the signature verification key used to verify all encountered JWS signatures. If the encountered JWT
+ * string is not a JWS (e.g. unsigned or a JWE), this key is not used.
+ *
+ * This is a convenience method to use in a specific scenario: when the parser will only ever encounter + * JWSs with signatures that can always be verified by a single key. This also implies that this key + * MUST be a valid key for the signature algorithm ({@code alg} header) used for the JWS.
+ * + *If there is any chance that the parser will encounter JWSs + * that need different signature verification keys based on the JWS being parsed, or JWEs, it is strongly + * recommended to configure your own {@link Locator} via the + * {@link #setKeyLocator(Locator) setKeyLocator} method instead of using this one.
+ * + *Calling this method overrides any previously set signature verification key.
+ * + * @param key the signature verification key to use to verify all encountered JWS digital signatures. + * @return the parser builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder verifyWith(Key key); + + /** + * Sets the decryption key to be used to decrypt all encountered JWEs. If the encountered JWT string is not a + * JWE (e.g. a JWS), this key is not used. + * + *This is a convenience method to use in specific circumstances: when the parser will only ever encounter + * JWEs that can always be decrypted by a single key. This also implies that this key MUST be a valid + * key for both the key management algorithm ({@code alg} header) and the content encryption algorithm + * ({@code enc} header) used for the JWE.
+ * + *If there is any chance that the parser will encounter JWEs that need different decryption keys based on the + * JWE being parsed, or JWSs, it is strongly recommended to configure + * your own {@link Locator Locator} via the {@link #setKeyLocator(Locator) setKeyLocator} method instead of + * using this one.
+ * + *Calling this method overrides any previously set decryption key.
+ * + * @param key the algorithm-specific decryption key to use to decrypt all encountered JWEs. + * @return the parser builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder decryptWith(Key key); + + /** + * Sets the {@link Locator} used to acquire any signature verification or decryption key needed during parsing. + *Specifying a key {@code Locator} is necessary when the signing or decryption key is not already known before + * parsing the JWT and the JWT header must be inspected first to determine how to + * look up the verification or decryption key. Once returned by the locator, the JwtParser will then either + * verify the JWS signature or decrypt the JWE payload with the returned key. For example:
+ * + *+ * Jws<Claims> jws = Jwts.parserBuilder().setKeyLocator(new Locator<Key>() { + * @Override + * public Key locate(Header<?> header) { + * if (header instanceof JwsHeader) { + * return getSignatureVerificationKey((JwsHeader)header); // implement me + * } else { + * return getDecryptionKey((JweHeader)header); // implement me + * } + * }}) + * .build() + * .parseClaimsJws(compact); + *+ * + *
A Key {@code Locator} is invoked once during parsing before performing decryption or signature verification.
+ * + * @param keyLocator the locator used to retrieve decryption or signature verification keys. + * @return the parser builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder setKeyLocator(LocatorDeprecation Notice
+ * + *This method has been deprecated as of JJWT version JJWT_RELEASE_VERSION because it only supports key location + * for JWSs (signed JWTs) instead of both signed (JWS) and encrypted (JWE) scenarios. Use the + * {@link #setKeyLocator(Locator) setKeyLocator} method instead to ensure a locator that can work for both JWS and + * JWE inputs. This method will be removed for the 1.0 release.
+ * + *Previous Documentation
+ * + *Sets the {@link SigningKeyResolver} used to acquire the signing key
that should be used to verify
+ * a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing - * the JWT and the JWT header or payload (plaintext body or Claims) must be inspected first to determine how to + * the JWT and the JWT header or payload (content byte array or Claims) must be inspected first to determine how to * look up the signing key. Once returned by the resolver, the JwtParser will then verify the JWS signature with the * returned key. For example:
* @@ -250,22 +418,104 @@ public interface JwtParserBuilder { * *A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.
* - *This method should only be used if a signing key is not provided by the other {@code setSigningKey*} builder - * methods.
- * * @param signingKeyResolver the signing key resolver used to retrieve the signing key. * @return the parser builder for method chaining. + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #setKeyLocator(Locator)} */ + @SuppressWarnings("DeprecatedIsStillUsed") + @Deprecated 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. + * + *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} + * collection, the later element will evict any previously-added algorithm with the same {@code id}.
+ * + *Finally, {@link CompressionCodecs#DEFLATE} and {@link CompressionCodecs#GZIP} are added last, + * after those in the {@code codecs} collection, to ensure that JWA standard algorithms cannot be + * accidentally replaced.
+ * + *This method is a simpler alternative than creating and registering a custom locator via the + * {@link #setCompressionCodecLocator(Locator)} method.
+ * + * @param codecs collection of compression codecs to add to the parser's total set of supported compression codecs. + * @return the builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder addCompressionCodecs(Collection extends CompressionCodec> codecs); + + /** + * Adds the specified AEAD encryption algorithms to the parser's total set of supported encryption algorithms, + * overwriting any previously-added algorithms with the same {@link AeadAlgorithm#getId() id}s. + * + *There may be only one registered {@code AeadAlgorithm} per algorithm {@code id}, and the {@code encAlgs} + * 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}.
+ * + *Finally, the {@link Jwts#ENC JWA standard encryption algorithms} are added last, + * after those in the {@code encAlgs} collection, to ensure that JWA standard algorithms cannot be + * accidentally replaced.
+ * + * @param encAlgs collection of AEAD encryption algorithms to add to the parser's total set of supported + * encryption algorithms. + * @return the builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder addEncryptionAlgorithms(Collection extends AeadAlgorithm> encAlgs); + + /** + * Adds the specified signature algorithms to the parser's total set of supported signature algorithms, + * overwriting any previously-added algorithms with the same + * {@link Identifiable#getId() id}s. + * + *There may be only one registered {@code SecureDigestAlgorithm} per algorithm {@code id}, and the + * {@code sigAlgs} collection is added in iteration order; if a duplicate id is found when iterating the + * {@code sigAlgs} collection, the later element will evict any previously-added algorithm with the same + * {@code id}.
+ * + *Finally, the {@link Jwts#SIG JWA standard signature and MAC algorithms} are + * added last, after those in the {@code sigAlgs} collection, to ensure that JWA standard algorithms + * cannot be accidentally replaced.
+ * + * @param sigAlgs collection of signing algorithms to add to the parser's total set of supported signature + * algorithms. + * @return the builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder addSignatureAlgorithms(Collection extends SecureDigestAlgorithm, ?>> sigAlgs); + + /** + * Adds the specified key management algorithms to the parser's total set of supported key algorithms, + * overwriting any previously-added algorithms with the same {@link KeyAlgorithm#getId() id}s. + * + *There may be only one registered {@code KeyAlgorithm} per algorithm {@code id}, and the {@code keyAlgs} + * 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}.
+ * + *Finally, the {@link StandardKeyAlgorithms#values() JWA standard key management algorithms} + * are added last, after those in the {@code keyAlgs} collection, to ensure that JWA standard algorithms + * cannot be accidentally replaced.
+ * + * @param keyAlgs collection of key management algorithms to add to the parser's total set of supported key + * management algorithms. + * @return the builder for method chaining. + * @since JJWT_RELEASE_VERSION + */ + JwtParserBuilder addKeyAlgorithms(Collection extends KeyAlgorithm, ?>> keyAlgs); + /** * 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. * - *NOTE: Compression is not defined by the JWT Specification, and it is not expected that other libraries - * (including JJWT versions < 0.6.0) are able to consume a compressed JWT body correctly. This method is only - * useful if the compact JWT was compressed with JJWT >= 0.6.0 or another library that you know implements - * the same behavior.
+ *NOTE: Compression is not defined by the JWS Specification - only the JWE Specification - and it is + * not expected that other libraries (including JJWT versions < 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 >= 0.6.0 or + * another library that you know implements the same behavior.
* *Default Support
* @@ -274,15 +524,55 @@ public interface JwtParserBuilder { * and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to * specify a {@code CompressionCodecResolver} in these cases. * - *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 + *
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.
* * @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 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. + * + *NOTE: Compression is not defined by the JWS Specification - only the JWE Specification - and it is + * not expected that other libraries (including JJWT versions < 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 >= 0.6.0 or + * another library that you know implements the same behavior.
+ * + *Simple Registration
+ * + *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.
+ * + *You only need to call this method with a custom locator if compression codec lookup cannot be based on the + * {@code zip} header value.
+ * + *Default Support
+ * + *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.
+ * + *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.
+ * + * @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(LocatorStandard Algorithm References
+ *Standard JSON Web Token algorithms used during JWS or JWE building or parsing are available organized by + * algorithm type. Each organized collection of algorithms is available via a constant to allow + * for easy code-completion in IDEs, showing available algorithm instances. For example, when typing:
+ *+ * * @since 0.1 */ public final class Jwts { + @SuppressWarnings("rawtypes") private static final Class[] MAP_ARG = new Class[]{Map.class}; + /** + * All JWA (RFC 7518) standard Cryptographic + * Algorithms for Content Encryption defined in the + * + * JSON Web Signature and Encryption Algorithms Registry. In addition to its + * {@link Registry#get(Object) get} and {@link Registry#find(Object) find} lookup methods, each standard algorithm + * is also available as a ({@code public final}) constant for direct type-safe reference in application code. + * For example: + *+ * Jwts.// press code-completion hotkeys to suggest available algorithm registry fields + * Jwts.{@link #SIG}.// press hotkeys to suggest individual Digital Signature or MAC algorithms or utility methods + * Jwts.{@link #ENC}.// press hotkeys to suggest individual encryption algorithms or utility methods + * Jwts.{@link #KEY}.// press hotkeys to suggest individual key algorithms or utility methods
+ * + * @since JJWT_RELEASE_VERSION + */ + public static final StandardEncryptionAlgorithms ENC = StandardEncryptionAlgorithms.get(); + + /** + * All JWA (RFC 7518) standard Cryptographic + * Algorithms for Digital Signatures and MACs defined in the + * JSON Web Signature and Encryption Algorithms + * Registry. In addition to its + * {@link Registry#get(Object) get} and {@link Registry#find(Object) find} lookup methods, each standard algorithm + * is also available as a ({@code public final}) constant for direct type-safe reference in application code. + * For example: + *+ * Jwts.builder() + * // ... etc ... + * .encryptWith(aKey, Jwts.ENC.A256GCM) // or A128GCM, A192GCM, etc... + * .build();
+ * + * @since JJWT_RELEASE_VERSION + */ + public static final StandardSecureDigestAlgorithms SIG = StandardSecureDigestAlgorithms.get(); + + /** + * All JWA (RFC 7518) standard Cryptographic + * Algorithms for Key Management. In addition to its + * convenience {@link Registry#get(Object) get} and {@link Registry#find(Object) find} lookup methods, each + * standard algorithm is also available as a ({@code public final}) constant for direct type-safe reference in + * application code. For example: + *+ * Jwts.builder() + * // ... etc ... + * .signWith(aKey, Jwts.SIG.HS512) // or RS512, PS256, EdDSA, etc... + * .build();
+ * + * @since JJWT_RELEASE_VERSION + */ + public static final StandardKeyAlgorithms KEY = StandardKeyAlgorithms.get(); + + /** + * Private constructor, prevent instantiation. + */ private Jwts() { } /** - * Creates a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. As this - * is a less common use of JWTs, consider using the {@link #jwsHeader()} factory method instead if you will later - * digitally sign the JWT. + *+ * Jwts.builder() + * // ... etc ... + * .encryptWith(aKey, Jwts.KEY.ECDH_ES_A256KW, Jwts.ENC.A256GCM) + * .build();
Deprecation Notice: Renamed from {@code header} to {@code unprotectedHeader} since + * JJWT_RELEASE_VERSION and deprecated in favor of {@link #header()} as + * the updated builder-based method supports method chaining and is capable of automatically constructing + * {@link UnprotectedHeader}, {@link JwsHeader}, and {@link JweHeader} automatically based on builder state.
* - * @return a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. + *Previous Documentation
+ *Creates a new {@link UnprotectedHeader} instance suitable for unprotected (not digitally signed or encrypted) + * JWTs. Because {@code Header} extends {@link Map} and map mutation methods cannot support method chaining, + * consider using the more flexible {@link #header()} method instead, which does support method + * chaining and other builder conveniences not available on the {@link UnprotectedHeader} interface.
+ * + * @return a new {@link UnprotectedHeader} instance suitable for unprotected (not digitally signed or + * encrypted) JWTs. + * @see #header() + * @since JJWT_RELEASE_VERSION + * @deprecated since JJWT_RELEASE_VERSION. This method was created to rename the previous {@code header} + * method, but header construction should now use {@link #header()}. This method will be removed in a future + * release before 1.0. */ - public static Header header() { - return Classes.newInstance("io.jsonwebtoken.impl.DefaultHeader"); + @Deprecated + public static UnprotectedHeader unprotectedHeader() { + return Classes.newInstance("io.jsonwebtoken.impl.DefaultUnprotectedHeader"); } /** - * Creates a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs, populated - * with the specified name/value pairs. As this is a less common use of JWTs, consider using the - * {@link #jwsHeader(java.util.Map)} factory method instead if you will later digitally sign the JWT. + *Deprecation Notice: deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as + * the newer method supports method chaining and is capable of automatically constructing + * {@link UnprotectedHeader}, {@link JwsHeader}, and {@link JweHeader} automatically based on builder state.
* - * @param header map of name/value pairs used to create a plaintext (not digitally signed) JWT + *Previous Documentation
+ *Creates a new {@link UnprotectedHeader} instance suitable for unprotected (not digitally signed or encrypted) + * JWTs, populated with the specified name/value pairs. Because {@code Header} extends {@link Map} and map + * mutation methods cannot support method chaining, consider using the more flexible {@link #header()} + * method instead, which does support method chaining and other builder conveniences not available on the + * {@link UnprotectedHeader} interface.
+ * + * @param header map of name/value pairs used to create an unprotected (not digitally signed or encrypted) JWT * {@code Header} instance. - * @return a new {@link Header} instance suitable for plaintext (not digitally signed) JWTs. + * @return a new {@link UnprotectedHeader} instance suitable for unprotected (not digitally signed or encrypted) + * JWTs. + * @see #header() + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as the builder supports + * method chaining and is more flexible and powerful. This method will be removed in a future release before 1.0. */ - public static Header header(MapDeprecation Notice: deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as + * the newer method supports method chaining and is capable of automatically constructing + * {@link UnprotectedHeader}, {@link JwsHeader}, and {@link JweHeader} automatically based on builder state.
+ * + *Previous Documentation
+ *Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's). Because {@code Header} + * extends {@link Map} and map mutation methods cannot support method chaining, consider using the + * more flexible {@link #header()} method instead, which does support method chaining, as well as other + * convenience builder methods not available via the {@link JwsHeader} interface.
* * @return a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's). + * @see #header() * @see JwtBuilder#setHeader(Header) + * @see JwtBuilder#setHeader(Builder) + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as the builder supports + * method chaining and is more flexible and powerful. This method will be removed in a future release before 1.0. */ + @Deprecated public static JwsHeader jwsHeader() { return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwsHeader"); } /** - * Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the - * specified name/value pairs. + *Deprecation Notice: deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as + * the newer method supports method chaining and is capable of automatically constructing + * {@link UnprotectedHeader}, {@link JwsHeader}, and {@link JweHeader} automatically based on builder state.
+ * + *Previous Documentation
+ *Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the + * specified name/value pairs. Because {@code Header} extends {@link Map} and map mutation methods cannot + * support method chaining, consider using the more flexible {@link #header()} method instead, + * which does support method chaining and other builder conveniences not available on the + * {@link JwsHeader} interface directly.
* * @param header map of name/value pairs used to create a new {@link JwsHeader} instance. * @return a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the * specified name/value pairs. + * @see #header() * @see JwtBuilder#setHeader(Header) + * @see JwtBuilder#setHeader(Builder) + * @deprecated since JJWT_RELEASE_VERSION in favor of {@link #header()} as the builder supports + * method chaining and is more flexible and powerful. This method will be removed in a future release before 1.0. */ + @Deprecated public static JwsHeader jwsHeader(MapFor example, a {@code Locator} implementation can inspect a header's {@code kid} (Key ID) parameter, and use the + * discovered {@code kid} value to lookup and return the associated {@link Key} instance. JJWT could then use this + * {@code key} to decrypt a JWE or verify a JWS signature.
+ * + * @paramThe keyId header parameter is a hint indicating which key was used to secure a JWS or JWE. This + * parameter allows originators to explicitly signal a change of key to recipients. The structure of the keyId + * value is unspecified. Its value is a CaSe-SeNsItIvE string.
+ * + *When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.
+ * + * @return the case-sensitive {@code kid} header value or {@code null} if not present. + * @see JWS Key ID + * @see JWE Key ID + */ + String getKeyId(); + + /** + * Returns the header parameter names that use extensions to the JWT or JWA specification that MUST + * be understood and supported by the JWT recipient, or {@code null} if not present. + * + * @return the header parameter names that use extensions to the JWT or JWA specification that MUST + * be understood and supported by the JWT recipient, or {@code null} if not present. + * @see JWS {@code crit} (Critical) Header Parameter + * @see JWS {@code crit} (Critical) Header Parameter + */ + Setjwk
(JSON Web Key) Header Parameter
+ * @see JWE jwk
(JSON Web Key) Header Parameter
+ */
+ T setJwk(PublicJwk> jwk);
+
+ /**
+ * Sets the JWT case-sensitive {@code kid} (Key ID) header value. A {@code null} value will remove the property
+ * from the JSON map.
+ *
+ * The keyId header parameter is a hint indicating which key was used to secure a JWS or JWE. This parameter + * allows originators to explicitly signal a change of key to recipients. The structure of the keyId value is + * unspecified. Its value MUST be a case-sensitive string.
+ * + *When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.
+ * + * @param kid the case-sensitive JWS {@code kid} header value or {@code null} to remove the property from the JSON map. + * @return the header instance for method chaining. + * @see JWS Key ID + * @see JWE Key ID + */ + T setKeyId(String kid); + + /** + * Sets the header parameter names that use extensions to the JWT or JWA specification that MUST + * be understood and supported by the JWT recipient. A {@code null} value will remove the + * property from the JSON map. + * + * @param crit the header parameter names that use extensions to the JWT or JWA specification that MUST + * be understood and supported by the JWT recipient. + * @return the header for method chaining. + * @see JWScrit
(Critical) Header Parameter
+ * @see JWS crit
(Critical) Header Parameter
+ */
+ T setCritical(SetA {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing the JWT and the - * JWT header or payload (plaintext body or Claims) must be inspected first to determine how to look up the signing key. + * JWT header or payload (byte array or Claims) must be inspected first to determine how to look up the signing key. * Once returned by the resolver, the JwtParser will then verify the JWS signature with the returned key. For * example:
* @@ -40,13 +40,15 @@ import java.security.Key; * *If you only need to resolve a signing key for a particular JWS (either a plaintext or Claims JWS), consider using + *
If you only need to resolve a signing key for a particular JWS (either a content or Claims JWS), consider using * the {@link io.jsonwebtoken.SigningKeyResolverAdapter} and overriding only the method you need to support instead of * implementing this interface directly.
* - * @see io.jsonwebtoken.SigningKeyResolverAdapter + * @see io.jsonwebtoken.JwtParserBuilder#setKeyLocator(Locator) * @since 0.4 + * @deprecated since JJWT_RELEASE_VERSION. Implement {@link Locator} instead. */ +@Deprecated public interface SigningKeyResolver { /** @@ -54,20 +56,20 @@ public interface SigningKeyResolver { * header and claims. * * @param header the header of the JWS to validate - * @param claims the claims (body) of the JWS to validate + * @param claims the Claims payload of the JWS to validate * @return the signing key that should be used to validate a digital signature for the Claims JWS with the specified * header and claims. */ Key resolveSigningKey(JwsHeader header, Claims claims); /** - * Returns the signing key that should be used to validate a digital signature for the Plaintext JWS with the - * specified header and plaintext payload. + * Returns the signing key that should be used to validate a digital signature for the content JWS with the + * specified header and byte array payload. * - * @param header the header of the JWS to validate - * @param plaintext the plaintext body of the JWS to validate - * @return the signing key that should be used to validate a digital signature for the Plaintext JWS with the - * specified header and plaintext payload. + * @param header the header of the JWS to validate + * @param content the byte array payload of the JWS to validate + * @return the signing key that should be used to validate a digital signature for the content JWS with the + * specified header and byte array payload. */ - Key resolveSigningKey(JwsHeader header, String plaintext); + Key resolveSigningKey(JwsHeader header, byte[] content); } diff --git a/api/src/main/java/io/jsonwebtoken/SigningKeyResolverAdapter.java b/api/src/main/java/io/jsonwebtoken/SigningKeyResolverAdapter.java index 1be7ec55..c3af881f 100644 --- a/api/src/main/java/io/jsonwebtoken/SigningKeyResolverAdapter.java +++ b/api/src/main/java/io/jsonwebtoken/SigningKeyResolverAdapter.java @@ -21,44 +21,67 @@ import javax.crypto.spec.SecretKeySpec; import java.security.Key; /** - * An Adapter implementation of the - * {@link SigningKeyResolver} interface that allows subclasses to process only the type of JWS body that - * is known/expected for a particular case. + *The {@link #resolveSigningKey(JwsHeader, Claims)} and {@link #resolveSigningKey(JwsHeader, String)} method + *
As of JJWT JJWT_RELEASE_VERSION, various Resolver concepts (including the {@code SigningKeyResolver}) have been + * unified into a single {@link Locator} interface. For key location, (for both signing and encryption keys), + * use the {@link JwtParserBuilder#setKeyLocator(Locator)} to configure a parser with your desired Key locator instead + * of using a {@code SigningKeyResolver}. Also see {@link LocatorAdapter} for the Adapter pattern parallel of this + * class. This {@code SigningKeyResolverAdapter} class will be removed before the 1.0 release.
+ * + *Previous Documentation
+ * + *An Adapter implementation of the + * {@link SigningKeyResolver} interface that allows subclasses to process only the type of JWS body that + * is known/expected for a particular case.
+ * + *The {@link #resolveSigningKey(JwsHeader, Claims)} and {@link #resolveSigningKey(JwsHeader, byte[])} method * implementations delegate to the - * {@link #resolveSigningKeyBytes(JwsHeader, Claims)} and {@link #resolveSigningKeyBytes(JwsHeader, String)} methods + * {@link #resolveSigningKeyBytes(JwsHeader, Claims)} and {@link #resolveSigningKeyBytes(JwsHeader, byte[])} methods * respectively. The latter two methods simply throw exceptions: they represent scenarios expected by * calling code in known situations, and it is expected that you override the implementation in those known situations; * non-overridden *KeyBytes methods indicates that the JWS input was unexpected.
* - *If either {@link #resolveSigningKey(JwsHeader, String)} or {@link #resolveSigningKey(JwsHeader, Claims)} + *
If either {@link #resolveSigningKey(JwsHeader, byte[])} or {@link #resolveSigningKey(JwsHeader, Claims)} * are not overridden, one (or both) of the *KeyBytes variants must be overridden depending on your expected * use case. You do not have to override any method that does not represent an expected condition.
* + * @see io.jsonwebtoken.JwtParserBuilder#setKeyLocator(Locator) + * @see LocatorAdapter * @since 0.4 + * @deprecated since JJWT_RELEASE_VERSION. Use {@link LocatorAdapter LocatorAdapter} with + * {@link JwtParserBuilder#setKeyLocator(Locator)} */ +@SuppressWarnings("DeprecatedIsStillUsed") +@Deprecated public class SigningKeyResolverAdapter implements SigningKeyResolver { + /** + * Default constructor. + */ + public SigningKeyResolverAdapter() { + + } + @Override public Key resolveSigningKey(JwsHeader header, Claims claims) { SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm()); - Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, Claims) implementation cannot be " + - "used for asymmetric key algorithms (RSA, Elliptic Curve). " + - "Override the resolveSigningKey(JwsHeader, Claims) method instead and return a " + - "Key instance appropriate for the " + alg.name() + " algorithm."); + Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, Claims) implementation cannot " + + "be used for asymmetric key algorithms (RSA, Elliptic Curve). " + + "Override the resolveSigningKey(JwsHeader, Claims) method instead and return a " + + "Key instance appropriate for the " + alg.name() + " algorithm."); byte[] keyBytes = resolveSigningKeyBytes(header, claims); return new SecretKeySpec(keyBytes, alg.getJcaName()); } @Override - public Key resolveSigningKey(JwsHeader header, String plaintext) { + public Key resolveSigningKey(JwsHeader header, byte[] content) { SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm()); - Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, String) implementation cannot be " + - "used for asymmetric key algorithms (RSA, Elliptic Curve). " + - "Override the resolveSigningKey(JwsHeader, String) method instead and return a " + - "Key instance appropriate for the " + alg.name() + " algorithm."); - byte[] keyBytes = resolveSigningKeyBytes(header, plaintext); + Assert.isTrue(alg.isHmac(), "The default resolveSigningKey(JwsHeader, byte[]) implementation cannot " + + "be used for asymmetric key algorithms (RSA, Elliptic Curve). " + + "Override the resolveSigningKey(JwsHeader, byte[]) method instead and return a " + + "Key instance appropriate for the " + alg.name() + " algorithm."); + byte[] keyBytes = resolveSigningKeyBytes(header, content); return new SecretKeySpec(keyBytes, alg.getJcaName()); } @@ -76,24 +99,25 @@ public class SigningKeyResolverAdapter implements SigningKeyResolver { */ public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { throw new UnsupportedJwtException("The specified SigningKeyResolver implementation does not support " + - "Claims JWS signing key resolution. Consider overriding either the " + - "resolveSigningKey(JwsHeader, Claims) method or, for HMAC algorithms, the " + - "resolveSigningKeyBytes(JwsHeader, Claims) method."); + "Claims JWS signing key resolution. Consider overriding either the " + + "resolveSigningKey(JwsHeader, Claims) method or, for HMAC algorithms, the " + + "resolveSigningKeyBytes(JwsHeader, Claims) method."); } /** - * Convenience method invoked by {@link #resolveSigningKey(JwsHeader, String)} that obtains the necessary signing - * key bytes. This implementation simply throws an exception: if the JWS parsed is a plaintext JWS, you must - * override this method or the {@link #resolveSigningKey(JwsHeader, String)} method instead. + * Convenience method invoked by {@link #resolveSigningKey(JwsHeader, byte[])} that obtains the necessary signing + * key bytes. This implementation simply throws an exception: if the JWS parsed is a content JWS, you must + * override this method or the {@link #resolveSigningKey(JwsHeader, byte[])} method instead. * - * @param header the parsed {@link JwsHeader} - * @param payload the parsed String plaintext payload + * @param header the parsed {@link JwsHeader} + * @param content the byte array payload * @return the signing key bytes to use to verify the JWS signature. */ - public byte[] resolveSigningKeyBytes(JwsHeader header, String payload) { + @SuppressWarnings("unused") + public byte[] resolveSigningKeyBytes(JwsHeader header, byte[] content) { throw new UnsupportedJwtException("The specified SigningKeyResolver implementation does not support " + - "plaintext JWS signing key resolution. Consider overriding either the " + - "resolveSigningKey(JwsHeader, String) method or, for HMAC algorithms, the " + - "resolveSigningKeyBytes(JwsHeader, String) method."); + "content JWS signing key resolution. Consider overriding either the " + + "resolveSigningKey(JwsHeader, byte[]) method or, for HMAC algorithms, the " + + "resolveSigningKeyBytes(JwsHeader, byte[]) method."); } } diff --git a/api/src/main/java/io/jsonwebtoken/UnprotectedHeader.java b/api/src/main/java/io/jsonwebtoken/UnprotectedHeader.java new file mode 100644 index 00000000..3aef82c8 --- /dev/null +++ b/api/src/main/java/io/jsonwebtoken/UnprotectedHeader.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * 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}. + * + * @since JJWT_RELEASE_VERSION + */ +public interface UnprotectedHeader extends HeaderFor example, this exception would be thrown if parsing an unsigned plaintext JWT when the application + *
For example, this exception would be thrown if parsing an unprotected content JWT when the application * requires a cryptographically signed Claims JWS instead.
* * @since 0.2 */ public class UnsupportedJwtException extends JwtException { + /** + * Creates a new instance with the specified explanation message. + * + * @param message the message explaining why the exception is thrown. + */ public UnsupportedJwtException(String message) { super(message); } + /** + * Creates a new instance with the specified explanation message and underlying cause. + * + * @param message the message explaining why the exception is thrown. + * @param cause the underlying cause that resulted in this exception being thrown. + */ public UnsupportedJwtException(String message, Throwable cause) { super(message, cause); } diff --git a/api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java b/api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java index 3c8bc817..fa76d3aa 100644 --- a/api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java +++ b/api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java @@ -18,6 +18,9 @@ package io.jsonwebtoken.io; import io.jsonwebtoken.lang.Assert; /** + * Very fast Base64 decoder guaranteed to + * work in all >= Java 7 JDK and Android environments. + * * @since 0.10.0 */ class Base64Decoder extends Base64Support implements DecoderIllegalArgumentException
* if the test result is false
.
* Assert.isTrue(i > 0, "The value must be greater than zero");+ * * @param expression a boolean expression - * @param message the exception message to use if the assertion fails + * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if expression is
false
*/
public static void isTrue(boolean expression, String message) {
@@ -40,6 +46,7 @@ public final class Assert {
* Assert a boolean expression, throwing IllegalArgumentException
* if the test result is false
.
* Assert.isTrue(i > 0);+ * * @param expression a boolean expression * @throws IllegalArgumentException if expression is
false
*/
@@ -50,7 +57,8 @@ public final class Assert {
/**
* Assert that an object is null
.
* Assert.isNull(value, "The value must be null");- * @param object the object to check + * + * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is not
null
*/
@@ -63,6 +71,7 @@ public final class Assert {
/**
* Assert that an object is null
.
* Assert.isNull(value);+ * * @param object the object to check * @throws IllegalArgumentException if the object is not
null
*/
@@ -73,19 +82,24 @@ public final class Assert {
/**
* Assert that an object is not null
.
* Assert.notNull(clazz, "The class must not be null");- * @param object the object to check + * + * @param object the object to check + * @param
null
*/
- public static void notNull(Object object, String message) {
+ public static null
.
* Assert.notNull(clazz);+ * * @param object the object to check * @throws IllegalArgumentException if the object is
null
*/
@@ -97,7 +111,8 @@ public final class Assert {
* Assert that the given String is not empty; that is,
* it must not be null
and not the empty String.
* Assert.hasLength(name, "Name must not be empty");- * @param text the String to check + * + * @param text the String to check * @param message the exception message to use if the assertion fails * @see Strings#hasLength */ @@ -111,32 +126,37 @@ public final class Assert { * Assert that the given String is not empty; that is, * it must not be
null
and not the empty String.
* Assert.hasLength(name);+ * * @param text the String to check * @see Strings#hasLength */ public static void hasLength(String text) { hasLength(text, - "[Assertion failed] - this String argument must have length; it must not be null or empty"); + "[Assertion failed] - this String argument must have length; it must not be null or empty"); } /** * Assert that the given String has valid text content; that is, it must not * be
null
and must contain at least one non-whitespace character.
* Assert.hasText(name, "'name' must not be empty");- * @param text the String to check + * + * @param text the String to check * @param message the exception message to use if the assertion fails + * @return the string if it has text * @see Strings#hasText */ - public static void hasText(String text, String message) { + public static String hasText(String text, String message) { if (!Strings.hasText(text)) { throw new IllegalArgumentException(message); } + return text; } /** * Assert that the given String has valid text content; that is, it must not * be
null
and must contain at least one non-whitespace character.
* Assert.hasText(name, "'name' must not be empty");+ * * @param text the String to check * @see Strings#hasText */ @@ -148,13 +168,14 @@ public final class Assert { /** * Assert that the given text does not contain the given substring. *
Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");+ * * @param textToSearch the text to search - * @param substring the substring to find within the text - * @param message the exception message to use if the assertion fails + * @param substring the substring to find within the text + * @param message the exception message to use if the assertion fails */ public static void doesNotContain(String textToSearch, String substring, String message) { if (Strings.hasLength(textToSearch) && Strings.hasLength(substring) && - textToSearch.indexOf(substring) != -1) { + textToSearch.indexOf(substring) != -1) { throw new IllegalArgumentException(message); } } @@ -162,12 +183,13 @@ public final class Assert { /** * Assert that the given text does not contain the given substring. *
Assert.doesNotContain(name, "rod");+ * * @param textToSearch the text to search - * @param substring the substring to find within the text + * @param substring the substring to find within the text */ public static void doesNotContain(String textToSearch, String substring) { doesNotContain(textToSearch, substring, - "[Assertion failed] - this String argument must not contain the substring [" + substring + "]"); + "[Assertion failed] - this String argument must not contain the substring [" + substring + "]"); } @@ -175,20 +197,24 @@ public final class Assert { * Assert that an array has elements; that is, it must not be *
null
and must have at least one element.
* Assert.notEmpty(array, "The array must have elements");- * @param array the array to check + * + * @param array the array to check * @param message the exception message to use if the assertion fails + * @return the non-empty array for immediate use * @throws IllegalArgumentException if the object array is
null
or has no elements
*/
- public static void notEmpty(Object[] array, String message) {
+ public static Object[] notEmpty(Object[] array, String message) {
if (Objects.isEmpty(array)) {
throw new IllegalArgumentException(message);
}
+ return array;
}
/**
* Assert that an array has elements; that is, it must not be
* null
and must have at least one element.
* Assert.notEmpty(array);+ * * @param array the array to check * @throws IllegalArgumentException if the object array is
null
or has no elements
*/
@@ -196,17 +222,44 @@ public final class Assert {
notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
}
- public static void notEmpty(byte[] array, String msg) {
+ /**
+ * Assert that the specified byte array is not null and has at least one byte element.
+ *
+ * @param array the byte array to check
+ * @param msg the exception message to use if the assertion fails
+ * @return the byte array if the assertion passes
+ * @throws IllegalArgumentException if the byte array is null or empty
+ * @since JJWT_RELEASE_VERSION
+ */
+ public static byte[] notEmpty(byte[] array, String msg) {
if (Objects.isEmpty(array)) {
throw new IllegalArgumentException(msg);
}
+ return array;
+ }
+
+ /**
+ * Assert that the specified character array is not null and has at least one byte element.
+ *
+ * @param chars the character array to check
+ * @param msg the exception message to use if the assertion fails
+ * @return the character array if the assertion passes
+ * @throws IllegalArgumentException if the character array is null or empty
+ * @since JJWT_RELEASE_VERSION
+ */
+ public static char[] notEmpty(char[] chars, String msg) {
+ if (Objects.isEmpty(chars)) {
+ throw new IllegalArgumentException(msg);
+ }
+ return chars;
}
/**
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* Assert.noNullElements(array, "The array must have non-null elements");- * @param array the array to check + * + * @param array the array to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object array contains a
null
element
*/
@@ -224,6 +277,7 @@ public final class Assert {
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* Assert.noNullElements(array);+ * * @param array the array to check * @throws IllegalArgumentException if the object array contains a
null
element
*/
@@ -235,46 +289,56 @@ public final class Assert {
* Assert that a collection has elements; that is, it must not be
* null
and must have at least one element.
* Assert.notEmpty(collection, "Collection must have elements");+ * * @param collection the collection to check - * @param message the exception message to use if the assertion fails + * @param
null
or has no elements
*/
- public static void notEmpty(Collection collection, String message) {
+ public static null
and must have at least one element.
* Assert.notEmpty(collection, "Collection must have elements");+ * * @param collection the collection to check * @throws IllegalArgumentException if the collection is
null
or has no elements
*/
- public static void notEmpty(Collection collection) {
+ public static void notEmpty(Collection> collection) {
notEmpty(collection,
- "[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
+ "[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
}
/**
* Assert that a Map has entries; that is, it must not be null
* and must have at least one entry.
* Assert.notEmpty(map, "Map must have entries");- * @param map the map to check + * + * @param map the map to check + * @param
null
or has no entries
*/
- public static void notEmpty(Map map, String message) {
+ public static null
* and must have at least one entry.
* Assert.notEmpty(map);+ * * @param map the map to check * @throws IllegalArgumentException if the map is
null
or has no entries
*/
@@ -286,41 +350,49 @@ public final class Assert {
/**
* Assert that the provided object is an instance of the provided class.
* Assert.instanceOf(Foo.class, foo);+ * + * @param
Assert.instanceOf(Foo.class, foo);- * @param type the type to check against - * @param obj the object to check + * + * @param type the type to check against + * @param
superType.isAssignableFrom(subType)
is true
.
* Assert.isAssignable(Number.class, myClass);+ * * @param superType the super type to check - * @param subType the sub type to check + * @param subType the sub type to check * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType) { @@ -330,12 +402,13 @@ public final class Assert { /** * Assert that
superType.isAssignableFrom(subType)
is true
.
* Assert.isAssignable(Number.class, myClass);+ * * @param superType the super type to check against - * @param subType the sub type to check - * @param message a message which will be prepended to the message produced by - * the function itself, and which may be used to provide context. It should - * normally end in a ": " or ". " so that the function generate message looks - * ok when prepended to it. + * @param subType the sub type to check + * @param message a message which will be prepended to the message produced by + * the function itself, and which may be used to provide context. It should + * normally end in a ": " or ". " so that the function generate message looks + * ok when prepended to it. * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType, String message) { @@ -345,14 +418,54 @@ public final class Assert { } } + /** + * Asserts that a specified {@code value} is equal to the given {@code requirement}, throwing + * an {@link IllegalArgumentException} with the given message if not. + * + * @param
IllegalStateException
* if the test result is false
. Call isTrue if you wish to
* throw IllegalArgumentException on an assertion failure.
* Assert.state(id == null, "The id property must not already be initialized");+ * * @param expression a boolean expression - * @param message the exception message to use if the assertion fails + * @param message the exception message to use if the assertion fails * @throws IllegalStateException if expression is
false
*/
public static void state(boolean expression, String message) {
@@ -367,6 +480,7 @@ public final class Assert {
* Call {@link #isTrue(boolean)} if you wish to * throw {@link IllegalArgumentException} on an assertion failure. *
Assert.state(id == null);+ * * @param expression a boolean expression * @throws IllegalStateException if the supplied expression is
false
*/
@@ -374,4 +488,21 @@ public final class Assert {
state(expression, "[Assertion failed] - this state invariant must be true");
}
+ /**
+ * Asserts that the specified {@code value} is not null, otherwise throws an
+ * {@link IllegalStateException} with the specified {@code msg}. Intended to be used with
+ * code invariants (as opposed to method arguments, like {@link #notNull(Object)}).
+ *
+ * @param value value to assert is not null
+ * @param msg exception message to use if {@code value} is null
+ * @throws IllegalStateException with the specified {@code msg} if {@code value} is null.
+ * @since JJWT_RELEASE_VERSION
+ */
+ public static ClassNotFoundException
.
*
* @param fqcn the fully qualified class name to load
- * @param true
if the supplied Collection is null
* or empty. Otherwise, return false
.
+ *
* @param collection the Collection to check
* @return whether the given Collection is empty
*/
- public static boolean isEmpty(Collection collection) {
- return (collection == null || collection.isEmpty());
+ public static boolean isEmpty(Collection> collection) {
+ return size(collection) == 0;
}
/**
@@ -45,7 +178,7 @@ public final class Collections {
* @return the collection's size or {@code 0} if the collection is {@code null}.
* @since 0.9.2
*/
- public static int size(Collection collection) {
+ public static int size(Collection> collection) {
return collection == null ? 0 : collection.size();
}
@@ -56,18 +189,19 @@ public final class Collections {
* @return the map's size or {@code 0} if the map is {@code null}.
* @since 0.9.2
*/
- public static int size(Map map) {
+ public static int size(Map, ?> map) {
return map == null ? 0 : map.size();
}
/**
* Return true
if the supplied Map is null
* or empty. Otherwise, return false
.
+ *
* @param map the Map to check
* @return whether the given Map is empty
*/
- public static boolean isEmpty(Map map) {
- return (map == null || map.isEmpty());
+ public static boolean isEmpty(Map, ?> map) {
+ return size(map) == 0;
}
/**
@@ -75,6 +209,7 @@ public final class Collections {
* converted into a List of the appropriate wrapper type.
* A Uses Enforces the given instance to be present, rather than returning
* null
source value will be converted to an
* empty List.
+ *
* @param source the (potentially primitive) array
* @return the converted List result
* @see Objects#toObjectArray(Object)
@@ -83,9 +218,28 @@ public final class Collections {
return Arrays.asList(Objects.toObjectArray(source));
}
+ /**
+ * Concatenate the specified set with the specified array elements, resulting in a new {@link LinkedHashSet} with
+ * the array elements appended to the end of the existing Set.
+ *
+ * @param c the set to append to
+ * @param elements the array elements to append to the end of the set
+ * @param null
)
+ *
+ * @param array the array to merge (may be null
)
* @param collection the target Collection to merge the array into
*/
@SuppressWarnings("unchecked")
@@ -94,9 +248,7 @@ public final class Collections {
throw new IllegalArgumentException("Collection must not be null");
}
Object[] arr = Objects.toObjectArray(array);
- for (Object elem : arr) {
- collection.add(elem);
- }
+ java.util.Collections.addAll(collection, arr);
}
/**
@@ -104,8 +256,9 @@ public final class Collections {
* copying all properties (key-value pairs) over.
* Properties.propertyNames()
to even catch
* default properties linked into the original Properties instance.
+ *
* @param props the Properties instance to merge (may be null
)
- * @param map the target Map to merge the properties into
+ * @param map the target Map to merge the properties into
*/
@SuppressWarnings("unchecked")
public static void mergePropertiesIntoMap(Properties props, Map map) {
@@ -113,7 +266,7 @@ public final class Collections {
throw new IllegalArgumentException("Map must not be null");
}
if (props != null) {
- for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
+ for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {
String key = (String) en.nextElement();
Object value = props.getProperty(key);
if (value == null) {
@@ -128,8 +281,9 @@ public final class Collections {
/**
* Check whether the given Iterator contains the given element.
+ *
* @param iterator the Iterator to check
- * @param element the element to look for
+ * @param element the element to look for
* @return true
if found, false
else
*/
public static boolean contains(Iterator iterator, Object element) {
@@ -146,8 +300,9 @@ public final class Collections {
/**
* Check whether the given Enumeration contains the given element.
+ *
* @param enumeration the Enumeration to check
- * @param element the element to look for
+ * @param element the element to look for
* @return true
if found, false
else
*/
public static boolean contains(Enumeration enumeration, Object element) {
@@ -166,8 +321,9 @@ public final class Collections {
* Check whether the given Collection contains the given element instance.
* true
for an equal element as well.
+ *
* @param collection the Collection to check
- * @param element the element to look for
+ * @param element the element to look for
* @return true
if found, false
else
*/
public static boolean containsInstance(Collection collection, Object element) {
@@ -184,7 +340,8 @@ public final class Collections {
/**
* Return true
if any element in 'candidates
' is
* contained in 'source
'; otherwise returns false
.
- * @param source the source Collection
+ *
+ * @param source the source Collection
* @param candidates the candidates to search for
* @return whether any of the candidates has been found
*/
@@ -205,7 +362,8 @@ public final class Collections {
* 'source
'. If no element in 'candidates
' is present in
* 'source
' returns null
. Iteration order is
* {@link Collection} implementation specific.
- * @param source the source Collection
+ *
+ * @param source the source Collection
* @param candidates the candidates to search for
* @return the first present object, or null
if not found
*/
@@ -223,9 +381,10 @@ public final class Collections {
/**
* Find a single value of the given type in the given Collection.
+ *
* @param collection the Collection to search
- * @param type the type to look for
- * @param null
if none or more than one such value found
*/
@@ -251,8 +410,9 @@ public final class Collections {
* Find a single value of one of the given types in the given Collection:
* searching the Collection for a value of the first type, then
* searching for a value of the second type, etc.
+ *
* @param collection the collection to search
- * @param types the types to look for, in prioritized order
+ * @param types the types to look for, in prioritized order
* @return a value of one of the given types found if there is a clear match,
* or null
if none or more than one such value found
*/
@@ -271,6 +431,7 @@ public final class Collections {
/**
* Determine whether the given Collection only contains a single unique object.
+ *
* @param collection the Collection to check
* @return true
if the collection contains a single reference or
* multiple references to the same instance, false
else
@@ -285,8 +446,7 @@ public final class Collections {
if (!hasCandidate) {
hasCandidate = true;
candidate = elem;
- }
- else if (candidate != elem) {
+ } else if (candidate != elem) {
return false;
}
}
@@ -295,6 +455,7 @@ public final class Collections {
/**
* Find the common element type of the given Collection, if any.
+ *
* @param collection the Collection to check
* @return the common element type, or null
if no clear
* common type has been found (or the collection was empty)
@@ -308,8 +469,7 @@ public final class Collections {
if (val != null) {
if (candidate == null) {
candidate = val.getClass();
- }
- else if (candidate != val.getClass()) {
+ } else if (candidate != val.getClass()) {
return null;
}
}
@@ -321,14 +481,15 @@ public final class Collections {
* Marshal the elements from the given enumeration into an array of the given type.
* Enumeration elements must be assignable to the type of the given array. The array
* returned will be a different instance than the array given.
+ *
* @param enumeration the collection to convert to an array
- * @param array an array instance that matches the type of array to return
- * @param the element type of the array that will be created
- * @param