In the below code there might arise a question why we need to do hashing and extract the final secret key from it. The importance of this step could be felt incase we are storing passwords in DBs instead of directly using actual secret key and hashing, the hashing by using message digest adds more strength.

EncryptUtil.java

package com.mugil.org;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;

public class EncryptUtil {
    private static SecretKeySpec secretKey;
    private static byte[] key;

    public static void setKey(String myKey) {
        MessageDigest sha = null;
        try {
            //Byte Arrays are secured compared to storing in String as it is immutable
            key = myKey.getBytes("UTF-8");

            /*
              MessageDigest class represents a cryptographic hash function which can calculate a
              message digest from binary data.A hash function is a mathematical function that 
              converts a numerical input value into another compressed numerical value.The input to 
              the hash function is of arbitrary length but output is always of fixed length.
              Values returned by a hash function are called message digest or simply hash values.
            */

            /*
              Code will work without below two lines but hashing adds more strength to secret key
              AES needs a 128/192/256 bits key. If you don't hash your key and only trim the input it
              would only use the first 16/24/32 Bytes. So generating a Hash is the only reasonable way.
             */
            sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);

            /*Use SHA-1 to generate a hash from your key and trim the result to 128 bit (16 bytes).*/
            key = Arrays.copyOf(key, 16);

            /*Taking Key that would be used for Encrytion */
            secretKey = new SecretKeySpec(key, "AES");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public static String encrypt(String strToEncrypt, String secret) {
        try {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
        } catch (Exception e) {
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }

    public static String decrypt(String strToDecrypt, String secret) {
        try {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error while decrypting: " + e.toString());
        }
        return null;
    }
}

SimpleEncryption.java

package com.mugil.org;

public class SimpleEncryption {
    public static void main(String[] args) {
        final String secretKey = "ItSecretKey";

        String originalString = "You Nailed It!!!";
        String encryptedString = EncryptUtil.encrypt(originalString, secretKey) ;
        String decryptedString = EncryptUtil.decrypt(encryptedString, secretKey) ;

        System.out.println(originalString);
        System.out.println(encryptedString);
        System.out.println(decryptedString);
    }
}

You Nailed It!!!
lFY4a0e0BRVfcfko56Lpe78iUfPoZm5wkq/zv0hrFco=
You Nailed It!!!

JWT can be implemented the same way for OAuth where the authorization server and the resource server are different. In this scenario Authorization server provides just the token and exposes JWK(JSON web keyset) for its public key.

JWT always deals with Authorization, not Authentication.

  1. User logins to application – Authentication to the user-facing application
  2. Once authenticated, User can request access to any resource.
  3. User-facing application can request an access token that will serve to request the external resource in representation of the end user, so the resource server can determine who is requesting the information (authentication) and the access level of that user (authorization). On this step an Oauth2 Access token request is performed
  4. Authorization server will validate the request for an access token. The end-user application can provide client credentials to prove identity of the invoker, as well as a hint to determine the user who triggers the process.

    Authorization server to generate a new RSA key pair, whose private key will be utilized to create the JWT signature, while the public key will be stored and published so any resource server who receives the JWT can look up the public key and perform signature verification.

  5. User-facing application receives the JWT access token, attaches it to a new HTTP request as an Authorization header, as a bearer token, and invokes the resource server to attempt the retrieval of the external resource.
  6. Resource server detects that a Bearer token was included in the new request received, decodes the header in order to find out the JWK key id, then connects to the authorization server JWK set URL in order to retrieve the list of available public keys, then filters the key with id indicated in the header. Once the JWK is found, Resource server just must perform signature verification to determine if the JWT received is valid
  7. Once JWT is verified, the payload is considered as valid, and any information within the payload can be used by the resource server to determine whether the requested resource can be delivered in the request received or not.

Advantage of Using Asymmetric Keys
In a microservice architecture where JWTs are exchanged, each service can have a public/private key pair. Compared to symmetric signatures, this scheme significantly reduces the impact of a breach of a single service in this architecture.

Posted in JWT.