March 11, 2023

Using PrismJS in Blogger for Code Highlighting

Abstract

For my blog, since I write mostly about technology and specifically software development, I needed a syntax highlighter for styling my source code examples.

I first started using SyntaxHighlighter to style source code. SyntaxHighligher works by add a class to a <pre> tag like this:

<pre class=“brush: java”>

This worked well until I started writing my blog posts using Scrivener. With Scrivener, I write in Markdown and Scrivener compiles to HTML for me. The standard HTML to use for source code is a <pre> tag surrounding a <code> tag like this:

<pre><code class=“java”>

Unfortunately, SyntaxHighligher does not support this HTML so it no longer worked for me.

I then started using Highlight.js. This tool has been working well, but its styling is a little too simple. Plus I really wanted to start having line numbers added to my source code examples and Highlight.js does not support this.

It is time for another change. The purpose of this post is demonstrate how to incorporate the PrismJS syntax highlighting tool into Blogger.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

I did all of the work for this post using the following major technologies. You may be able to do the same thing with different technologies or versions, but no guarantees.

  • Blogger
  • Scrivener 3
  • PrismJS 1.29

Download PrismJS

Visit the PrismJS download page: https://prismjs.com/download.html. On this page you are able to select the languages and plugins you want to include in your prism download. It is tempting to select everything, but, the reality is you will never use some of the languages listed. It only takes a few minutes to go through the language list and select the ones you use most often.

For PrismJS plugins, my primary reason for switching to PrismJS is the Line Numbers plugin. This is an important feature for me. I want to have line numbers added to my source code examples.

Another important plugin is the Autoloader plugin. If you try to style a language you have not previously included in your download, Autoloader will automatically get the styling for that language for you. This is good for occasional use. I would not rely on it all the time. If you start blogging about a new language regularly, re-download PrismJS with that language selected.

What is also nice about the PrismJS download page is that while you are selecting languages and plugins, the URL in your browser is automatically updated to reflect your selections. This means, once you are done selecting all the options you want, save the URL in your your favorite note-taking software (OneNote). Then all you need to do is click on the URL and you don’t have to go through selecting all your languages and plugins again. Very nice!

These are my selections: https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+bash+http+java+javadoc+javadoclike+json+json5+plsql+python+regex+sql+typescript+yaml&plugins=line-numbers+autoloader

Host Your PrismJS Download on OneDrive

After you have downloaded the Prism JS and CSS files, the next thing you need to figure out is what to do with them. There are a couple options:

  1. Cut and paste the contents inside your Blogger theme.
  2. Host the JS and CSS files somewhere and update your Blogger template to use them.

Although option #2 is a bit more complicated, it is the better long-term option in my opinion. Blogger does not allow file uploads, so the files have to be hosted somewhere else. There are a number of different options where to host the files, but I chose to use my Microsoft OneDrive account to do this. I chose OneDrive because I already have an account, I use it all the time, and it is easy to use. Most online file upload system (Google Drive, etc.) allow you to get a read-only, permanent link to a file. That is exactly what we are going to to do.

I’m assuming you already know how to save a file on OneDrive, so I will start from there. Login to OneDrive with a web browser and navigate to the folder where you have the Prism JS and CSS files. As seen in Figure 1, you will need to click three times to generate the embedded code.

  1. Select one of the files.
  2. Click the “Embed” option.
  3. Click the “Generate” button.

Figure 1 - Three Clicks to Generate Embedded Code

Three Clicks to Generate Embedded Code
Three Clicks to Generate Embedded Code

Figure 2 shows an embedded code example. You will notice it is an <iframe> tag with a src attribute (and a few others). We will be concentrating on the src attribute.

Figure 2 - The iframe Embedded Code

The iframe Embedded Code
The iframe Embedded Code

Let us take a look at the <iframe> tag a little more closely:

<iframe src=“https://onedrive.live.com/embed?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127125&authkey=ADxgzT72UZ6zQQM” width=“98” height=“120” frameborder=“0” scrolling=“no”></iframe>

Extract the bolded URL like this:

https://onedrive.live.com/embed?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127125&authkey=ADxgzT72UZ6zQQM

Then changed embed to download like this:

https://onedrive.live.com/download?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127125&authkey=ADxgzT72UZ6zQQM

