Fusionauth jwt
A simple to use Java JWT Library. Verify, Sign, Encode, Decode all day.
FusionAuth JWT is intended to be fast and easy to use. FusionAuth JWT has a single external dependency on Jackson, no Bouncy Castle, Apache Commons or Guava. The project is written primarily in Java, distributed under the Apache License 2.0 license, first published in 2016. Key topics include: der, ed25519, ed448, eddsa, elliptic.
FusionAuth JWT

FusionAuth JWT is intended to be fast and easy to use. FusionAuth JWT has a single external dependency on Jackson, no Bouncy Castle, Apache Commons or Guava.
Security disclosures
If you find a vulnerability or other security related bug, please report the vulnerability here before opening a GitHub issue. This will allow us to assess the disclosure and prepare a fix prior to a public disclosure.
We are very interested in compensating anyone that can identify a security related bug or vulnerability and properly disclose it to us.
Features
- JWT signing using the following algorithms
HS256,HS384,HS512,RS256,RS384,RS512,ES256,ES384,ES512,PS256,PS384,PS512,Ed25519,Ed448- In order to use the OpenID Connect hashing functions for
at_hashorc_hashwith theEd448algorithm, you must register a provider to add support for theSHAKE256message digest, such as BouncyCastle as this algorithm is not provided in the default JCA. - When using
Ed25519orEd448, thealgJWT header and the JWKalgproperty will be equal to the algorithm name. The legacyEdDSAvalue has been deprecated in JOSE in favor of the fully-specified algorithm namesEd25519andEd448. In practice this means that this library will be unable to accept a JWT using theEdDSAvalue for thealgin the JWT header.
- In order to use the OpenID Connect hashing functions for
- Support for Bouncy Castle JCE or other third party providers.
- PEM decoding / encoding
- Decode PEM files to PrivateKey or PublicKey
- Decode private EC keys un-encapsulated in PKCS#8, returned PEM will be in PKCS#8 form.
- Both public and private keys will be returned when encoded in the private PEM
- Encode PrivateKey or PublicKey to PEM
- Decode PEM files to PrivateKey or PublicKey
- JSON Web Key
- Build JWK from Private Key
- Build JWK from Public Key
- Build JWK from PEM
- Parse public keys from a JSON Web Key
- Retrieve JWK from JWKS endpoints
- Helpers
- Generate RSA Key Pairs in
2048,3072or4096bit sizes - Generate RSA PSS Key Pairs in
2048,3072or4096bit sizes - Generate EC Key Pairs in
256,384and521bit sizes - Generate EdDSA Key Pairs for
Ed2559andEd448curves - Generate
x5tandx5t#256values from X.509 Certificates - Generate JWK thumbprint using
SHA-1orSHA-256 - Generate ideal HMAC secret lengths for
SHA-256,SHA-384andSHA-512 - Generate the
at_hashandc_hashclaims for OpenID Connect
- Generate RSA Key Pairs in
Get it
Maven
xml<dependency> <groupId>io.fusionauth</groupId> <artifactId>fusionauth-jwt</artifactId> <version>6.0.0</version> </dependency>
Gradle
groovyimplementation 'io.fusionauth:fusionauth-jwt:6.0.0'
Gradle Kotlin
kotlinimplementation("io.fusionauth:fusionauth-jwt:6.0.0")
Savant
groovydependency(id: "io.fusionauth:fusionauth-jwt:6.0.0")
For others see https://search.maven.org.
Example Code:
JWT Signing and Verifying
Sign and encode a JWT using HMAC
java// Build an HMAC signer using a SHA-256 hash Signer signer = HMACSigner.newSHA256Signer("too many secrets"); // Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp) JWT jwt = new JWT().setIssuer("www.acme.com") .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC)) .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3") .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60)); // Sign and encode the JWT to a JSON string representation String encodedJWT = JWT.getEncoder().encode(jwt, signer);
A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.
java// Build an HMAC signer using a SHA-384 hash Signer signer384 = HMACSigner.newSHA384Signer("too many secrets"); // Build an HMAC signer using a SHA-512 hash Signer signer512 = HMACSigner.newSHA512Signer("too many secrets");
Verify and decode a JWT using HMAC
java// Build an HMC verifier using the same secret that was used to sign the JWT Verifier verifier = HMACVerifier.newVerifier("too many secrets"); // Verify and decode the encoded string JWT to a rich object JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier); // Assert the subject of the JWT is as expected assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");
Sign and encode a JWT using RSA
java// Build an RSA signer using a SHA-256 hash. A signer may also be built using the PrivateKey object. Signer signer = RSASigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get("private_key.pem")))); // Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp) JWT jwt = new JWT().setIssuer("www.acme.com") .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC)) .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3") .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60)); // Sign and encode the JWT to a JSON string representation String encodedJWT = JWT.getEncoder().encode(jwt, signer);
A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.
java// Build an RSA signer using a SHA-384 hash Signer signer = RSASigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get("private_key.pem")))); // Build an RSA signer using a SHA5124 hash Signer signer = RSASigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));
Verify and decode a JWT using RSA
java// Build an RSA verifier using an RSA Public Key. A verifier may also be built using the PublicKey object. Verifier verifier = RSAVerifier.newVerifier(Paths.get("public_key.pem")); // Verify and decode the encoded string JWT to a rich object JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier); // Assert the subject of the JWT is as expected assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");
Sign and encode a JWT using EC
java// Build an EC signer using a SHA-256 hash. A signer may also be built using the PrivateKey object. Signer signer = ECSigner.newSHA256Signer(new String(Files.readAllBytes(Paths.get("private_key.pem")))); // Build a new JWT with an issuer(iss), issued at(iat), subject(sub) and expiration(exp) JWT jwt = new JWT().setIssuer("www.acme.com") .setIssuedAt(ZonedDateTime.now(ZoneOffset.UTC)) .setSubject("f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3") .setExpiration(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(60)); // Sign and encode the JWT to a JSON string representation String encodedJWT = JWT.getEncoder().encode(jwt, signer);
A higher strength hash can be used by changing the signer. The encoding and decoding steps are not affected.
java// Build an EC signer using a SHA-384 hash Signer signer = ECSigner.newSHA384Signer(new String(Files.readAllBytes(Paths.get("private_key.pem")))); // Build an EC signer using a SHA-512 hash Signer signer = ECSigner.newSHA512Signer(new String(Files.readAllBytes(Paths.get("private_key.pem"))));
Verify and decode a JWT using EC
java// Build an EC verifier using an EC Public Key. A verifier may also be built using the PublicKey object. Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem")); // Verify and decode the encoded string JWT to a rich object JWT jwt = JWT.getDecoder().decode(encodedJWT, verifier); // Assert the subject of the JWT is as expected assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");
Verify a JWT adjusting for Clock Skew
java// Build an EC verifier using an EC Public Key Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem")); // Verify and decode the encoded string JWT to a rich object and allow up to 60 seconds // of clock skew when asserting the 'exp' and 'nbf' claims if they exist. JWT jwt = JWT.getDecoder().withClockSkew(60).decode(encodedJWT, verifier); // Assert the subject of the JWT is as expected assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");
Verify an expired JWT by going back in time
In a scenario where you may have a hard coded JWT in a test case that you wish to validate, you may use the time machine JWT decoder. Ideally you would not hard code JWTs in your tests and instead generate a new one each time so that the JWT would pass the expiration check. If this is not possible, this option is provided.
java// Build an EC verifier using an EC Public Key Verifier verifier = ECVerifier.newVerifier(Paths.get("public_key.pem")); // Using the time machine decoder, you may adjust 'now' to any point in the past, or future. // Note, this is only provided for testing, and should not be used in production. ZonedDateTime thePast = ZonedDateTime.of(2019, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC) JWT jwt = JWT.getTimeMachineDecoder(thePast).decode(encodedJWT, verifier); // Assert the subject of the JWT is as expected assertEquals(jwt.subject, "f1e33ab3-027f-47c5-bb07-8dd8ab37a2d3");
Enable a third party JCE provider sucha s Bouncy Castle
Once you have enabled an additional provider no change should be necessary to your code.
java// Insert the provider ahead of the JCA. Security.insertProviderAt(new BouncyCastleFipsProvider(), 1);
JSON Web Keys
Retrieve JSON Web Keys from a JWKS endpoint
java// Retrieve JSON Web Keys using a known JWKS endpoint // - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection. List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromJWKS("https://www.googleapis.com/oauth2/v3/certs"); // Retrieve JSON Web Keys using a well known OpenID Connect configuration endpoint // - You may optionally provide a HttpURLConnection to this method instead of a string if you want to build your own connection. List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromWellKnownConfiguration("https://accounts.google.com/.well-known/openid-configuration"); // Retrieve JSON Web Keys using an OpenID Connect issuer endpoint List<JSONWebKey> keys = JSONWebKeySetHelper.retrieveKeysFromIssuer("https://accounts.google.com");
Convert a Public Key to JWK
javaJSONWebKey jwk = JSONWebKey.build(publicKey); String json = jwk.toJSON();
json{ "e": "AQAB", "kty": "RSA", "n": "Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U", "use": "sig" }
Extract the Public Key from a JWK
json{ "e": "AQAB", "kty": "RSA", "n": "Auchby3lZKHbiAZrTkJh79hJvgC3W7STSS4y6UZEhhxx3m3W2hD8qCyw6BEyrciPpwou-vmeDN7qBSk2QKqTTjlg5Pkf8O4z8d9HAlBTUDg4p98qLFOF2EFWWTiFbQwAP2qODOIv9WCAM2rkXEPwGiF962XAoOwiSmldeDu7Uo5A-bnTi0z3oNu4qm_48kv90o9CMiELszE9jsfoH32WE71HDqhsRjVNddDJ81e5zxBN8UEmaR-gmWqa63laON2KANPugJP7PrYJ_PC9ilQfV3F1rDpqbvlFQkshohJ39VrVpEtSRmJ12nqTFuspXLApekOyic3J9jo6ZI7o3IdQmy3bpnJIT_U", "use": "sig" }
javaString json = { ... example above ... } byte[] bytes = json.getBytes(StandardCharsets.UTF_8); JSONWebKey jwk = Mapper.deserialize(bytes, JSONWebKey.class); PublicKey publicKey = JSONWebKey.parse(jwk);
Convert a Private Key to JWK
javaJSONWebKey jwk = JSONWebKey.build(privateKey); String json = jwk.toJSON();
json{ "p": "9dy6wUxA0eOHopUP-E5QjDzuW8rXdaQMR566oDJ1qL0iD0koQAB9X3hboB-2Rru0aATu6WDW-jd4mgtYnXO8ow", "kty": "RSA", "q": "6Nfc6c8meTRkVRAHCF24LB5GLfsjoMB0tOeEO9w9Ous1a4o-D24bAePMUImAp3woFoNDRfWtlNktOqLel5Pjew", "d": "C0G3QGI6OQ6tvbCNYGCqq043YI_8MiBl7C5dqbGZmx1ewdJBhMNJPStuckhskURaDwk4-8VBW9SlvcfSJJrnZhgFMjOYSSsBtPGBIMIdM5eSKbenCCjO8Tg0BUh_xa3CHST1W4RQ5rFXadZ9AeNtaGcWj2acmXNO3DVETXAX3x0", "e": "AQAB", "use": "sig", "qi": "XLE5O360x-MhsdFXx8Vwz4304-MJg-oGSJXCK_ZWYOB_FGXFRTfebxCsSYi0YwJo-oNu96bvZCuMplzRI1liZw", "dp": "32QGgDmjr9GX3N6p2wh1YWa_gMHmUSqUScLseUA_7eijeNYU70pCoCtAvVXzDYPhoJ3S4lQuIL2kI_tpMe8GFw", "dq": "21tJjqeN-k-mWhCwX2xTbpTSzsyy4uWMzUTy6aXxtUkTWY2yK70yClS-Df2MS70G0za0MPtjnUAAgSYhB7HWcw", "n": "359ZykLITko_McOOKAtpJRVkjS5itwZxzjQidW2X6tBEOYCH4LZbwfj8fGGvlUtzpyuwnYuIlNX8TvZLTenOk45pphXr5PMCMKi7YZgkhd6_t_oeHnXY-4bnDLF1r9OUFKwj6C-mFFM-woKc-62tuK6QJiuc-5bFfn9wRL15K1E" }
Add a custom property to a JWK
javaJSONWebKey jwk = JSONWebKey.build(privateKey) .add("boom", "goes the dynamite") .add("more", "cowbell"); String json = jwk.toJSON();
json{ "alg" : "ES256", "boom" : "goes the dynamite", "crv" : "P-256", "kty" : "EC", "more" : "cowbell", "use" : "sig", "x" : "NIWpsIea0qzB22S0utDG8dGFYqEInv9C7ZgZuKtwjno", "y" : "iVFFtTgiInz_fjh-n1YqbibnUb2vtBZFs3wPpQw3mc0" }
Building
Maven
bash$ mvn install
Savant
bash$ sb int
Note: If you do not yet have Savant build tool installed, use the following instructions.
bashmkdir ~/savant cd ~/savant wget http://savant.inversoft.org/org/savantbuild/savant-core/2.0.2/savant-2.0.2.tar.gz tar xvfz savant-2.0.2.tar.gz ln -s ./savant-2.0.2 current export PATH=$PATH:~/savant/current/bin/
For more information, checkout savantbuild.org.
Contributors
Showing top 12 contributors by commit count.
