Create and Validate JWT Token in Java using JJWT

1. JWT Token Overview

JSON Web Token (JWT) is an open standard defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens signs using public/private key pairs, the signature also certifies that only the party holding the private key is the one signed it.

1.1 What is JSON Web Token (JWT) Structure?

JWT tokens consist of 3 parts separated by a period ( . ).
These parts are:

  1. Header
  2. Payload
  3. Signature

The JWT typically looks like:

aaaa.bbbb.cccc
Code language: CSS (css)

2. Setting up JJWT library

To start using JJWT library for JWT generation, add following dependencies in your build.gradle file.

compile 'io.jsonwebtoken:jjwt-api:0.11.1' runtime 'io.jsonwebtoken:jjwt-impl:0.11.1' runtime 'io.jsonwebtoken:jjwt-jackson:0.11.1'
Code language: Gradle (gradle)

Alternatively if using Maven, add following dependencies in pom.xml

<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.1</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.1</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.1</version> <scope>runtime</scope> </dependency>
Code language: HTML, XML (xml)

3. Create JWT Token

Jwts.builder() is used to create a JWT token. We can specify claims, subject and other JWT attribute.

import io.jsonwebtoken.Jwts; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.UUID; //... String jwtToken = Jwts.builder() .claim("name", "Jane Doe") .claim("email", "jane@example.com") .setSubject("jane") .setId(UUID.randomUUID().toString()) .setIssuedAt(Date.from(now)) .setExpiration(Date.from(now.plus(5l, ChronoUnit.MINUTES))) .compact();
Code language: Java (java)

Generated JWT Token:

eyJhbGciOiJub25lIn0.eyJuYW1lIjoiSmFuZSBEb2UiLCJlbWFpbCI6ImphbmVAZXhhbXBsZS5jb20iLCJzdWIiOiJqYW5lIiwianRpIjoiNDVmNGRiNTEtYTRlYy00YjYzLWJhMDgtNTE3MDJjYTI1MmEzIiwiaWF0IjoxNTkwNTc3NTY0LCJleHAiOjE1OTA1Nzc4NjR9.
Code language: Bash (bash)

The above code to generate JWT is pretty self-explanatory however let’s check step by step how are we generating JWT token:

  1. Add claims name and email with value Jane Doe and jane@example.com respectively
  2. Add subject in JWT token with value jane
  3. Set Id for the JWT token using randomly generate GUID
  4. Set issued at to current time
  5. Set expiration to current time plus 5 minutes. So the JWT is valid for only 5 minutes

The JWT generated above is not signed (Check algorithm alg attribute in the header). We have just encoded the claims in JSON format. If using JWT for authentication or authorization it is advisable to Sign the JWT, so it can be verified.

4. Validate/Parse JWT Token

To validate or parse the JWT token, Jwts.parserBuilder() method is used.

import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Claims; //... Jws<Claims> jwt = Jwts.parserBuilder() .setSigningKey(...) .build() .parseClaimsJws(jwt);
Code language: Java (java)

While parsing the JWT token we need to pass Signing key to verify the JWT signature. Let us see how to sign the JWT token using different algorithms.

5. Create and Validate JWT Token Signed using HMAC Secret

The simplest way of creating a signed JWT token is by using HMAC secret. HMAC stands for hash-based message authentication code and is cryptographic hash function. It is used to simultaneously verify both the data integrity and the authenticity of a token.

5.1 Create JWT Token signed with HMAC

To create JWT token signed with HMAC shared secret, we need to specify signature using .signWith() method.

import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Base64; //... // Key is hardcoded here for simplicity. // Ideally this will get loaded from env configuration/secret vault String secret = "asdfSFS34wfsdfsdfSDSD32dfsddDDerQSNCK34SOWEK5354fdgdf4"; Key hmacKey = new SecretKeySpec(Base64.getDecoder().decode(secret), SignatureAlgorithm.HS256.getJcaName()); Instant now = Instant.now(); String jwtToken = Jwts.builder() .claim("name", "Jane Doe") .claim("email", "jane@example.com") .setSubject("jane") .setId(UUID.randomUUID().toString()) .setIssuedAt(Date.from(now)) .setExpiration(Date.from(now.plus(5l, ChronoUnit.MINUTES))) .signWith(hmacKey) .compact();
Code language: Java (java)

Generated JWT Token:

eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSmFuZSBEb2UiLCJlbWFpbCI6ImphbmVAZXhhbXBsZS5jb20iLCJzdWIiOiJqYW5lIiwianRpIjoiYjMwMmU5NmUtODg2OS00NTJkLTg1ZjMtZGZjNDQzNTY3ZGUwIiwiaWF0IjoxNTkwNjE4NjI2LCJleHAiOjE1OTA2MTg5MjZ9.LJaar-3rnb3iuRXJBYK024l1PtEu3ay5ds24Q0bkf7w
Code language: Bash (bash)

