Click here to Skip to main content
15,867,771 members
Articles / Programming Languages / Java

Java JWT Token Tutorial using JJWT Library

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
25 Jul 2018MIT8 min read 35.5K   478   6   2
This article is an introduction on how to use the JJWT library, key stores, private/public keys to encrypt and decrypt the JWT token.

Introduction

In this article, I will present a very simple tutorial on how to create a JWT token, how to encrypt the token, and how to decrypt the token. This article will be simple and fun. Although I am no subject expert, I will show the following:

  • How to create a Java keystore
  • How to extract public key out of the KeyStore file
  • How to create the JWT token
  • How to encrypt the JWT token using the private key in the keystore
  • How to decrypt the encrypted JWT token using the exported public key

You might ask, what would be the purpose of doing this? I have a personal project that will be implemented as a se of micro-services. One of the micro-services would be an authentication service. When a request comes into my micro-service ecosystem, the request will first be authenticated. User credential will be sent to the authentication and authorization service first, which can look up the user in database, and verify the credentials, then send back an encrypted JWT token that contains the user information. The service that is supposed to process the request will decrypt the JWT token and verify the user's authorization before the request can be handled. The authorization check will see what roles the user has and whether the user roles are sufficient for the request to be handled.

In order to do all these, we need a pair of keys -- RSA private/public key pair. The authentication service uses the private key to encrypt the token. The service that handles user request needs to decrypt the token, which can use the public key to do this. In between, we need to find a way to create the JWT token unencrypted. All these will be explained in this article.

Create Java KeyStore

A Java KeyStore file is a repository of certificates (public key) and corresponding private keys. They can be used for SSL transportation. One example you might notice about the Java KeyStore file is that it can be used by J2EE application containers like Jetty, Glassfish, TomCat through some XML configuration. And you will have https enabled for these application containers.

For this tutorial, all we need to know is how to create one Java KeyStore file with just one RSA certificate and its corresponding private key. To create a new Java KeyStore file, here is the command:

keytool -genkey -alias hanbotest -keyalg RSA -keystore hanbotest.jks -keysize 2048

What the above command does is to create a keystore with one RSA key in it. The alias of the key is called "hanbotest". The algorithm that is used to create the key is RSA. The size of the key is 2048 bits. And the default format of the KeyStore is JKS.

Once you type in the command, hit enter, it will prompt you for the following information:

  1. The password for the keystore, I use "123test321".
  2. Again for the same password.
  3. First name and last name, I entered "Han Bo".
  4. Name of the organization unit, I entered "Hanbo Enterprise".
  5. Name of the organization, I entered "Hanbo Enterprise" again.
  6. Name of the city, I entered "Chicago".
  7. Name of the state or province, I entered "IL".
  8. Two characters of the country, I entered "US".
  9. Last one is just "[no]", I type in "yes" and hit enter.
  10. The last last thing is the password of the certificate and private key. I keep it the same as the password of the keystore. Just hit enter.
  11. Look into the destination directory, you will see the KeyStore file.

Don't worry about the information you entered. This is a mock certificate and used for testing only. And I have included this mock KeyStore as part of the sample source code. Once you got it, you can swap out with your own KeyStore file and play with the source code.

Export the Public Key/Certificate

Now that you have a KeyStore file. The next step is to export the public key as a separated entity. Private key is something to be protected, and public key can be shared to other parties. To extract public key, here is the command:

keytool -export -keystore hanbotest.jks -alias hanbotest -file hanbotest.cer

What this command does is specify the jks file, the alias of the key, and the output file which is called "hanbotest.cer". When it is typed in, and hit enter, it will prompt for password. Enter password "123test321". The cer file will be created successfully.

Now they can be used for the JWT token encryption and decryption.

Encrypting and Decrypting JWT Token

I wrote a simple Java console app that demonstrates the encryption of the JWT token using the private key. It will be a string once encrypted. Then I take the string, and use the public key to decrypt the encrypted token.

The POM File

Since this is a simple Java console program, the POM file is pretty straight forward. The most important part is the dependencies. Here they are:

XML
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>${jackson.version}</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>${jackson.version}</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>${jackson.version}</version>
</dependency>

The version of the jackson libraries is 2.7.3. And I defined it in a separated section in the same pom file, like this:

XML
<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <jackson.version>2.7.3</jackson.version>
</properties>

One more thing about this, in the official JJWT library tutorial, it said there is no need to include any other dependencies. This is not true. I had to include the three jackson jar files. or the application will not work.

The Main Program

The main program is super simple. It is one class, with a main entry, and a static helper method. All it does is create the JWT token, encrypt, then decrypt. The purpose is to demonstrate how JWT token is created and protected.

I use the JJWT library. And it is very easy to create the JWT token. Here is the code:

Java
String compactJws = Jwts.builder()
   .setSubject("Joe")
   .setAudience("testAudienceId")
   .setExpiration(dateExp)
   .setIssuedAt(dateNow)
   .setId("testuserid")
   .signWith(SignatureAlgorithm.RS512, key)
   .compact();
   
System.out.println(compactJws);

There are two things that you must be aware of:

  • setExpiration() should set a Date object that is later than the Date object for the setIssuedAt().
  • signWith() uses the private key to sign the JWT token. This is where we should use the private key.