You will want to do this for both the JS file and the CSS file. When you are done you will have two URL values that look like this.

JS. https://onedrive.live.com/download?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127124&authkey=AIhs3YZuWx_nl8k

CSS. https://onedrive.live.com/download?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127125&authkey=ADxgzT72UZ6zQQM

These URL values are the direct links to your JS file and the CSS file. Test them by pasting the URL values into a browser. The browser should download the file without redirecting to OneDrive. If it does not download directly, something is not right and you should try again.

Now that we have the direct links to the hosted JS and CSS files, let us look at how we update the Blogger theme.

Blogger Theme Updates

Now that the Prism JS and CSS files are hosted on OneDrive and I have permanent URL values to retrieve them, I now need to update my Blogger theme to use these files. I will need to make two updates to the Blogger theme:

  1. Include both the JS and CSS files.
  2. Add the ‘line-numbers’ class to the <body> tag.

Let us take a look at how to do both.

Include both the JS and CSS files

The JS file gets included with a <script> tag and the CS file gets included with a <link> tag. Start by creating both of these tags and drop in the permanent URL values like this:

JS. <script src=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127124&authkey=AIhs3YZuWx_nl8k’ type=‘text/javascript’/>

CSS. <link href=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&resid=C5144D8101C068D%2127125&authkey=ADxgzT72UZ6zQQM’ rel=‘stylesheet’/>

However, your not done yet! Normally this is all you need to do, but Blogger themes have a bit of a quirk. They seem to be saved as XML so the & characters in the URL values are a problem. To successfully save these tags to the Blogger theme, you need to escape the & characters with &amp; like this:

JS. <script src=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127124&amp;authkey=AIhs3YZuWx_nl8k’ type=‘text/javascript’/>

CSS. <link href=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127125&amp;authkey=ADxgzT72UZ6zQQM’ rel=‘stylesheet’/>

These tags are now ready to be used in the Blogger theme.

Add the ‘line-numbers’ class to the <body> tag

Recall that one of the reasons I am switching to Prism is because it was important to me to have line numbers added to my source code examples. This is done by the Prism Line Numbers plugin. To use this plugin, you need to do is add the ‘line-numbers’ class to the <body> tag within the Blogger theme. It looks like this:

<body expr:class=‘&quot;loading line-numbers&quot; + data:blog.mobileClass’>

Now let us get these updates into the Blogger theme.

Updating the Blogger Theme

After you log into your blog, perform the following steps as shown in Figures 3 and 4:

  1. Click “Theme” on the left
  2. Click the down-pointing arrow
  3. Select “Edit HTML”

Figure 3 - Blogger Theme Customization

Blogger Theme Customization
Blogger Theme Customization

Figure 4 - Blogger Edit HTML

Blogger Edit HTML
Blogger Edit HTML

Your are now looking at the HTML template of your blog’s theme. The Prism <script> and <link> tags you created above need to go somewhere within the opening and closing <head></head> tags so it looks like this:

<head>

<script src=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127124&amp;authkey=AIhs3YZuWx_nl8k’ type=‘text/javascript’/> <link href=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127125&amp;authkey=ADxgzT72UZ6zQQM’ rel=‘stylesheet’/>

</head>

The update to <body> is even easier. Just search the template for “<body” and update it to include the “line-numbers” class.

NOTE An HTML element can have multiple class values. The values are separated by a blank space. So note in the example below the blank space between loading and line-numbers.

<body expr:class=‘&quot;loading line-numbers&quot; + data:blog.mobileClass’>

That’s it! Save the file and you are good to go!

Examples

Let us take a look at a few source code syntax highlighting examples to make sure everything is working OK.

Listing 1 - Java

package org.prism.example;

public static final void main(String [] args) {
    System.out.println("Hello world!");
}

Listing 2 - JavaScript

const baseValue = prompt('Enter the base of a triangle: ');
const heightValue = prompt('Enter the height of a triangle: ');

// calculate the area
const areaValue = (baseValue * heightValue) / 2;

console.log(
  `The area of the triangle is ${areaValue}`
);

Listing 3 - TypeScript

class Employee {
    id: number;
    firstName: string;
    lastName: string;

    constructor(id: number, firstName: string, lastName: string) 
    {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + ' ' + this.lastName;
    }
}

