Revert "update readme.md: installation in gradle 7+"

This reverts commit 371577df98.
This commit is contained in:
minho 2021-05-31 11:32:47 +09:00 committed by Brian Demers
parent 915a3753c4
commit 861ec66832
1 changed files with 426 additions and 497 deletions

365
README.md
View File

@ -23,121 +23,117 @@ enforcement.
## Table of Contents ## Table of Contents
- [Features](#features) * [Features](#features)
- [Currently Unsupported Features](#features-unsupported) * [Currently Unsupported Features](#features-unsupported)
- [Community](#community) * [Community](#community)
- [Getting Help](#help) * [Getting Help](#help)
- [Questions](#help-questions) * [Questions](#help-questions)
- [Bugs and Feature Requests](#help-issues) * [Bugs and Feature Requests](#help-issues)
- [Contributing](#contributing) * [Contributing](#contributing)
- [Pull Requests](#contributing-pull-requests) * [Pull Requests](#contributing-pull-requests)
- [Help Wanted](#contributing-help-wanted) * [Help Wanted](#contributing-help-wanted)
- [What is a JSON Web Token?](#overview) * [What is a JSON Web Token?](#overview)
- [Installation](#install) * [Installation](#install)
- [JDK Projects](#install-jdk) * [JDK Projects](#install-jdk)
- [Maven](#install-jdk-maven) * [Maven](#install-jdk-maven)
- [Gradle](#install-jdk-gradle) * [Gradle](#install-jdk-gradle)
- [Android Projects](#install-android) * [Android Projects](#install-android)
- [Dependencies](#install-android-dependencies) * [Dependencies](#install-android-dependencies)
- [Proguard Exclusions](#install-android-proguard) * [Proguard Exclusions](#install-android-proguard)
- [Understanding JJWT Dependencies](#install-understandingdependencies) * [Understanding JJWT Dependencies](#install-understandingdependencies)
- [Quickstart](#quickstart) * [Quickstart](#quickstart)
- [Signed JWTs](#jws) * [Signed JWTs](#jws)
- [Signature Algorithm Keys](#jws-key) * [Signature Algorithm Keys](#jws-key)
- [HMAC-SHA](#jws-key-hmacsha) * [HMAC-SHA](#jws-key-hmacsha)
- [RSA](#jws-key-rsa) * [RSA](#jws-key-rsa)
- [Elliptic Curve](#jws-key-ecdsa) * [Elliptic Curve](#jws-key-ecdsa)
- [Creating Safe Keys](#jws-key-create) * [Creating Safe Keys](#jws-key-create)
- [Secret Keys](#jws-key-create-secret) * [Secret Keys](#jws-key-create-secret)
- [Asymetric Keys](#jws-key-create-asym) * [Asymetric Keys](#jws-key-create-asym)
- [Create a JWS](#jws-create) * [Create a JWS](#jws-create)
- [Header](#jws-create-header) * [Header](#jws-create-header)
- [Instance](#jws-create-header-instance) * [Instance](#jws-create-header-instance)
- [Map](#jws-create-header-map) * [Map](#jws-create-header-map)
- [Claims](#jws-create-claims) * [Claims](#jws-create-claims)
- [Standard Claims](#jws-create-claims-standard) * [Standard Claims](#jws-create-claims-standard)
- [Custom Claims](#jws-create-claims-custom) * [Custom Claims](#jws-create-claims-custom)
- [Claims Instance](#jws-create-claims-instance) * [Claims Instance](#jws-create-claims-instance)
- [Claims Map](#jws-create-claims-map) * [Claims Map](#jws-create-claims-map)
- [Signing Key](#jws-create-key) * [Signing Key](#jws-create-key)
- [SecretKey Formats](#jws-create-key-secret) * [SecretKey Formats](#jws-create-key-secret)
- [Signature Algorithm Override](#jws-create-key-algoverride) * [Signature Algorithm Override](#jws-create-key-algoverride)
- [Compression](#jws-create-compression) * [Compression](#jws-create-compression)
- [Read a JWS](#jws-read) * [Read a JWS](#jws-read)
- [Verification Key](#jws-read-key) * [Verification Key](#jws-read-key)
- [Find the Verification Key at Runtime](#jws-read-key-resolver) * [Find the Verification Key at Runtime](#jws-read-key-resolver)
- [Claims Assertions](#jws-read-claims) * [Claims Assertions](#jws-read-claims)
- [Accounting for Clock Skew](#jws-read-clock) * [Accounting for Clock Skew](#jws-read-clock)
- [Custom Clock](#jws-read-clock-custom) * [Custom Clock](#jws-read-clock-custom)
- [Decompression](#jws-read-decompression) * [Decompression](#jws-read-decompression)
<!-- * [Error Handling](#jws-read-errors) --> <!-- * [Error Handling](#jws-read-errors) -->
- [Compression](#compression) * [Compression](#compression)
- [Custom Compression Codec](#compression-custom) * [Custom Compression Codec](#compression-custom)
- [JSON Processor](#json) * [JSON Processor](#json)
- [Custom JSON Processor](#json-custom) * [Custom JSON Processor](#json-custom)
- [Jackson ObjectMapper](#json-jackson) * [Jackson ObjectMapper](#json-jackson)
- [Custom Claim Types](#json-jackson-custom-types) * [Custom Claim Types](#json-jackson-custom-types)
- [Gson](#json-gson) * [Gson](#json-gson)
- [Base64 Support](#base64) * [Base64 Support](#base64)
- [Base64 in Security Contexts](#base64-security) * [Base64 in Security Contexts](#base64-security)
- [Base64 is not Encryption](#base64-not-encryption) * [Base64 is not Encryption](#base64-not-encryption)
- [Changing Base64 Characters](#base64-changing-characters) * [Changing Base64 Characters](#base64-changing-characters)
- [Custom Base64 Codec](#base64-custom) * [Custom Base64 Codec](#base64-custom)
<a name="features"></a> <a name="features"></a>
## Features ## Features
- Fully functional on all JDKs and Android * Fully functional on all JDKs and Android
- Automatic security best practices and assertions * Automatic security best practices and assertions
- Easy to learn and read API * Easy to learn and read API
- Convenient and readable [fluent](http://en.wikipedia.org/wiki/Fluent_interface) interfaces, great for IDE auto-completion to write code quickly * Convenient and readable [fluent](http://en.wikipedia.org/wiki/Fluent_interface) interfaces, great for IDE auto-completion to write code quickly
- Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors * Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors
- Stable implementation with enforced 100% test code coverage. Literally every single method, statement and * Stable implementation with enforced 100% test code coverage. Literally every single method, statement and
conditional branch variant in the entire codebase is tested and required to pass on every build. conditional branch variant in the entire codebase is tested and required to pass on every build.
- Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms: * Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms:
- HS256: HMAC using SHA-256 * HS256: HMAC using SHA-256
- HS384: HMAC using SHA-384 * HS384: HMAC using SHA-384
- HS512: HMAC using SHA-512 * HS512: HMAC using SHA-512
- ES256: ECDSA using P-256 and SHA-256 * ES256: ECDSA using P-256 and SHA-256
- ES384: ECDSA using P-384 and SHA-384 * ES384: ECDSA using P-384 and SHA-384
- ES512: ECDSA using P-521 and SHA-512 * ES512: ECDSA using P-521 and SHA-512
- RS256: RSASSA-PKCS-v1_5 using SHA-256 * RS256: RSASSA-PKCS-v1_5 using SHA-256
- RS384: RSASSA-PKCS-v1_5 using SHA-384 * RS384: RSASSA-PKCS-v1_5 using SHA-384
- RS512: RSASSA-PKCS-v1_5 using SHA-512 * RS512: RSASSA-PKCS-v1_5 using SHA-512
- PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256<sup>1</sup> * PS256: RSASSA-PSS using SHA-256 and MGF1 with SHA-256<sup>1</sup>
- PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384<sup>1</sup> * PS384: RSASSA-PSS using SHA-384 and MGF1 with SHA-384<sup>1</sup>
- PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512<sup>1</sup> * PS512: RSASSA-PSS using SHA-512 and MGF1 with SHA-512<sup>1</sup>
<sup>1. Requires JDK 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup> <sup>1. Requires JDK 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.</sup>
- Convenience enhancements beyond the specification such as * Convenience enhancements beyond the specification such as
- Body compression for any large JWT, not just JWEs * Body compression for any large JWT, not just JWEs
- Claims assertions (requiring specific values) * Claims assertions (requiring specific values)
- Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson) * Claim POJO marshaling and unmarshaling when using a compatible JSON parser (e.g. Jackson)
- Secure Key generation based on desired JWA algorithms * Secure Key generation based on desired JWA algorithms
- and more... * and more...
<a name="features-unsupported"></a> <a name="features-unsupported"></a>
### Currently Unsupported Features ### Currently Unsupported Features
- [Non-compact](https://tools.ietf.org/html/rfc7515#section-7.2) serialization and parsing. * [Non-compact](https://tools.ietf.org/html/rfc7515#section-7.2) serialization and parsing.
- JWE (Encryption for JWT) * JWE (Encryption for JWT)
These features will be implemented in a future release. Community contributions are welcome! These features will be implemented in a future release. Community contributions are welcome!
<a name="community"></a> <a name="community"></a>
## Community ## Community
<a name="help"></a> <a name="help"></a>
### Getting Help ### Getting Help
If you have trouble using JJWT, please first read the documentation on this page before asking questions. We try 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.
<a name="help-questions"></a> <a name="help-questions"></a>
#### Questions #### Questions
If the documentation or the API JavaDoc isn't sufficient, and you either have usability questions or are confused If the documentation or the API JavaDoc isn't sufficient, and you either have usability questions or are confused
@ -161,7 +157,6 @@ 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.**
<a name="help-issues"></a> <a name="help-issues"></a>
#### Bugs and Feature Requests #### Bugs and Feature Requests
If you do not have a usability question and believe you have a legitimate bug or feature request, If you do not have a usability question and believe you have a legitimate bug or feature request,
@ -171,11 +166,9 @@ If you feel like you'd like to help fix a bug or implement the new feature yours
section next before starting any work. section next before starting any work.
<a name="contributing"></a> <a name="contributing"></a>
### Contributing ### Contributing
<a name="contributing-pull-requests"></a> <a name="contributing-pull-requests"></a>
#### Pull Requests #### Pull Requests
Simple Pull Requests that fix anything other than JJWT core code (documentation, JavaDoc, typos, test cases, etc) are Simple Pull Requests that fix anything other than JJWT core code (documentation, JavaDoc, typos, test cases, etc) are
@ -194,7 +187,6 @@ So, please [create a new JJWT issue](https://github.com/jwtk/jjwt/issues/new) fi
(or how) a PR is warranted. Thank you! (or how) a PR is warranted. Thank you!
<a name="contributing-help-wanted"></a> <a name="contributing-help-wanted"></a>
#### Help Wanted #### Help Wanted
If you would like to help, but don't know where to start, please visit the If you would like to help, but don't know where to start, please visit the
@ -206,7 +198,6 @@ appreciated based on the above caveats concerning [contributing pull reqeuests](
to discuss or ask questions first if you're not sure. :) to discuss or ask questions first if you're not sure. :)
<a name="overview"></a> <a name="overview"></a>
## What is a JSON Web Token? ## 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. Don't know what a JSON Web Token is? Read on. Otherwise, jump on down to the [Installation](#Installation) section.
@ -234,7 +225,6 @@ If you pass the first two parts through a base 64 url decoder, you'll get the fo
clarity): clarity):
`header` `header`
``` ```
{ {
"alg": "HS256" "alg": "HS256"
@ -242,7 +232,6 @@ clarity):
``` ```
`body` `body`
``` ```
{ {
"sub": "Joe" "sub": "Joe"
@ -258,7 +247,6 @@ 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. To compute the signature, you need a secret key to sign it. We'll cover keys and algorithms later.
<a name="install"></a> <a name="install"></a>
## Installation ## Installation
Use your favorite Maven-compatible build tool to pull the dependencies from Maven Central. Use your favorite Maven-compatible build tool to pull the dependencies from Maven Central.
@ -267,13 +255,11 @@ The dependencies could differ slightly if you are working with a [JDK project](#
[Android project](#install-android). [Android project](#install-android).
<a name="install-jdk"></a> <a name="install-jdk"></a>
### JDK Projects ### JDK Projects
If you're building a (non-Android) JDK project, you will want to define the following dependencies: If you're building a (non-Android) JDK project, you will want to define the following dependencies:
<a name="install-jdk-maven"></a> <a name="install-jdk-maven"></a>
#### Maven #### Maven
```xml ```xml
@ -307,7 +293,6 @@ If you're building a (non-Android) JDK project, you will want to define the foll
``` ```
<a name="install-jdk-gradle"></a> <a name="install-jdk-gradle"></a>
#### Gradle #### Gradle
```groovy ```groovy
@ -317,20 +302,15 @@ dependencies {
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms: // Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60', //'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.11.2' // or 'io.jsonwebtoken:jjwt-gson:0.11.2' for gson 'io.jsonwebtoken:jjwt-jackson:0.11.2' // or 'io.jsonwebtoken:jjwt-gson:0.11.2' for gson
// Gradle 7+ doesn't support the compile and runtime configuration anymore. Please use implementation and runtimeOnly instead.
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
} }
``` ```
<a name="install-android"></a> <a name="install-android"></a>
### Android Projects ### Android Projects
Android projects will want to define the following dependencies and Proguard exclusions: Android projects will want to define the following dependencies and Proguard exclusions:
<a name="install-android-dependencies"></a> <a name="install-android-dependencies"></a>
#### Dependencies #### Dependencies
Add the dependencies to your project: Add the dependencies to your project:
@ -348,7 +328,6 @@ dependencies {
``` ```
<a name="install-android-proguard"></a> <a name="install-android-proguard"></a>
#### Proguard #### Proguard
You can use the following [Android Proguard](https://developer.android.com/studio/build/shrink-code) exclusion rules: You can use the following [Android Proguard](https://developer.android.com/studio/build/shrink-code) exclusion rules:
@ -366,7 +345,6 @@ You can use the following [Android Proguard](https://developer.android.com/studi
``` ```
<a name="install-understandingdependencies"></a> <a name="install-understandingdependencies"></a>
### Understanding JJWT Dependencies ### Understanding JJWT Dependencies
Notice the above dependency declarations all have only one compile-time dependency and the rest are declared as Notice the above dependency declarations all have only one compile-time dependency and the rest are declared as
@ -388,7 +366,6 @@ implementations whenever and however necessary. This helps us implement features
you more quickly and efficiently. you more quickly and efficiently.
<a name="quickstart"></a> <a name="quickstart"></a>
## Quickstart ## Quickstart
Most complexity is hidden behind a convenient and readable builder-based [fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), great for relying on IDE auto-completion to write code quickly. Here's an example: Most complexity is hidden behind a convenient and readable builder-based [fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), great for relying on IDE auto-completion to write code quickly. Here's an example:
@ -410,10 +387,10 @@ How easy was that!?
In this case, we are: In this case, we are:
1. _building_ a JWT that will have the 1. *building* a JWT that will have the
[registered claim](https://tools.ietf.org/html/rfc7519#section-4.1) `sub` (subject) set to `Joe`. We are then [registered claim](https://tools.ietf.org/html/rfc7519#section-4.1) `sub` (subject) set to `Joe`. We are then
2. _signing_ the JWT using a key suitable for the HMAC-SHA-256 algorithm. Finally, we are 2. *signing* the JWT using a key suitable for the HMAC-SHA-256 algorithm. Finally, we are
3. _compacting_ it into its final `String` form. A signed JWT is called a 'JWS'. 3. *compacting* it into its final `String` form. A signed JWT is called a 'JWS'.
The resultant `jws` String looks like this: The resultant `jws` String looks like this:
@ -451,7 +428,6 @@ try {
``` ```
<a name="jws"></a> <a name="jws"></a>
## Signed JWTs ## Signed JWTs
The JWT specification provides for the ability to The JWT specification provides for the ability to
@ -468,7 +444,6 @@ So how is a JWT signed? Let's walk through it with some easy-to-read pseudocode:
1. Assume we have a JWT with a JSON header and body (aka 'Claims') as follows: 1. Assume we have a JWT with a JSON header and body (aka 'Claims') as follows:
**header** **header**
``` ```
{ {
"alg": "HS256" "alg": "HS256"
@ -476,7 +451,6 @@ So how is a JWT signed? Let's walk through it with some easy-to-read pseudocode:
``` ```
**body** **body**
``` ```
{ {
"sub": "Joe" "sub": "Joe"
@ -518,6 +492,7 @@ So how is a JWT signed? Let's walk through it with some easy-to-read pseudocode:
String jws = concatenated + '.' + base64URLEncode( signature ) String jws = concatenated + '.' + base64URLEncode( signature )
``` ```
And there you have it, the final `jws` String looks like this: And there you have it, the final `jws` String looks like this:
``` ```
@ -535,24 +510,23 @@ Keys, specifically as they relate to the JWT specifications. Understanding them
JWS properly. JWS properly.
<a name="jws-key"></a> <a name="jws-key"></a>
### Signature Algorithms Keys ### Signature Algorithms Keys
The JWT specification identifies 12 standard signature algorithms - 3 secret key algorithms and 9 asymmetric The JWT specification identifies 12 standard signature algorithms - 3 secret key algorithms and 9 asymmetric
key algorithms - identified by the following names: key algorithms - identified by the following names:
- `HS256`: HMAC using SHA-256 * `HS256`: HMAC using SHA-256
- `HS384`: HMAC using SHA-384 * `HS384`: HMAC using SHA-384
- `HS512`: HMAC using SHA-512 * `HS512`: HMAC using SHA-512
- `ES256`: ECDSA using P-256 and SHA-256 * `ES256`: ECDSA using P-256 and SHA-256
- `ES384`: ECDSA using P-384 and SHA-384 * `ES384`: ECDSA using P-384 and SHA-384
- `ES512`: ECDSA using P-521 and SHA-512 * `ES512`: ECDSA using P-521 and SHA-512
- `RS256`: RSASSA-PKCS-v1_5 using SHA-256 * `RS256`: RSASSA-PKCS-v1_5 using SHA-256
- `RS384`: RSASSA-PKCS-v1_5 using SHA-384 * `RS384`: RSASSA-PKCS-v1_5 using SHA-384
- `RS512`: RSASSA-PKCS-v1_5 using SHA-512 * `RS512`: RSASSA-PKCS-v1_5 using SHA-512
- `PS256`: RSASSA-PSS using SHA-256 and MGF1 with SHA-256 * `PS256`: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
- `PS384`: RSASSA-PSS using SHA-384 and MGF1 with SHA-384 * `PS384`: RSASSA-PSS using SHA-384 and MGF1 with SHA-384
- `PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512 * `PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
These are all represented in the `io.jsonwebtoken.SignatureAlgorithm` enum. These are all represented in the `io.jsonwebtoken.SignatureAlgorithm` enum.
@ -572,23 +546,22 @@ one wants completely insecure JWTs, right? Neither would we.
So what are the requirements? So what are the requirements?
<a name="jws-key-hmacsha"></a> <a name="jws-key-hmacsha"></a>
#### HMAC-SHA #### HMAC-SHA
JWT HMAC-SHA signature algorithms `HS256`, `HS384`, and `HS512` require a secret key that is _at least_ as many bits as JWT HMAC-SHA signature algorithms `HS256`, `HS384`, and `HS512` require a secret key that is _at least_ as many bits as
the algorithm's signature (digest) length per [RFC 7512 Section 3.2](https://tools.ietf.org/html/rfc7518#section-3.2). the algorithm's signature (digest) length per [RFC 7512 Section 3.2](https://tools.ietf.org/html/rfc7518#section-3.2).
This means: This means:
- `HS256` is HMAC-SHA-256, and that produces digests that are 256 bits (32 bytes) long, so `HS256` _requires_ that you * `HS256` is HMAC-SHA-256, and that produces digests that are 256 bits (32 bytes) long, so `HS256` _requires_ that you
use a secret key that is at least 32 bytes long. use a secret key that is at least 32 bytes long.
- `HS384` is HMAC-SHA-384, and that produces digests that are 384 bits (48 bytes) long, so `HS384` _requires_ that you
* `HS384` is HMAC-SHA-384, and that produces digests that are 384 bits (48 bytes) long, so `HS384` _requires_ that you
use a secret key that is at least 48 bytes long. use a secret key that is at least 48 bytes long.
- `HS512` is HMAC-SHA-512, and that produces digests that are 512 bits (64 bytes) long, so `HS512` _requires_ that you * `HS512` is HMAC-SHA-512, and that produces digests that are 512 bits (64 bytes) long, so `HS512` _requires_ that you
use a secret key that is at least 64 bytes long. use a secret key that is at least 64 bytes long.
<a name="jws-key-rsa"></a> <a name="jws-key-rsa"></a>
#### RSA #### RSA
JWT RSA signature algorithms `RS256`, `RS384`, `RS512`, `PS256`, `PS384` and `PS512` all require a minimum key length JWT RSA signature algorithms `RS256`, `RS384`, `RS512`, `PS256`, `PS384` and `PS512` all require a minimum key length
@ -599,28 +572,27 @@ Anything smaller than this (such as 1024 bits) will be rejected with an `Invalid
That said, in keeping with best practices and increasing key lengths for security longevity, JJWT That said, in keeping with best practices and increasing key lengths for security longevity, JJWT
recoommends that you use: recoommends that you use:
- at least 2048 bit keys with `RS256` and `PS256` * at least 2048 bit keys with `RS256` and `PS256`
- at least 3072 bit keys with `RS384` and `PS384` * at least 3072 bit keys with `RS384` and `PS384`
- at least 4096 bit keys with `RS512` and `PS512` * at least 4096 bit keys with `RS512` and `PS512`
These are only JJWT suggestions and not requirements. JJWT only enforces JWT specification requirements and These are only JJWT suggestions and not requirements. JJWT only enforces JWT specification requirements and
for any RSA key, the requirement is the RSA key (modulus) length in bits MUST be >= 2048 bits. for any RSA key, the requirement is the RSA key (modulus) length in bits MUST be >= 2048 bits.
<a name="jws-key-ecdsa"></a> <a name="jws-key-ecdsa"></a>
#### Elliptic Curve #### Elliptic Curve
JWT Elliptic Curve signature algorithms `ES256`, `ES384`, and `ES512` all require a minimum key length JWT Elliptic Curve signature algorithms `ES256`, `ES384`, and `ES512` all require a minimum key length
(aka an Elliptic Curve order bit length) that is _at least_ as many bits as the algorithm signature's individual (aka an Elliptic Curve order bit length) that is _at least_ as many bits as the algorithm signature's individual
`R` and `S` components per [RFC 7512 Section 3.4](https://tools.ietf.org/html/rfc7518#section-3.4). This means: `R` and `S` components per [RFC 7512 Section 3.4](https://tools.ietf.org/html/rfc7518#section-3.4). This means:
- `ES256` requires that you use a private key that is at least 256 bits (32 bytes) long. * `ES256` requires that you use a private key that is at least 256 bits (32 bytes) long.
- `ES384` requires that you use a private key that is at least 384 bits (48 bytes) long.
- `ES512` requires that you use a private key that is at least 512 bits (64 bytes) long. * `ES384` requires that you use a private key that is at least 384 bits (48 bytes) long.
* `ES512` requires that you use a private key that is at least 512 bits (64 bytes) long.
<a name="jws-key-create"></a> <a name="jws-key-create"></a>
#### Creating Safe Keys #### Creating Safe Keys
If you don't want to think about bit length requirements or just want to make your life easier, JJWT has If you don't want to think about bit length requirements or just want to make your life easier, JJWT has
@ -628,7 +600,6 @@ provided the `io.jsonwebtoken.security.Keys` utility class that can generate suf
JWT signature algorithm you might want to use. JWT signature algorithm you might want to use.
<a name="jws-key-create-secret"></a> <a name="jws-key-create-secret"></a>
##### Secret Keys ##### Secret Keys
If you want to generate a sufficiently strong `SecretKey` for use with the JWT HMAC-SHA algorithms, use the If you want to generate a sufficiently strong `SecretKey` for use with the JWT HMAC-SHA algorithms, use the
@ -652,7 +623,6 @@ Ensure you save the resulting `secretString` somewhere safe -
further encrypt it, etc, before saving to disk (for example). further encrypt it, etc, before saving to disk (for example).
<a name="jws-key-create-asym"></a> <a name="jws-key-create-asym"></a>
##### Asymmetric Keys ##### Asymmetric Keys
If you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with JWT ECDSA or RSA If you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with JWT ECDSA or RSA
@ -671,7 +641,6 @@ the [Installation](#Installation) section to see how to enable BouncyCastle. All
supported by the JDK. supported by the JDK.
<a name="jws-create"></a> <a name="jws-create"></a>
### Creating a JWS ### Creating a JWS
You create a JWS as follows: You create a JWS as follows:
@ -694,7 +663,6 @@ String jws = Jwts.builder() // (1)
``` ```
<a name="jws-create-header"></a> <a name="jws-create-header"></a>
#### Header Parameters #### Header Parameters
A JWT Header provides metadata about the contents, format and cryptographic operations relevant to the JWT's Claims. A JWT Header provides metadata about the contents, format and cryptographic operations relevant to the JWT's Claims.
@ -719,7 +687,6 @@ potentially overwriting any existing identically-named key/value pair.
depending on the signature algorithm or compression algorithm used. depending on the signature algorithm or compression algorithm used.
<a name="jws-create-header-instance"></a> <a name="jws-create-header-instance"></a>
##### Header Instance ##### Header Instance
If you want to specify the entire header at once, you can use the `Jwts.header()` method and build up the header If you want to specify the entire header at once, you can use the `Jwts.header()` method and build up the header
@ -744,7 +711,6 @@ already been set. In all cases however, JJWT will still set (and overwrite) any
if those are in the specified `header` object or not. if those are in the specified `header` object or not.
<a name="jws-create-header-map"></a> <a name="jws-create-header-map"></a>
##### Header Map ##### Header Map
If you want to specify the entire header at once and you don't want to use `Jwts.header()`, you can use `JwtBuilder` If you want to specify the entire header at once and you don't want to use `Jwts.header()`, you can use `JwtBuilder`
@ -762,30 +728,29 @@ String jws = Jwts.builder()
``` ```
**NOTE**: Calling `setHeader` will overwrite any existing header name/value pairs with the same names that might have **NOTE**: Calling `setHeader` will overwrite any existing header name/value pairs with the same names that might have
already been set. In all cases however, JJWT will still set (and overwrite) any `alg` and `zip` headers regardless already been set. In all cases however, JJWT will still set (and overwrite) any `alg` and `zip` headers regardless
if those are in the specified `header` object or not. if those are in the specified `header` object or not.
<a name="jws-create-claims"></a> <a name="jws-create-claims"></a>
#### Claims #### Claims
Claims are a JWT's 'body' and contain the information that the JWT creator wishes to present to the JWT recipient(s). Claims are a JWT's 'body' and contain the information that the JWT creator wishes to present to the JWT recipient(s).
<a name="jws-create-claims-standard"></a> <a name="jws-create-claims-standard"></a>
##### Standard Claims ##### Standard Claims
The `JwtBuilder` provides convenient setter methods for standard registered Claim names defined in the JWT The `JwtBuilder` provides convenient setter methods for standard registered Claim names defined in the JWT
specification. They are: specification. They are:
- `setIssuer`: sets the [`iss` (Issuer) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.1) * `setIssuer`: sets the [`iss` (Issuer) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.1)
- `setSubject`: sets the [`sub` (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2) * `setSubject`: sets the [`sub` (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2)
- `setAudience`: sets the [`aud` (Audience) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.3) * `setAudience`: sets the [`aud` (Audience) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.3)
- `setExpiration`: sets the [`exp` (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4) * `setExpiration`: sets the [`exp` (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4)
- `setNotBefore`: sets the [`nbf` (Not Before) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.5) * `setNotBefore`: sets the [`nbf` (Not Before) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.5)
- `setIssuedAt`: sets the [`iat` (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6) * `setIssuedAt`: sets the [`iat` (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6)
- `setId`: sets the [`jti` (JWT ID) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.7) * `setId`: sets the [`jti` (JWT ID) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.7)
For example: For example:
@ -805,7 +770,6 @@ String jws = Jwts.builder()
``` ```
<a name="jws-create-claims-custom"></a> <a name="jws-create-claims-custom"></a>
##### Custom Claims ##### Custom Claims
If you need to set one or more custom claims that don't match the standard setter method claims shown above, you If you need to set one or more custom claims that don't match the standard setter method claims shown above, you
@ -827,7 +791,6 @@ Obviously, you do not need to call `claim` for any [standard claim name](#jws-cr
recommended instead to call the standard respective setter method as this enhances readability. recommended instead to call the standard respective setter method as this enhances readability.
<a name="jws-create-claims-instance"></a> <a name="jws-create-claims-instance"></a>
###### Claims Instance ###### Claims Instance
If you want to specify all claims at once, you can use the `Jwts.claims()` method and build up the claims If you want to specify all claims at once, you can use the `Jwts.claims()` method and build up the claims
@ -851,7 +814,6 @@ String jws = Jwts.builder()
already been set. already been set.
<a name="jws-create-claims-map"></a> <a name="jws-create-claims-map"></a>
###### Claims Map ###### Claims Map
If you want to specify all claims at once and you don't want to use `Jwts.claims()`, you can use `JwtBuilder` If you want to specify all claims at once and you don't want to use `Jwts.claims()`, you can use `JwtBuilder`
@ -873,7 +835,6 @@ String jws = Jwts.builder()
already been set. already been set.
<a name="jws-create-key"></a> <a name="jws-create-key"></a>
#### Signing Key #### Signing Key
It is recommended that you specify the signing key by calling call the `JwtBuilder`'s `signWith` method and let JJWT It is recommended that you specify the signing key by calling call the `JwtBuilder`'s `signWith` method and let JJWT
@ -904,7 +865,6 @@ The same selection logic applies for Elliptic Curve `PrivateKey`s.
`PublicKey` for signing with an `InvalidKeyException`. `PublicKey` for signing with an `InvalidKeyException`.
<a name="jws-create-key-secret"></a> <a name="jws-create-key-secret"></a>
##### SecretKey Formats ##### SecretKey Formats
If you want to sign a JWS using HMAC-SHA algorithms and you have a secret key `String` or If you want to sign a JWS using HMAC-SHA algorithms and you have a secret key `String` or
@ -913,24 +873,22 @@ to convert it into a `SecretKey` instance to use as the `signWith` method argume
If your secret key is: If your secret key is:
- An [encoded byte array](https://docs.oracle.com/javase/8/docs/api/java/security/Key.html#getEncoded--): * An [encoded byte array](https://docs.oracle.com/javase/8/docs/api/java/security/Key.html#getEncoded--):
```java ```java
SecretKey key = Keys.hmacShaKeyFor(encodedKeyBytes); SecretKey key = Keys.hmacShaKeyFor(encodedKeyBytes);
``` ```
- A Base64-encoded string: * A Base64-encoded string:
```java ```java
SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString)); SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));
``` ```
- A Base64URL-encoded string: * A Base64URL-encoded string:
```java ```java
SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString)); SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));
``` ```
- A raw (non-encoded) string (e.g. a password String): * A raw (non-encoded) string (e.g. a password String):
```java ```java
SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8)); SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
``` ```
It is always incorrect to call `secretString.getBytes()` (without providing a charset). It is always incorrect to call `secretString.getBytes()` (without providing a charset).
However, raw password strings like this, e.g. `correcthorsebatterystaple` should be avoided whenever possible However, raw password strings like this, e.g. `correcthorsebatterystaple` should be avoided whenever possible
@ -938,7 +896,6 @@ If your secret key is:
If you are able, prefer creating a [new secure-random secret key](#jws-key-create-secret) instead. If you are able, prefer creating a [new secure-random secret key](#jws-key-create-secret) instead.
<a name="jws-create-key-algoverride"></a> <a name="jws-create-key-algoverride"></a>
##### SignatureAlgorithm Override ##### SignatureAlgorithm Override
In some specific cases, you might want to override JJWT's default selected algorithm for a given key. In some specific cases, you might want to override JJWT's default selected algorithm for a given key.
@ -963,17 +920,15 @@ bits.
used for that algorithm according to the JWT specification requirements.** used for that algorithm according to the JWT specification requirements.**
<a name="jws-create-compression"></a> <a name="jws-create-compression"></a>
#### JWS Compression #### JWS Compression
If your JWT claims set is large (contains a lot of data), and you are certain that JJWT will also be the same library If your JWT claims set is large (contains a lot of data), and you are certain that JJWT will also be the same library
that reads/parses your JWS, you might want to compress the JWS to reduce its size. Note that this is that reads/parses your JWS, you might want to compress the JWS to reduce its size. Note that this is
_not_ a standard feature for JWS and is not likely to be supported by other JWT libraries. *not* a standard feature for JWS and is not likely to be supported by other JWT libraries.
Please see the main [Compression](#compression) section to see how to compress and decompress JWTs. Please see the main [Compression](#compression) section to see how to compress and decompress JWTs.
<a name="jws-read"></a> <a name="jws-read"></a>
### Reading a JWS ### Reading a JWS
You read (parse) a JWS as follows: You read (parse) a JWS as follows:
@ -1011,7 +966,6 @@ catch (JwtException ex) { // (5)
available) as this guarantees the correct security model for parsing signed JWTs. available) as this guarantees the correct security model for parsing signed JWTs.
<a name="jws-read-key"></a> <a name="jws-read-key"></a>
#### Verification Key #### Verification Key
The most important thing to do when reading a JWS is to specify the key to use to verify the JWS's The most important thing to do when reading a JWS is to specify the key to use to verify the JWS's
@ -1020,7 +974,7 @@ discarded.
So which key do we use for verification? So which key do we use for verification?
- If the jws was signed with a `SecretKey`, the same `SecretKey` should be specified on the `JwtParserBuilder`. For example: * If the jws was signed with a `SecretKey`, the same `SecretKey` should be specified on the `JwtParserBuilder`. For example:
```java ```java
Jwts.parserBuilder() Jwts.parserBuilder()
@ -1030,8 +984,7 @@ So which key do we use for verification?
.build() .build()
.parseClaimsJws(jwsString); .parseClaimsJws(jwsString);
``` ```
* If the jws was signed with a `PrivateKey`, that key's corresponding `PublicKey` (not the `PrivateKey`) should be
- If the jws was signed with a `PrivateKey`, that key's corresponding `PublicKey` (not the `PrivateKey`) should be
specified on the `JwtParserBuilder`. For example: specified on the `JwtParserBuilder`. For example:
```java ```java
@ -1051,7 +1004,6 @@ In these cases, you can't call the `JwtParserBuilder`'s `setSigningKey` method w
to use a `SigningKeyResolver`, covered next. to use a `SigningKeyResolver`, covered next.
<a name="jws-read-key-resolver"></a> <a name="jws-read-key-resolver"></a>
##### Signing Key Resolver ##### Signing Key Resolver
If your application expects JWSs that can be signed with different keys, you won't call the `setSigningKey` method. If your application expects JWSs that can be signed with different keys, you won't call the `setSigningKey` method.
@ -1136,14 +1088,13 @@ Finally remember that for HMAC algorithms, the returned verification key should
algorithms, the key returned should be a `PublicKey` (not a `PrivateKey`). algorithms, the key returned should be a `PublicKey` (not a `PrivateKey`).
<a name="jws-read-claims"></a> <a name="jws-read-claims"></a>
#### Claim Assertions #### Claim Assertions
You can enforce that the JWS you are parsing conforms to expectations that you require and are important for your You can enforce that the JWS you are parsing conforms to expectations that you require and are important for your
application. application.
For example, let's say that you require that the JWS you are parsing has a specific `sub` (subject) value, For example, let's say that you require that the JWS you are parsing has a specific `sub` (subject) value,
otherwise you may not trust the token. You can do that by using one of the various `require`\* methods on the otherwise you may not trust the token. You can do that by using one of the various `require`* methods on the
`JwtParserBuilder`: `JwtParserBuilder`:
```java ```java
@ -1176,14 +1127,12 @@ try {
// the 'myfield' field was missing or did not have a 'myRequiredValue' value // the 'myfield' field was missing or did not have a 'myRequiredValue' value
} }
``` ```
(or, again, you could catch either `MissingClaimException` or `IncorrectClaimException` instead). (or, again, you could catch either `MissingClaimException` or `IncorrectClaimException` instead).
Please see the `JwtParserBuilder` class and/or JavaDoc for a full list of the various `require`\* methods you may use for claims Please see the `JwtParserBuilder` class and/or JavaDoc for a full list of the various `require`* methods you may use for claims
assertions. assertions.
<a name="jws-read-clock"></a> <a name="jws-read-clock"></a>
#### Accounting for Clock Skew #### Accounting for Clock Skew
When parsing a JWT, you might find that `exp` or `nbf` claim assertions fail (throw exceptions) because the clock on When parsing a JWT, you might find that `exp` or `nbf` claim assertions fail (throw exceptions) because the clock on
@ -1192,7 +1141,7 @@ obvious problems since `exp` and `nbf` are time-based assertions, and clock time
assertions. assertions.
You can account for these differences (usually no more than a few minutes) when parsing using the `JwtParserBuilder`'s You can account for these differences (usually no more than a few minutes) when parsing using the `JwtParserBuilder`'s
`setAllowedClockSkewSeconds`. For example: `setAllowedClockSkewSeconds`. For example:
```java ```java
long seconds = 3 * 60; //3 minutes long seconds = 3 * 60; //3 minutes
@ -1205,20 +1154,18 @@ Jwts.parserBuilder()
.build() .build()
.parseClaimsJws(jwt); .parseClaimsJws(jwt);
``` ```
This ensures that clock differences between the machines can be ignored. Two or three minutes should be more than This ensures that clock differences between the machines can be ignored. Two or three minutes should be more than
enough; it would be fairly strange if a production machine's clock was more than 5 minutes difference from most enough; it would be fairly strange if a production machine's clock was more than 5 minutes difference from most
atomic clocks around the world. atomic clocks around the world.
<a name="jws-read-clock-custom"></a> <a name="jws-read-clock-custom"></a>
##### Custom Clock Support ##### Custom Clock Support
If the above `setAllowedClockSkewSeconds` isn't sufficient for your needs, the timestamps created If the above `setAllowedClockSkewSeconds` isn't sufficient for your needs, the timestamps created
during parsing for timestamp comparisons can be obtained via a custom time source. Call the `JwtParserBuilder`'s `setClock` during parsing for timestamp comparisons can be obtained via a custom time source. Call the `JwtParserBuilder`'s `setClock`
method with an implementation of the `io.jsonwebtoken.Clock` interface. For example: method with an implementation of the `io.jsonwebtoken.Clock` interface. For example:
```java ```java
Clock clock = new MyClock(); Clock clock = new MyClock();
Jwts.parserBuilder().setClock(myClock) //... etc ... Jwts.parserBuilder().setClock(myClock) //... etc ...
@ -1229,7 +1176,6 @@ as most would expect. However, supplying your own clock could be useful, especia
guarantee deterministic behavior. guarantee deterministic behavior.
<a name="jws-read-decompression"></a> <a name="jws-read-decompression"></a>
#### JWS Decompression #### JWS Decompression
If you used JJWT to compress a JWS and you used a custom compression algorithm, you will need to tell the `JwtParserBuilder` If you used JJWT to compress a JWS and you used a custom compression algorithm, you will need to tell the `JwtParserBuilder`
@ -1240,7 +1186,6 @@ Please see the [Compression](#compression) section below to see how to decompres
<!-- TODO: ## Encrypted JWTs --> <!-- TODO: ## Encrypted JWTs -->
<a name="compression"></a> <a name="compression"></a>
## Compression ## Compression
**The JWT specification only standardizes this feature for JWEs (Encrypted JWTs) and not JWSs (Signed JWTs), however **The JWT specification only standardizes this feature for JWEs (Encrypted JWTs) and not JWSs (Signed JWTs), however
@ -1269,7 +1214,6 @@ If you use the `DEFLATE` or `GZIP` Compression Codecs - that's it, you're done.
parsing or configure the `JwtParser` for compression - JJWT will automatically decompress the body as expected. parsing or configure the `JwtParser` for compression - JJWT will automatically decompress the body as expected.
<a name="compression-custom"></a> <a name="compression-custom"></a>
### Custom Compression Codec ### Custom Compression Codec
If however, you used your own custom compression codec when creating the JWT (via `JwtBuilder` `compressWith`), then If however, you used your own custom compression codec when creating the JWT (via `JwtBuilder` `compressWith`), then
@ -1304,7 +1248,6 @@ public class MyCompressionCodecResolver implements CompressionCodecResolver {
``` ```
<a name="json"></a> <a name="json"></a>
## JSON Support ## JSON Support
A `JwtBuilder` will serialize the `Header` and `Claims` maps (and potentially any Java objects they A `JwtBuilder` will serialize the `Header` and `Claims` maps (and potentially any Java objects they
@ -1317,14 +1260,16 @@ They are checked in order, and the first one found is used:
1. Jackson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-jackson` as a project runtime 1. Jackson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-jackson` as a project runtime
dependency. Jackson supports POJOs as claims with full marshaling/unmarshaling as necessary. dependency. Jackson supports POJOs as claims with full marshaling/unmarshaling as necessary.
2. Gson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-gson` as a project runtime dependency. 2. Gson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-gson` as a project runtime dependency.
Gson also supports POJOs as claims with full marshaling/unmarshaling as necessary. Gson also supports POJOs as claims with full marshaling/unmarshaling as necessary.
3. JSON-Java (`org.json`): This will be used automatically if you specify `io.jsonwebtoken:jjwt-orgjson` as a 3. JSON-Java (`org.json`): This will be used automatically if you specify `io.jsonwebtoken:jjwt-orgjson` as a
project runtime dependency. project runtime dependency.
**NOTE:** `org.json` APIs are natively enabled in Android environments so this is the recommended JSON processor for **NOTE:** `org.json` APIs are natively enabled in Android environments so this is the recommended JSON processor for
Android applications _unless_ you want to use POJOs as claims. The `org.json` library supports simple Android applications _unless_ you want to use POJOs as claims. The `org.json` library supports simple
Object-to-JSON marshaling, but it _does not_ support JSON-to-Object unmarshalling. Object-to-JSON marshaling, but it *does not* support JSON-to-Object unmarshalling.
**If you want to use POJOs as claim values, use either the `io.jsonwebtoken:jjwt-jackson` or **If you want to use POJOs as claim values, use either the `io.jsonwebtoken:jjwt-jackson` or
`io.jsonwebtoken:jjwt-gson` dependency** (or implement your own Serializer and Deserializer if desired). **But beware**, `io.jsonwebtoken:jjwt-gson` dependency** (or implement your own Serializer and Deserializer if desired). **But beware**,
@ -1332,7 +1277,6 @@ Jackson will force a sizable (> 1 MB) dependency to an Android application thus
mobile users. mobile users.
<a name="json-custom"></a> <a name="json-custom"></a>
### Custom JSON Processor ### Custom JSON Processor
If you don't want to use JJWT's runtime dependency approach, or just want to customize how JSON serialization and If you don't want to use JJWT's runtime dependency approach, or just want to customize how JSON serialization and
@ -1364,7 +1308,6 @@ Jwts.parserBuilder()
``` ```
<a name="json-jackson"></a> <a name="json-jackson"></a>
### Jackson JSON Processor ### Jackson JSON Processor
If you want to use Jackson for JSON processing, just including the `io.jsonwebtoken:jjwt-jackson` dependency as a If you want to use Jackson for JSON processing, just including the `io.jsonwebtoken:jjwt-jackson` dependency as a
@ -1397,8 +1340,6 @@ scope which is the typical JJWT default). That is:
```groovy ```groovy
dependencies { dependencies {
compile 'io.jsonwebtoken:jjwt-jackson:0.11.2' compile 'io.jsonwebtoken:jjwt-jackson:0.11.2'
// if you are using gradle 7+, you need to use the implemetation dependency instead.
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
} }
``` ```
@ -1427,7 +1368,6 @@ Jwts.parserBuilder()
``` ```
<a name="json-jackson-custom-types"></a> <a name="json-jackson-custom-types"></a>
#### Parsing of Custom Claim Types #### Parsing of Custom Claim Types
By default JJWT will only convert simple claim types: String, Date, Long, Integer, Short and Byte. If you need to deserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in through a constructor. For example: By default JJWT will only convert simple claim types: String, Date, Long, Integer, Short and Byte. If you need to deserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in through a constructor. For example:
@ -1470,7 +1410,6 @@ consequences for a shared `ObjectMapper` instance. This should work for most app
parsing options, [configure the mapper directly](#json-jackson). parsing options, [configure the mapper directly](#json-jackson).
<a name="json-gson"></a> <a name="json-gson"></a>
### Gson JSON Processor ### Gson JSON Processor
If you want to use Gson for JSON processing, just including the `io.jsonwebtoken:jjwt-gson` dependency as a If you want to use Gson for JSON processing, just including the `io.jsonwebtoken:jjwt-gson` dependency as a
@ -1509,8 +1448,6 @@ scope which is the typical JJWT default). That is:
```groovy ```groovy
dependencies { dependencies {
compile 'io.jsonwebtoken:jjwt-gson:0.11.2' compile 'io.jsonwebtoken:jjwt-gson:0.11.2'
// if you are using gradle 7+, you need to use the implemetation dependency instead.
implementation 'io.jsonwebtoken:jjwt-gson:0.11.2'
} }
``` ```
@ -1540,7 +1477,6 @@ Jwts.parser()
``` ```
<a name="base64"></a> <a name="base64"></a>
## Base64 Support ## Base64 Support
JJWT uses a very fast pure-Java [Base64](https://tools.ietf.org/html/rfc4648) codec for Base64 and JJWT uses a very fast pure-Java [Base64](https://tools.ietf.org/html/rfc4648) codec for Base64 and
@ -1551,16 +1487,15 @@ utility classes.
`io.jsonwebtoken.io.Encoders`: `io.jsonwebtoken.io.Encoders`:
- `BASE64` is an RFC 4648 [Base64](https://tools.ietf.org/html/rfc4648#section-4) encoder * `BASE64` is an RFC 4648 [Base64](https://tools.ietf.org/html/rfc4648#section-4) encoder
- `BASE64URL` is an RFC 4648 [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) encoder * `BASE64URL` is an RFC 4648 [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) encoder
`io.jsonwebtoken.io.Decoders`: `io.jsonwebtoken.io.Decoders`:
- `BASE64` is an RFC 4648 [Base64](https://tools.ietf.org/html/rfc4648#section-4) decoder * `BASE64` is an RFC 4648 [Base64](https://tools.ietf.org/html/rfc4648#section-4) decoder
- `BASE64URL` is an RFC 4648 [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) decoder * `BASE64URL` is an RFC 4648 [Base64URL](https://tools.ietf.org/html/rfc4648#section-5) decoder
<a name="base64-security"></a> <a name="base64-security"></a>
### Understanding Base64 in Security Contexts ### Understanding Base64 in Security Contexts
All cryptographic operations, like encryption and message digest calculations, result in binary data - raw byte arrays. All cryptographic operations, like encryption and message digest calculations, result in binary data - raw byte arrays.
@ -1576,11 +1511,10 @@ into the original raw byte arrays for decryption or signature verification as ne
That's nice and convenient, but there are two very important properties of Base64 (and Base64URL) text strings that That's nice and convenient, but there are two very important properties of Base64 (and Base64URL) text strings that
are critical to remember when they are used in security scenarios like with JWTs: are critical to remember when they are used in security scenarios like with JWTs:
- [Base64 is not encryption](#base64-not-encryption) * [Base64 is not encryption](#base64-not-encryption)
- [Changing Base64 characters](#base64-changing-characters) **does not automatically invalidate data**. * [Changing Base64 characters](#base64-changing-characters) **does not automatically invalidate data**.
<a name="base64-not-encryption"></a> <a name="base64-not-encryption"></a>
#### Base64 is not encryption #### Base64 is not encryption
Base64-encoded text is _not_ encrypted. Base64-encoded text is _not_ encrypted.
@ -1600,7 +1534,6 @@ After Base64-encoding data into a string, it is possible to then encrypt the str
eyes if desired, but this is different. Encryption is not encoding. They are separate concepts. eyes if desired, but this is different. Encryption is not encoding. They are separate concepts.
<a name="base64-changing-characters"></a> <a name="base64-changing-characters"></a>
#### Changing Base64 Characters #### Changing Base64 Characters
In an effort to see if signatures or encryption is truly validated correctly, some try to edit a JWT In an effort to see if signatures or encryption is truly validated correctly, some try to edit a JWT
@ -1621,21 +1554,19 @@ and [JJWT issue comments](https://github.com/jwtk/jjwt/issues/211#issuecomment-2
Here's one [good answer](https://stackoverflow.com/questions/29941270/why-do-base64-decode-produce-same-byte-array-for-different-strings): Here's one [good answer](https://stackoverflow.com/questions/29941270/why-do-base64-decode-produce-same-byte-array-for-different-strings):
> Remember that Base64 encodes each 8 bit entity into 6 bit chars. The resulting string then needs exactly > Remember that Base64 encodes each 8 bit entity into 6 bit chars. The resulting string then needs exactly
> 11 \* 8 / 6 bytes, or 14 2/3 chars. But you can't write partial characters. Only the first 4 bits (or 2/3 of the > 11 * 8 / 6 bytes, or 14 2/3 chars. But you can't write partial characters. Only the first 4 bits (or 2/3 of the
> last char) are significant. The last two bits are not decoded. Thus all of: > last char) are significant. The last two bits are not decoded. Thus all of:
> >
> dGVzdCBzdHJpbmo > dGVzdCBzdHJpbmo
> dGVzdCBzdHJpbmp > dGVzdCBzdHJpbmp
> dGVzdCBzdHJpbmq > dGVzdCBzdHJpbmq
> dGVzdCBzdHJpbmr > dGVzdCBzdHJpbmr
>
> All decode to the same 11 bytes (116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 106). > All decode to the same 11 bytes (116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 106).
As you can see by the above 4 examples, they all decode to the same exact 11 bytes. So just changing one or two As you can see by the above 4 examples, they all decode to the same exact 11 bytes. So just changing one or two
characters at the end of a Base64 string may not work and can often result in an invalid test. characters at the end of a Base64 string may not work and can often result in an invalid test.
<a name="base64-invalid-characters"></a> <a name="base64-invalid-characters"></a>
##### Adding Invalid Characters ##### Adding Invalid Characters
JJWT's default Base64/Base64URL decoders automatically ignore illegal Base64 characters located in the beginning and JJWT's default Base64/Base64URL decoders automatically ignore illegal Base64 characters located in the beginning and
@ -1654,8 +1585,8 @@ byte array is changed, then yes, JJWT's cryptographic assertions will definitely
To help understand JJWT's approach, we have to remember why signatures exist. From our documentation above on To help understand JJWT's approach, we have to remember why signatures exist. From our documentation above on
[signing JWTs](#jws): [signing JWTs](#jws):
> - guarantees it was created by someone we know (it is authentic), as well as > * guarantees it was created by someone we know (it is authentic), as well as
> - guarantees that no-one has manipulated or changed it after it was created (its integrity is maintained). > * guarantees that no-one has manipulated or changed it after it was created (its integrity is maintained).
Just prepending or appending invalid text to try to 'trick' the algorithm doesn't change the integrity of the Just prepending or appending invalid text to try to 'trick' the algorithm doesn't change the integrity of the
underlying claims or signature byte arrays, nor the authenticity of the claims byte array, because those byte underlying claims or signature byte arrays, nor the authenticity of the claims byte array, because those byte
@ -1665,7 +1596,6 @@ Please see [JJWT Issue #518](https://github.com/jwtk/jjwt/issues/518) and its re
information. information.
<a name="base64-custom"></a> <a name="base64-custom"></a>
### Custom Base64 ### Custom Base64
If for some reason you want to specify your own Base64Url encoder and decoder, you can use the `JwtBuilder` If for some reason you want to specify your own Base64Url encoder and decoder, you can use the `JwtBuilder`
@ -1707,7 +1637,6 @@ Jwts.parserBuilder()
Maintained by Les Hazlewood &amp; [Okta](https://okta.com/) Maintained by Les Hazlewood &amp; [Okta](https://okta.com/)
<a name="license"></a> <a name="license"></a>
## License ## License
This project is open-source via the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). This project is open-source via the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0).