JJWT aims to be the easiest to use and understand library for creating and verifying JSON Web Tokens (JWTs) and JSON Web Keys (JWKs) on the JVM and Android.
JJWT is a pure Java implementation based exclusively on the JOSE Working Group RFC specifications:
It was created by Les Hazlewood and is supported and maintained by a community of contributors.
JJWT is open source under the terms of the Apache 2.0 License.
- Features
- Community
- What is a JSON Web Token?
- Installation
- Quickstart
- Creating a JWT
- Reading a JWT
- Signed JWTs
- Encrypted JWTs
- JSON Web Keys (JWKs)
- JWK Sets
- Compression
- JSON Support
- Base64 Support
- Examples
- JWT Signed with HMAC
- JWT Signed with RSA
- JWT Signed with ECDSA
- JWT Signed with EdDSA
- JWT Encrypted Directly with a SecretKey
- JWT Encrypted with RSA
- JWT Encrypted with AES Key Wrap
- JWT Encrypted with ECDH-ES
- JWT Encrypted with a Password
- SecretKey JWK
- RSA Public JWK
- RSA Private JWK
- Elliptic Curve Public JWK
- Elliptic Curve Private JWK
- Edwards Elliptic Curve Public JWK
- Edwards Elliptic Curve Private JWK
- Learn More
- Author
- License
-
Fully functional on all Java 7+ JDKs and Android
-
Automatic security best practices and assertions
-
Easy to learn and read API
-
Convenient and readable fluent interfaces, great for IDE auto-completion to write code quickly
-
Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors
-
Stable implementation with almost 1,700 tests and enforced 100% test code coverage. Every single method, statement and 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:
Identifier Signature Algorithm HS256
HMAC using SHA-256
HS384
HMAC using SHA-384
HS512
HMAC using SHA-512
ES256
ECDSA using P-256 and SHA-256
ES384
ECDSA using P-384 and SHA-384
ES512
ECDSA using P-521 and SHA-512
RS256
RSASSA-PKCS-v1_5 using SHA-256
RS384
RSASSA-PKCS-v1_5 using SHA-384
RS512
RSASSA-PKCS-v1_5 using SHA-512
PS256
RSASSA-PSS using SHA-256 and MGF1 with SHA-2561
PS384
RSASSA-PSS using SHA-384 and MGF1 with SHA-3841
PS512
RSASSA-PSS using SHA-512 and MGF1 with SHA-5121
EdDSA
Edwards-curve Digital Signature Algorithm2
1. Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
2. Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
-
Creating, parsing and decrypting encrypted compact JWTs (aka JWEs) with all standard JWE encryption algorithms:
Identifier Encryption Algorithm A128CBC‑HS256
AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm
A192CBC-HS384
AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm
A256CBC-HS512
AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm
A128GCM
AES GCM using 128-bit key1
A192GCM
AES GCM using 192-bit key1
A256GCM
AES GCM using 256-bit key1
1. 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 key1
A192GCMKW
Key wrapping with AES GCM using 192-bit key1
A256GCMKW
Key wrapping with AES GCM using 256-bit key1
PBES2-HS256+A128KW
PBES2 with HMAC SHA-256 and "A128KW" wrapping1
PBES2-HS384+A192KW
PBES2 with HMAC SHA-384 and "A192KW" wrapping1
PBES2‑HS512+A256KW
PBES2 with HMAC SHA-512 and "A256KW" wrapping1
1. 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
TypeJJWT Jwk
TypeSymmetric 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
1OctetPublicJwk
XDH Private Key
XECPrivateKey
1OctetPrivateJwk
EdDSA Public Key
EdECPublicKey
2OctetPublicJwk
EdDSA Private Key
EdECPublicKey
2OctetPrivateJwk
1. Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
2. Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.
-
Convenience enhancements beyond the specification such as
-
Payload compression for any large JWT, not just JWEs
-
Claims assertions (requiring specific values)
-
Claim POJO marshaling and unmarshalling when using a compatible JSON parser (e.g. Jackson)
-
Secure Key generation based on desired JWA algorithms
-
and more…
-
-
Non-compact serialization and parsing.
This feature may be implemented in a future release. Community contributions are welcome!
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.
If the documentation or the API JavaDoc isn’t sufficient, and you either have usability questions or are confused about something, please ask your question here. However:
Please do not create a GitHub issue to ask a question.
We use GitHub Issues to track actionable work that requires changes to JJWT’s design and/or codebase. If you have a usability question, instead please ask your question here, and we can convert that to an issue if necessary.
If a GitHub Issue is created that does not represent actionable work for JJWT’s codebase, it will be promptly closed.
If you do not have a usability question and believe you have a legitimate bug or feature request, please discuss it here FIRST. Please do a quick search first to see if an existing discussion related to yours exist already and join that existing discussion if necesary.
If you feel like you’d like to help fix a bug or implement the new feature yourself, please read the Contributing section next before starting any work.
Simple Pull Requests that fix anything other than JJWT core code (documentation, JavaDoc, typos, test cases, etc) are always appreciated and have a high likelihood of being merged quickly. Please send them!
However, if you want or feel the need to change JJWT’s functionality or core code, please do not issue a pull request without starting a new JJWT discussion 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 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.
So, please create a new JJWT discussion first to discuss, and then we can see easily convert the discussion to an issue and then see if (or how) a PR is warranted. Thank you!
If you would like to help, but don’t know where to start, please visit the Help Wanted Issues page and pick any of the ones there, and we’ll be happy to discuss and answer questions in the issue comments.
If any of those don’t appeal to you, no worries! Any help you would like to offer would be appreciated based on the above caveats concerning contributing pull requests. Feel free to discuss or ask questions first if you’re not sure. :)
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.
A JWT in its simplest form contains two parts:
-
The primary data within the JWT, called the
payload
, and -
A JSON
Object
with name/value pairs that represent metadata about thepayload
and the message itself, called theheader
.
A JWT payload
can be absolutely anything at all - anything that can be represented as a byte array, such as Strings,
images, documents, etc.
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) or encrypted (making it a JWE). 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.
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:
-
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.
-
Remove all unnecessary whitespace in the JSON:
String header = '{"alg":"none"}' String payload = 'The true sign of intelligence is not knowledge but imagination.'
-
Get the UTF-8 bytes and Base64URL-encode each:
String encodedHeader = base64URLEncode( header.getBytes("UTF-8") ) String encodedPayload = base64URLEncode( payload.getBytes("UTF-8") )
-
Join the encoded header and claims with period ('.') characters:
String compact = encodedHeader + '.' + encodedPayload + '.'
The final concatenated compact
JWT String looks like this:
eyJhbGciOiJub25lIn0.VGhlIHRydWUgc2lnbiBvZiBpbnRlbGxpZ2VuY2UgaXMgbm90IGtub3dsZWRnZSBidXQgaW1hZ2luYXRpb24u.
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.
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.
-
Assume we have a JSON
header
and a claimspayload
:header
{ "alg": "HS256" }
payload
{ "sub": "Joe" }
In this case, the
header
indicates that theHS256
(HMAC using SHA-256) algorithm will be used to cryptographically sign the JWT. Also, thepayload
JSON object has a single claim,sub
with valueJoe
.There are a number of standard claims, called Registered Claims, in the specification and
sub
(for 'Subject') is one of them. -
Remove all unnecessary whitespace in both JSON objects:
String header = '{"alg":"HS256"}' String claims = '{"sub":"Joe"}'
-
Get their UTF-8 bytes and Base64URL-encode each:
String encodedHeader = base64URLEncode( header.getBytes("UTF-8") ) String encodedClaims = base64URLEncode( claims.getBytes("UTF-8") )
-
Concatenate the encoded header and claims with a period character '.' delimiter:
String concatenated = encodedHeader + '.' + encodedClaims
-
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:
SecretKey key = getMySecretKey() byte[] signature = hmacSha256( concatenated, key )
-
Because signatures are always byte arrays, Base64URL-encode the signature and join it to the
concatenated
string with a period character '.' delimiter:String compact = concatenated + '.' + base64URLEncode( signature )
And there you have it, the final compact
String looks like this:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4
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.
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?