// create Employee class object
let employee = new Employee(100, 'Rita', 'Red');
console.log(employee);
console.log(employee.getFullName());

Listing 4 - PL/SQL

DECLARE
  name VARCHAR2(50);
BEGIN
  name := 'Rita';
  DBMS_OUTPUT.PUT_LINE('Hello, ' || name);
END;


FOR i IN 1..10 LOOP
  DBMS_OUTPUT.PUT_LINE('i = ' || i);
END LOOP;

Listing 5 - XML

<Catalog>
	<CD>
		<Title>Empire Burlesque</Title>
		<Artist>Bob Dylan</Artist>
		<Country>USA</Country>
		<Company>Columbia</Company>
		<Price>10.90</Price>
		<Year>1985</Year>
	</CD>
	<CD>
		<Title>Greatest Hits</Title>
		<Artist>Dolly Parton</Artist>
		<Country>USA</Country>
		<Company>RCA</Company>
		<Price>9.90</Price>
		<Year>1982</Year>
	</CD>
</Catalog>

Listing 6 - HTML

<!DOCTYPE html>
<html>
<body>

<h1>My First Heading</h1>
<p>My first paragraph.</p>

</body>
</html>

Listing 7 - JSON

{
  "colors": [
    {
      "color": "red",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,0,0,1],
        "hex": "#FF0"
      }
    },
    {
      "color": "blue",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [0,0,255,1],
        "hex": "#00F"
      }
    },
    {
      "color": "yellow",
      "category": "hue",
      "type": "primary",
      "code": {
        "rgba": [255,255,0,1],
        "hex": "#FF0"
      }
    },
    {
      "color": "green",
      "category": "hue",
      "type": "secondary",
      "code": {
        "rgba": [0,255,0,1],
        "hex": "#0F0"
      }
    }
  ]
}

Summary

PrismJS is a nice syntax highlighter for source code examples. Using it with Blogger is a little work, but not too complicated.

Visit the PrismJS download page: https://prismjs.com/download.html and download what you will use most often.

Host the downloaded JS and CSS files on the technology of your choice. My example used OneDrive. You can also use Google Drive, GitLab, AWS, Azure, and I’m sure there are others.

Include the JS and CSS files in the Blogger theme by updating the template:

<head>

<script src=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127124&amp;authkey=AIhs3YZuWx_nl8k’ type=‘text/javascript’/> <link href=‘https://onedrive.live.com/download?cid=0C5144D8101C068D&amp;resid=C5144D8101C068D%2127125&amp;authkey=ADxgzT72UZ6zQQM’ rel=‘stylesheet’/>

</head>

Add the “line-numbers” class to the <body> tag to support the Line Numbers plugin.

<body expr:class=‘&quot;loading line-numbers&quot; + data:blog.mobileClass’>

Save the template change, create a blog with source code examples, and that’s it!

Enjoy!

February 02, 2023

Creating, Signing, and Verifying JWT in Java

Abstract

You use JWTs don’t you? Everyone does, right? But do you know how to generate, sign, and verify them? The purpose of this post is to demonstrate how to code all these operations.

Disclaimer

This post is solely informative. Critically think before using any information presented. Learn from it but ultimately make your own decisions at your own risk.

Requirements

I did all of the work for this post using the following major technologies. You may be able to do the same thing with different technologies or versions, but no guarantees.

  • Java 11
  • Maven 3.8.6 (Bundled with NetBeans)

Download

Visit my GitHub page https://github.com/mjremijan to see all of my open source projects. The code for this post is located at: https://github.com/mjremijan/thoth-jwt

Introduction

A JWT is a simple three-part string of encoded characters - header, payload, signature - separated by 2 “.” characters.

xxxxx.yyyyy.zzzzz

JWT technology has been around for years. Read all about the JWT specification on the Introduction to JSON Web Tokens at https://jwt.io/introduction. This blog focuses on the Java code to create and verify JWT values. There are 2 examples:

  1. JWT with Symmetric HMAC SHA256 Signature
  2. JWT with Asymmetric RSA SHA256 Signature

Let’s take a look at them.

JWT with Symmetric HMAC SHA256 Signature

Listing 1 shows the code and Listing 2 shows example output.

Listing 1 - JWT with Symmetric HMAC SHA256 Signature