5.2 Validate/Parse JWT Token signed with HMAC

To validate/parse the JWT token generated using HMAC shared secret, the same steps can be applied. We need to use setSigningKey() method to set the key before we parse the JWT token.

public static Jws<Claims> parseJwt(String jwtString) { String secret = "asdfSFS34wfsdfsdfSDSD32dfsddDDerQSNCK34SOWEK5354fdgdf4"; Key hmacKey = new SecretKeySpec(Base64.getDecoder().decode(secret), SignatureAlgorithm.HS256.getJcaName()); Jws<Claims> jwt = Jwts.parserBuilder() .setSigningKey(hmacKey) .build() .parseClaimsJws(jwtString); return jwt; }
Code language: Java (java)

Output:

{name=Jane Doe, email=jane@example.com, sub=jane, jti=21da0729-577a-4d76-9c09-0ee11dcc2fdb, iat=1590619729, exp=1590620029}
Code language: JSON / JSON with Comments (json)

If the JWT token expires (exp claim value is less than current system time), the parseClaimsJws() method will throw SignatureException.

Exception in thread "main" io.jsonwebtoken.security.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:411) at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:541) at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:601) at io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173) at net.viralpatel.jwt.JWTGenerator.parseJwt(JWTGenerator.java:32) at net.viralpatel.jwt.JWTGenerator.main(JWTGenerator.java:20)
Code language: Shell Session (shell)

6. Create and Validate JWT Token Signed using RSA Private Key

When using JWT token for microservice authentication/authorization it is advisable to sign with RSA Private/Public Keys instead of using Shared HMAC Secret. The token is generated and signed by a central authority (usually an Authorization Server) and each microservice can validate the JWT token using the Public Key exposed from Authorization Server.

Before we see how to generate JWT token with Private/Public key, let us see how to generate a Private and Public RSA Key pairs.

6.1 Generate Private and Public RSA Key

Generate an RSA private key, of size 2048, and output it to a file named key.pem:

$ openssl genrsa -out key.pem 2048 Generating RSA private key, 2048 bit long modulus ..........+++ ..........................................................................+++ e is 65537 (0x10001)
Code language: Bash (bash)

Extract the public key from the key pair, which can be used in a certificate:

$ openssl rsa -in key.pem -outform PEM -pubout -out public.pem writing RSA key
Code language: Bash (bash)

The key.pem file contains the private key generated using RSA and public.pem file contains public key.

6.2 Create JWT Token signed with RSA

Following code snippets shows how to generate JWT Token Signed using RSA.