The question is, how do we load the private key. Turned out, this is not hard to do:

Java
String jksPassword = "123test321";
         
KeyStore ks  = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(new FileInputStream("C:\\DevJunk\\jwt\\keystore\\hanbotest.jks"), jksPassword.toCharArray());
Key key = ks.getKey("hanbotest", jksPassword.toCharArray());

The first line defines the string that represents the password. The second line gets a KeyStore instance, and I specify the type used is the default type. There are many different types of JKS which you can use. Besides the default type which Java supports out of box, there is also the PKCS12 format, and many others. In this case, I am just using the default type. The third line loads the KeyStore from the JKS file. In order to load, one must supply the password associated with the JKS file. The last line extracts the private key out of the KeyStore object. The object named "key" is the private key used by the signWith() in previous code sample.

If you run the program with just these two pieces of code, you will see something like the following output:

eyJhbGciOiJSUzUxMiJ9.eyJzdWIiOiJKb2UiLCJhdWQiOiJ0ZXN0QXVkaWVuY2VJZCIsImV4cCI6MTUzMjM5OTI4NCwiaWF0I
joxNTMyMTQwMDg0LCJqdGkiOiJ0ZXN0dXNlcmlkIn0.L9w54tGTWGa720T1TqCf7T5c-JnT0RI1kyK1vUmuuEfcX8wmwWnvZk2
Ybm4eEyXZHP5tBLWHtCVdZOPqLwdjDzZpr10Q5AmjasTE9FJqnzQVLvd5cJOKFK9VAXAc_hA4ZxP6mTM802vssNGfuVseqFg1a
r08uD-jbRRnN8DqegzEV9z0yJC-TNuUUkQBo5Rj2GKVKWyTi5MkPPL2aPsY7JExDj7QraYvaKl57XBwT2XRGefOaA0mXuCxcJj
xZv638LupQl9c1HcS7qTOSlV7V-h-QLqanDXZkKt-xhwR1hilEPCYjVyGPSDMWVYlLtxSNWXvFji1TI2baFD7i9fpOQ

The string contains three encrypted sections. They are separated by two ".". The sections are token header, body, and signature. The last part can be used to verify that the JWT token is generated by a legitimate private key.

The next part of the program is to decrypt the string back into a JWT token object. This is done using the public key. In order to do this, first we need to load the public key. This is the only hard part of this tutorial. I wrote a very simple helper method:

Java
public static PublicKey loadPublicKey(String filename)
   throws Exception
{
   CertificateFactory cf = CertificateFactory.getInstance("X.509");
   Certificate cert = cf.generateCertificate(new FileInputStream(filename));
   PublicKey retVal = cert.getPublicKey();
   return retVal;
}

This helper method takes a full file path to the cer file I just created, and return an object of PublicKey. The method has 4 lines:

  1. First, create a CertificateFactory object. The type of the certificate is specified by a string. This is probably the hardest of all. The type is actually "X.509".
  2. The next line takes a FileInputStream of the actual cer file and use the CertificateFile object to create a Certificate object.
  3. The third line extracts the PublicKey object out of the Certificate object.
  4. The last line returns the PublicKey object back to the caller.

Once a PublicKey object is available, we can take the encrypted string and use JJWT libraries to attempt decoding it. Here is how it is done:

Java
PublicKey publicKey = loadPublicKey("C:\\DevJunk\\jwt\\keystore\\hanbotest.cer");

Jws<Claims> x = Jwts.parser()
   .setSigningKey(publicKey)
   .parseClaimsJws(compactJws);

There are only two lines in this. The first one uses the helper method to get the public key from the cer file. The second one uses the JJWT library methods to decrypt the JWT token. The returning object is of type Jws<Claims>. compactJws is the string that holds the encrypted JWT token.

Once the token is decrypted, there will be three different parts.

  • JWT token's header
  • JWT token's body - this is where the user credentials are stored
  • JWT token signature

If a token can be decrypted correctly, then all we care about is the body portion of the token. Here is how I output and see if it is decrypted correctly:

Java
String id = x.getBody().getId();
System.out.println("id: " + id);
System.out.println("audience: " + x.getBody().getAudience());
System.out.println("audience: " + x.getBody().getSubject());

The output of the id, the audience and the subject are the same as the input value I have used when I created the JWT token.

Points of Interest

As promised, I have kept this article short and easy, simple and fun. All I have done in this article are:

  • Create a JKS KeyStore with just one RSA key.
  • Export the public certificate from the JKS KeyStore file out as a cer file.
  • A sample program that creates a JWT token, use the private key to encrypt it and use the public key (the exported certificate) to decrypt the token.

It is not very much, but it can be extended and incorporated into a real-world web application, where a service will be dedicated to do authentication and creating the JWT tokens, while other services can consume and verify the JWT token. It just made development of web based application a lot more fun.

History

  • 07/21/2018 - Initial draft

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Team Leader The Judge Group
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThe terminology is a bit wrong Pin
alexrait11-Feb-24 2:58
alexrait11-Feb-24 2:58 
Questionbest explanation Pin
Member 1465588614-Nov-19 21:11
Member 1465588614-Nov-19 21:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.