1. package org.thoth.jwt.main;
2. 
3. import java.util.Base64;
4. import javax.crypto.Mac;
5. import javax.crypto.spec.SecretKeySpec;
6. 
7. /**
8.  *
9.  * @author Michael Remijan mjremijan@yahoo.com @mjremijan
10.  */
11. public class SignatureWithSymmetricalHmacSha256Main 
12. {
13.     public static void main(String[] args) throws Exception 
14.     {
15.         // JWT HEADER
16.         //
17.         // This is the xxxxx of a JWT xxxxx.yyyyy.zzzzz
18.         //
19.         // Given the following JSON document, encode it
20.         // using Java as defined in the JWT specifications
21.         String header = "{\"alg\":\"HS256\",\"typ\": \"JWT\"}";
22.         String headerEncoded 
23.             = Base64.getUrlEncoder()
24.                     .withoutPadding()
25.                     .encodeToString(
26.                         header.getBytes()
27.                     );
28.         String headerDecoded
29.                 = new String(
30.                     Base64.getUrlDecoder().decode(headerEncoded)
31.                 );
32.         
33.         System.out.printf("Header Plain   : %s%n", header);
34.         System.out.printf("Header Encoded : %s%n", headerEncoded);
35.         System.out.printf("Header Decoded : %s%n", headerDecoded);
36.         
37.         
38.         // JWT PAYLOAD
39.         //
40.         // This is the yyyyy of a JWT xxxxx.yyyyy.zzzzz
41.         //
42.         // Given the following JSON document, encode it
43.         // using Java as defined in the JWT specifications
44.         String payload = "{\"sub\":\"TMJR00001\",\"name\":\"Michael J. Remijan\",\"exp\":61475608800,\"iss\":\"info@wstutorial.com\",\"groups\":[\"user\",\"admin\"]}";
45.         String payloadEncoded 
46.             = Base64.getUrlEncoder()
47.                     .withoutPadding()
48.                     .encodeToString(
49.                         payload.getBytes()
50.                     );
51.         
52.         String payloadDecoded
53.                 = new String(
54.                     Base64.getUrlDecoder().decode(payloadEncoded)
55.                 );
56.         
57.         System.out.printf("%n");
58.         System.out.printf("Payload Plain   : %s%n", payload);
59.         System.out.printf("Payload Encoded : %s%n", payloadEncoded);
60.         System.out.printf("Payload Decoded : %s%n", payloadDecoded);
61.         
62.     
63.         // SIGNATURE / VERIFY
64.         // This is the zzzzz of a JWT xxxxx.yyyyy.zzzzz
65.         //
66.         // Hash-based message authentication code(HMAC)
67.         // is a specific type of message authentication code 
68.         // (MAC) involving a cryptographic hash function and 
69.         // a secret cryptographic key. As with any MAC, it 
70.         // may be used to simultaneously verify both the data
71.         // integrity and authenticity of a message.
72.         // 
73.         // A cryptographic hash function (CHF) is any function 
74.         // that can be used to map data of arbitrary size to 
75.         // a fixed-size number of n bits that has special 
76.         // properties desirable for a cryptographic application.
77.         //
78.         // For this example, the process will use the SHA256
79.         // cryptographic hash function and a secret key
80.         // to generate a signatureCreatedFromThisData (hash) of the JWT data.
81.         // This signatureCreatedFromThisData can then be used to verify the
82.         // JWT data has not been tampered.
83.         //
84.         // Typically the secret key is only available on the 
85.         // Authentication Server. The key is used to create the 
86.         // signatureCreatedFromThisData for the JWT. Clients will typically make 
87.         // an authentication request (HTTPS) to the Authentication
88.         // server to verify a JWT. Clients cannot verify a JWT 
89.         // themselves because they do not have access to the
90.         // secret key. However, if a Client is 100% trusted,
91.         // The secret key can be shared with the Client so
92.         // that the Client can do its own verification. 
93.         // WARNING: This means the Client will also be able 
94.         // to make new JWTs, which can be dangerous.
95.         String algorithm  = "HmacSHA256";
96.         String secret = "thisismysupersecretkeywhichshouldonlybeontheauthenticationserver";
97.         SecretKeySpec key = new SecretKeySpec(secret.getBytes(), algorithm);
98.         Mac mac = Mac.getInstance(algorithm);
99.         mac.init(key);
100.         String signatureCreatedFromThisData 
101.             = headerEncoded + "." + payloadEncoded;
102.         String signatureEncoded 
103.             = Base64.getUrlEncoder()
104.                     .withoutPadding()
105.                     .encodeToString(mac.doFinal(
106.                             signatureCreatedFromThisData.getBytes()
107.                         )
108.                     );
109.         
110.         System.out.printf("%n");
111.         System.out.printf("Signature Algorithm : %s%n", algorithm);
112.         System.out.printf("Signature Secret    : %s%n", secret);
113.         System.out.printf("Signaure Encoded    :%s%n", signatureEncoded);
114.     }
115. }
116. 