import io.jsonwebtoken.Jwts; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.Date; import java.util.UUID; // ... public static String createJwtSignedHMAC() throws InvalidKeySpecException, NoSuchAlgorithmException { PrivateKey privateKey = getPrivateKey(); Instant now = Instant.now(); String jwtToken = Jwts.builder() .claim("name", "Jane Doe") .claim("email", "jane@example.com") .setSubject("jane") .setId(UUID.randomUUID().toString()) .setIssuedAt(Date.from(now)) .setExpiration(Date.from(now.plus(5l, ChronoUnit.MINUTES))) .signWith(privateKey) .compact(); return jwtToken; } private static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException { String rsaPrivateKey = "-----BEGIN PRIVATE KEY-----" + "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDK7c0HtOvefMRM" + "s1tkdiJm+A16Df85lQlmXjQvMHNgY4P/znvl4kRON9DdBdo3K81OG7pR/0H9XvdB" + "TEojj/6vCVuMDeeIiBrgx0OJjhv0r8oUD4d52+1kXXITaniyZcbJ08s4sF7fUSCu" + "IZOhvvwQTd/tIwXGU1qqfg+bsomQ6h2czPSKXAux54vUiRO2IWf/Y6twyk8cy1PH" + "IOfelCVUJ4kzmP+CsOH7Rh3JMwZ0Mc4GAzndWpKwNXKjVM20/bKE9FgIiIjzmEQd" + "VpSdUz2MbAKM1kskdaHXQyuaHoHfPwESYuEwBld4vh9AGMF3jYMu8ggnAzVRIoWG" + "Mr5eCE2tAgMBAAECggEBAKBPXiKRdahMzlJ9elyRyrmnihX7Cr41k7hwAS+qSetC" + "kpu6RjykFCvqgjCpF+tvyf/DfdybF0mPBStrlkIj1iH29YBd16QPSZR7NkprnoAd" + "gzl3zyGgcRhRjfXyrajZKEJ281s0Ua5/i56kXdlwY/aJXrYabcxwOvbnIXNxhqWY" + "NSejZn75fcacSyvaueRO6NqxmCTBG2IO4FDc/xGzsyFKIOVYS+B4o/ktUOlU3Kbf" + "vwtz7U5GAh9mpFF+Dkr77Kv3i2aQUonja6is7X3JlA93dPu4JDWK8jrhgdZqY9p9" + "Q8odbKYUaBV8Z8CnNgz2zaNQinshzwOeGfFlsd6H7SECgYEA7ScsDCL7omoXj4lV" + "Mt9RkWp6wQ8WDu5M+OCDrcM1/lfyta2wf7+9hv7iDb+FwQnWO3W7eFngYUTwSw5x" + "YP2uvOL5qbe7YntKI4Q9gHgUd4XdRJJSIdcoY9/d1pavkYwOGk7KsUrmSeoJJ2Jg" + "54ypVzZlVRkcHjuwiiXKvHwj2+UCgYEA2w5YvWSujExREmue0BOXtypOPgxuolZY" + "pS5LnuAr4rvrZakE8I4sdYjh0yLZ6qXJHzVlxW3DhTqhcrhTLhd54YDogy2IT2ff" + "0GzAV0kX+nz+mRhw0/u+Yw6h0QuzH9Q04Wg3T/u/K9+rG335j/RU1Tnh7nxetfGb" + "EwJ1oOqcXikCgYEAqBAWmxM/mL3urH36ru6r842uKJr0WuhuDAGvz7iDzxesnSvV" + "5PKQ8dY3hN6xfzflZoXssUGgTc55K/e0SbP93UZNAAWA+i29QKY6n4x5lKp9QFch" + "dXHw4baIk8Z97Xt/kw07f6FAyijdC9ggLHf2miOmdEQzNQm/9mcJ4cFn+DECgYEA" + "gvOepQntNr3gsUxY0jcEOWE3COzRroZD0+tLFZ0ZXx/L5ygVZeD4PwMnTNrGvvmA" + "tAFt54pomdqk7Tm3sBQkrmQrm0+67w0/xQ9eJE/z37CdWtQ7jt4twHXc0mVWHa70" + "NdPhTRVIAWhil7rFWANOO3Gw2KrMy6O1erW7sAjQlZECgYBmjXWzgasT7JcHrP72" + "fqrEx4cg/jQFNlqODNb515tfXSBBoAFiaxWJK3Uh/60/I6cFL/Qoner4trNDWSNo" + "YENBqXLZnWGfIo0vAIgniJ6OD67+1hEQtbenhSfeE8Hou2BnFOTajUxmYgGm3+hx" + "h8TPOvfHATdiwIm7Qu76gHhpzQ==" + "-----END PRIVATE KEY-----"; rsaPrivateKey = rsaPrivateKey.replace("-----BEGIN PRIVATE KEY-----", ""); rsaPrivateKey = rsaPrivateKey.replace("-----END PRIVATE KEY-----", ""); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rsaPrivateKey)); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey privKey = kf.generatePrivate(keySpec); return privKey; }
Code language: Java (java)

Generated JWT Token:

eyJhbGciOiJSUzI1NiJ9.eyJuYW1lIjoiSmFuZSBEb2UiLCJlbWFpbCI6ImphbmVAZXhhbXBsZS5jb20iLCJzdWIiOiJqYW5lIiwianRpIjoiNGJiMDU4YzUtZDZhNi00NGNiLWEwOWYtZmM5MjdiNjcwYTJiIiwiaWF0IjoxNTkwODA1NDM3LCJleHAiOjE1OTA4MDU3Mzd9.HZa6ckOvT73KqmYluW5vww10ESNGKRrcYumDjE6ESctrIkLyXrc_fasjDfMAit5SPgIWv8u2GO2XsZzFhyL1Ar_VfCMGY1m6-2Aq-clci0jfzk-q1_SBB57fBQE6io9lPuZcuGtRHjsSmwUUFwHA_bcvVU1Ka85UnQnYilgPoE0BQG9v1ZJy_hjPSisfQh56flHMqs-W4P3X6zY4Ak_92A8FHaaxWABJhJCY1Hl7OR2kSnbqcJaPRYI064x-p1gIePScPTjuDRjNOoyaA-5QEgvWV2BTW6lWIS9yE6NdzvmOt2Nv_5YN5ESuQcVT6XgxNwscoQu3iG3UiOthU6kz-w
Code language: Bash (bash)

