JSON web token (JWT) implementation in Java

JWT (JSON Web Token) is an open source standard commonly used to transmit data between two services in a compact and secure way. The information transmitted between services can be verified and trusted because it is digitally signed.

JSON Web Token (JWT) Structure

Header

  • Algorithm – The hashing algorithm used
    • Ex: RS256, HS256, HS384, HS512, RS384, PS384, ES256 etc.
  • Type – Type of the token.
    • Ex: JWT
  • Certificate chain
    • Ex: x5c –> X.509 Certificate Chain

Payload

  • iss — Issuer
  • sub — Subject
  • aud — Audience
  • exp — Expiration Time
  • iat — Issued At
  • jti — JWT ID
  • so on…

Signature

  • The encoded header
  • The encoded payload
  • A secret
  • An algorithm and sign

Create JWT Token

Below JsonReader class reads the Json file having all the payload and header and private key to store in a bean class.

Setup

Maven dependency.

   <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
    </dependencies>

Reader payload and header info from JSON file.

package com.jwt.oauth;

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonReader {
    public CertJsonBean readJsonFile() {

        ObjectMapper objectMapper;

        try {
            ObjectMapper mapper = new ObjectMapper();

            objectMapper = new ObjectMapper();
            CertJsonBean cert = objectMapper.readValue(new File("src/main/java/data/hsm_cert.json"), CertJsonBean.class);

            return cert;

        } catch (IOException ie) {
            ie.printStackTrace();
        }
        return null;

    }
}

Here is the bean class that has all the getter and setters.

package com.jwt.oauth;

import java.util.*;

public class CertJsonBean {
    public String privatekey;
    public Map<String, Object> payload;
    public Map<String, Object> header;

    public String getPrivatekey() {
        return privatekey;
    }
    public void setPrivatekey(String privatekey) {
        this.privatekey = privatekey;
    }
    public Map<String, Object> getPayload() {
        return payload;
    }
    public void setPayload(Map<String, Object> payload) {
        this.payload = payload;
    }
    public Map<String, Object> getHeader() {
        return header;
    }
    public void setHeader(Map<String, Object> header) {
        this.header = header;
    }
    @Override
    public String toString() {
        return "CertJsonBean{" +
                "privatekey='" + privatekey + '\'' +
                ", payload=" + payload +
                ", header=" + header +
                '}';
    }
}

Create a private key from the String key.

   private PrivateKey getPrivateKey(String privateKey) throws Exception {
        // Read in the key into a String
        StringBuilder pkcs8Lines = new StringBuilder();
        BufferedReader rdr = new BufferedReader(new StringReader(privateKey));
        String line;
        while ((line = rdr.readLine()) != null) {
            pkcs8Lines.append(line);
        }

        // Remove the "BEGIN" and "END" lines, as well as any whitespace
        String pkcs8Pem = pkcs8Lines.toString();
        pkcs8Pem = pkcs8Pem.replace("-----BEGIN PRIVATE KEY-----", "");
        pkcs8Pem = pkcs8Pem.replace("-----END PRIVATE KEY-----", "");
        pkcs8Pem = pkcs8Pem.replaceAll("\\s+", "");

        // Base64 decode the result
        byte[] pkcs8EncodedBytes = Base64.getDecoder().decode(pkcs8Pem);

        // extract the private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privKey = kf.generatePrivate(keySpec);

        return privKey;

    }

Generate the JWT token by passing Json data to the method

   public String generateToken(CertJsonBean certJsonBean) {

        String token = null;
        String privateKey;

        Map<String, Object> header = new HashMap<String, Object>();
        Map<String, Object> payload = new HashMap<String, Object>();

        try {
            privateKey = certJsonBean.privatekey;
            payload = certJsonBean.payload;
            header = certJsonBean.header;

            // get the private key from encoded key.
            PrivateKey pvtKey = getPrivateKey(privateKey);

            if (!certJsonBean.privatekey.isEmpty()) {
                token = Jwts.builder().setClaims(payload).setHeader(header).signWith(pvtKey, SignatureAlgorithm.RS256).compact();

            } else {
                token = "Something went wrong!!";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return token;
    }

Main method to generate the token.

   public static void main(String[] args) throws Exception {

        JwtTokenCreator jwtTokenCreator = new JwtTokenCreator();
        JsonReader jsonReader = new JsonReader();
        CertJsonBean certJsonBean = jsonReader.readJsonFile();
        String token = jwtTokenCreator.generateToken(certJsonBean);
        System.out.println(token);
    }

Verification of JWT Token

   private void verifyToken(String token, PublicKey publicKey) {

        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();

            System.out.println(claims.get("iss"));
            System.out.println(claims.get("sub"));

        } catch (Exception e) {
            claims = null;
        }
    }

Conclusion

Currently, there are several standardized security approaches (session cookie, HTTP Basic, and HTTP Digest) will work with REST services as well, they all have problems that would be nice to avoid by using a better standard. JWT is another approach to save the problem.
JWT’s main strength is handling user authentication in a stateless, and therefore scalable, way, while keeping everything secure with up-to-date cryptography standards.

Leave a Reply

Your email address will not be published. Required fields are marked *