Listing 2 - HMAC Example Output

Header Plain   : {"alg":"HS256","typ": "JWT"}
Header Encoded : eyJhbGciOiJIUzI1NiIsInR5cCI6ICJKV1QifQ
Header Decoded : {"alg":"HS256","typ": "JWT"}

Payload Plain   : {"sub":"TMJR00001","name":"Michael J. Remijan","exp":61475608800,"iss":"info@wstutorial.com","groups":["user","admin"]}
Payload Encoded : eyJzdWIiOiJUTUpSMDAwMDEiLCJuYW1lIjoiTWljaGFlbCBKLiBSZW1pamFuIiwiZXhwIjo2MTQ3NTYwODgwMCwiaXNzIjoiaW5mb0B3c3R1dG9yaWFsLmNvbSIsImdyb3VwcyI6WyJ1c2VyIiwiYWRtaW4iXX0
Payload Decoded : {"sub":"TMJR00001","name":"Michael J. Remijan","exp":61475608800,"iss":"info@wstutorial.com","groups":["user","admin"]}

Signature Algorithm : HmacSHA256
Signature Secret    : thisismysupersecretkeywhichshouldonlybeontheauthenticationserver
Signaure Encoded    :Xi6kVafrGX18FQIkNZuVJVBbmGbmEzI8cM-5G02S32A

Line #21 of Listing 1 starts the creation of the JWT header. This is the xxxxx part of a xxxxx.yyyyy.zzzzz JWT. As you can see, the code is simple. Use Base64.getUrlEncoder().withoutPadding() for encoding and Base64.getUrlDecoder() for decoding.

NOTE Make sure to use the **.withoutPadding()** encoder. If not, trailing “=” characters will be added by the encoder to make the encoded string the necessary length. These trailing “=” are not allowed by the JWT specification so if you have them, other JWT decoders won’t be able to decode your JWT properly.

Line #44 of Listing 1 starts the creation of the JWT payload, typically user information, but in theory can be anything. This is the yyyyy part of a xxxxx.yyyyy.zzzzz JWT. As you can see, the code is simple. Use Base64.getUrlEncoder().withoutPadding() for encoding and Base64.getUrlDecoder() for decoding. See NOTE above about using the .withoutPadding() encoder.

Line #95 of Listing 1 starts the creation of the JWT signature. This is the zzzzz part of a xxxxx.yyyyy.zzzzz JWT. Listing 1 is an example of using the "alg":"HS256" aka HmacSHA256 algorithm. This is a single-key, symmetric algorithm which relies on a user-generated secret value as seen on line #96. This secret typically is stored outside the application in some kind of configuration system (file, git, database, etc.). Staring with line #102, you see how the MAC is used to finish the hash and the Base64.getUrlEncoder().withoutPadding() is used to encode the hash.

You’ll notice that after signing, there is no more code in Listing 1. Where’s the code showing how to verify a JWT? Well with a single-key, symmetric algorithm like HmacSHA256, the signing and verifying steps are exactly the same. To verify, the signature needs to be generated again and compared with the zzzzz part of a xxxxx.yyyyy.zzzzz JWT.

That’s it for JWT with Symmetric HMAC SHA256 Signature.

JWT with Asymmetric RSA SHA256 Signature

Listing 3 shows the code and Listing 4 shows example output.

Listing 3 - JWT with Asymmetric RSA SHA256 Signature