For simplicity the Private Key is hard coded in above example. However, in real production system it will be loaded from environment variable, or a secret vault (Hashicorp Vault or AWS Parameter Store).

In above example the method getPrivateKey() gets the java.security.PrivateKey which is then used in Jwts.builder to sign the JWT token using Private key.

6.3 Validate/Parse JWT Token signed with RSA Private/Public Keys

Next, let us validate and parse the JWT signed using RSA. For that we will need Public Key instance in java.security.PublicKey format which Jwts.parserBuilder will use to validate the JWT.

import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; //.. public static Jws<Claims> parseJwt(String jwtString) throws InvalidKeySpecException, NoSuchAlgorithmException { PublicKey publicKey = getPublicKey(); Jws<Claims> jwt = Jwts.parserBuilder() .setSigningKey(publicKey) .build() .parseClaimsJws(jwtString); return jwt; } private static PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException { String rsaPublicKey = "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyu3NB7Tr3nzETLNbZHYi" + "ZvgNeg3/OZUJZl40LzBzYGOD/8575eJETjfQ3QXaNyvNThu6Uf9B/V73QUxKI4/+" + "rwlbjA3niIga4MdDiY4b9K/KFA+HedvtZF1yE2p4smXGydPLOLBe31EgriGTob78" + "EE3f7SMFxlNaqn4Pm7KJkOodnMz0ilwLseeL1IkTtiFn/2OrcMpPHMtTxyDn3pQl" + "VCeJM5j/grDh+0YdyTMGdDHOBgM53VqSsDVyo1TNtP2yhPRYCIiI85hEHVaUnVM9" + "jGwCjNZLJHWh10Mrmh6B3z8BEmLhMAZXeL4fQBjBd42DLvIIJwM1USKFhjK+XghN" + "rQIDAQAB" + "-----END PUBLIC KEY-----"; rsaPublicKey = rsaPublicKey.replace("-----BEGIN PUBLIC KEY-----", ""); rsaPublicKey = rsaPublicKey.replace("-----END PUBLIC KEY-----", ""); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(rsaPublicKey)); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey publicKey = kf.generatePublic(keySpec); return publicKey; }
Code language: Java (java)

Output:

{name=Jane Doe, email=jane@example.com, sub=jane, jti=4bb058c5-d6a6-44cb-a09f-fc927b670a2b, iat=1590805437, exp=1590805737}
Code language: JSON / JSON with Comments (json)

In above example, we have hardcoded the Public Key. In a production system, it is usually configured through environment variable or service configuration.

7. Source Code – Generate and Validate JWT Tokens using Java & JJWT

Source code for the Creating and Validating JWT token in Java.

Github – source code

View Comments

  • During calling Jwts.parserBuilder() i am getting io.jsonwebtoken.io.DecodingException: Illegal base64url character: '"' error, i am using the exact sample of this page

  • Pulic key not working
    Exception in thread "main" io.jsonwebtoken.security.InvalidKeyException: JWT standard signing algorithms require either 1) a SecretKey for HMAC-SHA algorithms or 2) a private RSAKey for RSA algorithms or 3) a private ECKey for Elliptic Curve algorithms. The specified key is of type sun.security.rsa.RSAPublicKeyImpl
    at io.jsonwebtoken.SignatureAlgorithm.forSigningKey(SignatureAlgorithm.java:555)

  • private key work after
    openssl.exe pkcs8 -topk8 -inform PEM -in d:\key.pem -out d:\private_key.pem -nocrypt

  • 1) private key work after
    openssl.exe pkcs8 -topk8 -inform PEM -in d:\key.pem -out d:\private_key.pem -nocrypt

    2) Public key not working
    io.jsonwebtoken.security.InvalidKeyException: JWT standard signing algorithms require either 1) a SecretKey for HMAC-SHA algorithms or 2) a private RSAKey for RSA algorithms or 3) a private ECKey for Elliptic Curve algorithms. The specified key is of type sun.security.rsa.RSAPublicKeyImpl

Recent Posts

  • Java

Java URL Encoder/Decoder Example

Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…

4 years ago
  • General

How to Show Multiple Examples in OpenAPI Spec

Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…

4 years ago
  • General

How to Run Local WordPress using Docker

Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…

4 years ago
  • Spring Boot

Spring Boot GraphQL Subscription Realtime API

GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…

4 years ago
  • Spring Boot

Spring Boot DynamoDB Integration Test using Testcontainers

1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…

5 years ago
  • Spring Boot

Spring Boot Webflux DynamoDB Tutorial

Creating REST API in Spring Boot Webflux and AWS DynamoDB. Step by step guide for…

5 years ago