1. package org.thoth.jwt.main;
2. 
3. import java.security.KeyPair;
4. import java.security.KeyPairGenerator;
5. import java.security.PrivateKey;
6. import java.security.PublicKey;
7. import java.security.Signature;
8. import java.util.Base64;
9. 
10. /**
11.  *
12.  * @author Michael Remijan mjremijan@yahoo.com @mjremijan
13.  */
14. public class SignatureWithAsymmetricalRsaSha256Main 
15. {
16.     public static void main(String[] args) throws Exception 
17.     {
18.         // JWT HEADER
19.         //
20.         // This is the xxxxx of a JWT xxxxx.yyyyy.zzzzz
21.         //
22.         // Given the following JSON document, encode it
23.         // using Java as defined in the JWT specifications
24.         String header = "{\"alg\":\"RS256\",\"typ\": \"JWT\"}";
25.         String headerEncoded 
26.             = Base64.getUrlEncoder()
27.                     .withoutPadding()
28.                     .encodeToString(
29.                         header.getBytes()
30.                     );
31.         String headerDecoded
32.                 = new String(
33.                     Base64.getUrlDecoder().decode(headerEncoded)
34.                 );
35.         
36.         System.out.printf("Header Plain   : %s%n", header);
37.         System.out.printf("Header Encoded : %s%n", headerEncoded);
38.         System.out.printf("Header Decoded : %s%n", headerDecoded);
39.         
40.         
41.         // JWT PAYLOAD
42.         //
43.         // This is the yyyyy of a JWT xxxxx.yyyyy.zzzzz
44.         //
45.         // Given the following JSON document, encode it
46.         // using Java as defined in the JWT specifications
47.         String payload = "{\"sub\":\"TMJR00001\",\"name\":\"Michael J. Remijan\",\"exp\":61475608800,\"iss\":\"info@wstutorial.com\",\"groups\":[\"user\",\"admin\"]}";
48.         String payloadEncoded 
49.             = Base64.getUrlEncoder()
50.                     .withoutPadding()
51.                     .encodeToString(
52.                         payload.getBytes()
53.                     );
54.         
55.         String payloadDecoded
56.                 = new String(
57.                     Base64.getUrlDecoder().decode(payloadEncoded)
58.                 );
59.         
60.         System.out.printf("%n");
61.         System.out.printf("Payload Plain   : %s%n", payload);
62.         System.out.printf("Payload Encoded : %s%n", payloadEncoded);
63.         System.out.printf("Payload Decoded : %s%n", payloadDecoded);
64.         
65.     
66.         // SIGNATURE
67.         //
68.         // This is the zzzzz of a JWT xxxxx.yyyyy.zzzzz
69.         //
70.         // RSA (Rivest--Shamir--Adleman) is a public-key cryptosystem 
71.         // that is widely used for secure data transmission.
72.         // In a public-key cryptosystem, the public key is used for
73.         // encryption and the private key is used for decryption. The
74.         // private key is also used for creating digital signatures
75.         // of data and the public key is used for verifying the
76.         // digital signature.
77.         //
78.         // A cryptographic hash function (CHF) is any function 
79.         // that can be used to map data of arbitrary size to 
80.         // a fixed-size number of n bits that has special 
81.         // properties desirable for a cryptographic application.
82.         //
83.         // For this example, the process will use the SHA256
84.         // cryptographic hash function along with a public/private
85.         // keypair and the RSA encryption algorithm to generate
86.         // a signature for the JWT.
87.         //
88.         // The private key is used for creating the signature.
89.         //
90.         KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
91.         keyGenerator.initialize(1024);
92.         KeyPair kp = keyGenerator.genKeyPair();
93.         PublicKey publicKey = (PublicKey) kp.getPublic();
94.         PrivateKey privateKey = (PrivateKey) kp.getPrivate();
95.         String algorithm = "SHA256withRSA";
96.         String signatureCreatedFromThisData 
97.             = headerEncoded + "." + payloadEncoded;
98.         
99.         Signature privateSignature 
100.             = Signature.getInstance(algorithm);
101.         privateSignature.initSign(privateKey);
102.         
103.         System.out.printf("%n");
104.         System.out.printf("Algorithm    : %s%n", algorithm);
105.         System.out.printf("Public Key   : %s%n", Base64.getEncoder().encodeToString(publicKey.getEncoded()));
106.         System.out.printf("Private Key  : %s%n", Base64.getEncoder().encodeToString(privateKey.getEncoded()));
107.         
108.         privateSignature.update(signatureCreatedFromThisData.getBytes());
109.         String signatureEncoded 
110.                 = Base64.getUrlEncoder()
111.                         .withoutPadding()
112.                         .encodeToString(
113.                             privateSignature.sign()
114.                         );
115.         System.out.printf("%n");
116.         System.out.printf("Signaure Encoded         : %s%n", signatureEncoded);
117.         
118.         // VERIFY
119.         // This is the zzzzz of a JWT xxxxx.yyyyy.zzzzz
120.         //
121.         // The public key is used for verifying the signature.
122.         //
123.         // Becuase the public key is used for creating a signature,
124.         // it safe to distribute the public key to Clients so 
125.         // that Clients can verify the JWT signature without
126.         // having to ask the Authentication Server for verification
127.         //
128.         
129.         Signature publicSignature = Signature.getInstance(algorithm);
130.         publicSignature.initVerify(publicKey);
131.         publicSignature.update(signatureCreatedFromThisData.getBytes());
132.         boolean verified = publicSignature.verify(
133.             Base64.getUrlDecoder().decode(signatureEncoded)
134.         );
135.         System.out.printf("Signature Verified (t/f) : %b%n", verified);
136.     }
137. }
138. 

Listing 4 - RSA Example Output

Header Plain   : {"alg":"RS256","typ": "JWT"}
Header Encoded : eyJhbGciOiJSUzI1NiIsInR5cCI6ICJKV1QifQ
Header Decoded : {"alg":"RS256","typ": "JWT"}

Payload Plain   : {"sub":"TMJR00001","name":"Michael J. Remijan","exp":61475608800,"iss":"info@wstutorial.com","groups":["user","admin"]}
Payload Encoded : eyJzdWIiOiJUTUpSMDAwMDEiLCJuYW1lIjoiTWljaGFlbCBKLiBSZW1pamFuIiwiZXhwIjo2MTQ3NTYwODgwMCwiaXNzIjoiaW5mb0B3c3R1dG9yaWFsLmNvbSIsImdyb3VwcyI6WyJ1c2VyIiwiYWRtaW4iXX0
Payload Decoded : {"sub":"TMJR00001","name":"Michael J. Remijan","exp":61475608800,"iss":"info@wstutorial.com","groups":["user","admin"]}

Algorithm    : SHA256withRSA
Public Key   : MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRxw6Ncvsx0/kDYKwA6pLwn3hSbRdYFBOv1aiBomF7lPfOPfqaTgN2yPN6hErlLAP2d+94ig4uXv7MROXlsn8n7jdr2g5yo/kC92RJwALpffzBlWh29hEadiznWp2u0b0h++Cn4HJejfJpZOek6wurBL/7K2Y2TELOg8eg1uipEwIDAQAB
Private Key  : MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANHHDo1y+zHT+QNgrADqkvCfeFJtF1gUE6/VqIGiYXuU9849+ppOA3bI83qESuUsA/Z373iKDi5e/sxE5eWyfyfuN2vaDnKj+QL3ZEnAAul9/MGVaHb2ERp2LOdana7RvSH74Kfgcl6N8mlk56TrC6sEv/srZjZMQs6Dx6DW6KkTAgMBAAECgYBqVyPzZGQeADxtD+ZhmIfgXpaaAh8hURwhuIdxH6WXBg8Qh66v5fgvkPKMGt/0iHmByY6lZiaGLzWuywZXiEKYSl6tpK8WtiY+gyYxOVgFckAKzjBJ4GYb6YvPI5p5/qDFqN/9Ca4vDn9URFEIRSBIc1it8TWzze8x2Ljd4vu54QJBAOmlSJ/m4dHJMzLnyM6Y1x/e2fqm48DbfV3m+jDjyR7YrTwcVoZSC17B1z4J7W+/Ea7N61UWRRvelvC4c8OKkEkCQQDl2StC7vbKCsnDAFyDjUADq7p2aE+vVH7v7ZUjHhsTXMF8TFMgfkxl5cH58nDNq1Yo82SKGvMeRnmBYHeHlqZ7AkBP1Ur4YBJ+9QmKdkpV1UGEQUgn7ghaKGUwxbBtLhfVc2HV7TTfVn9OFFuwdgHsMdQf73peq2pXuHnIrK3ZfaoJAkEAh0nXg/NCAdRtw8C/s5L9feujujRVyt6SRMj0ApKi3ze2j2Ihf7u3XjbpgSRprzVNZpc0s3F/bm+O708HrCBJZwJBANPeVhBizgqPZOiQxLRxpNN2+EvEfs8js7YFRwB45orM/+9yVelNojEKxcHT7zS6j59dTlwvbGp6LVrKCrwtwLw=

Signaure Encoded         : HO4FLrLDt4ObECVWRiUGIGUimU1M70Y9aILT5op0UkV-kbEx8AqjCsLTh-Y1zOAisvFmuH5LYRw1wQyncQ5uEUWJYcoeldr-1_uFlpD2LqUy-QZfng8e6pxXOopL8Of_OcNEOqRijmI_dob8Gf0UnT7GQWpGTl32cIuuIFDeRHo
Signature Verified (t/f) : true

Line #24 of Listing 3 starts the creation of the JWT header. This is the xxxxx part of a xxxxx.yyyyy.zzzzz JWT. As you can see, the code is simple. Use Base64.getUrlEncoder().withoutPadding() for encoding and Base64.getUrlDecoder() for decoding.

NOTE Make sure to use the **.withoutPadding()** encoder. If not, trailing “=” characters will be added by the encoder to make the encoded string the necessary length. These trailing “=” are not allowed by the JWT specification so if you have them, other JWT decoders won’t be able to decode your JWT properly.

Line #47 of Listing 3 starts the creation of the JWT payload, typically user information, but in theory can be anything. This is the yyyyy part of a xxxxx.yyyyy.zzzzz JWT. As you can see, the code is simple. Use Base64.getUrlEncoder().withoutPadding() for encoding and Base64.getUrlDecoder() for decoding. See NOTE above about using the .withoutPadding() encoder.

Line #90 of Listing 3 starts the creation of the JWT signature. This is the zzzzz part of a xxxxx.yyyyy.zzzzz JWT. Listing 3 is an example of using the "alg":"RS256" aka SHA256withRSA algorithm. This is a two-key, asymmetric algorithm which relies on a public/private keypair created on Line #92. A Signature object is created on line #99 and it is initialized with the private key. Staring with line #108, see how the Signature is used to create a signature and the Base64.getUrlEncoder().withoutPadding() is used to encode the signature.

NOTE The public/private keypair will need to be generated outside the application and kept in some kind of configuration store (file, git, database, etc.). This is an exercise left up to you.

Line #129 of Listing 3 starts the verify process. A Client may verify a JWT it receives from an Authentication server to guard against tampering while in transit. To verify a JWT created using an asymmetric RSA SHA256 signature, the Client will need the public key. This typically is not a problem since public keys are designed to be giving away. Line #132 demonstrates the call to .verify().

That’s it for JWT with Asymmetric RSA SHA256 Signature.

Summary

Most of this blog is the code. Review the code, top to bottom, it is not overly complicated. But now you know how to create and verify JWT values using both a Symmetric HMAC SHA256 Signature and an Asymmetric RSA SHA256 Signature.

Enjoy!

References

Introduction to JSON Web Tokens. (n.d.). JWT. https://jwt.io/introduction.

Debugger. (n.d.). JWT. https://jwt.io/#debugger-io

Alx. (2017, December). Create jwt in java using Public key rsa. https://wstutorial.com/misc/jwt-java-public-key-rsa.html

Base64 withoutPadding Encoding of string or byte array (Java8). (n.d.). MakeInJava Tutorials. https://makeinjava.com/base64-withoutpadding-encoding-string-byte-array-java8/

Poulsen, Søren. (n.d.). Calculate HMAC-Sha256 with Java. https://sorenpoulsen.com/calculate-hmac-sha256-with-java

Dommerholt, Niels. (2016, December 28). Example of RSA generation, sign, verify, encryption, decryption and keystores in Java. https://gist.github.com/nielsutrecht/855f3bef0cf559d8d23e94e2aecd4ede1

HMAC. (2023, January 1). Wikipedia. https://en.wikipedia.org/wiki/HMAC

Cryptographic hash function. (2023, January 10). Wikipedia. https://en.wikipedia.org/wiki/Cryptographic_hash_function

Hash function. (2023, February 1). Wikipedia. https://en.wikipedia.org/wiki/Hash_function