JSON Web Token (JWT)

1.JSON Web Token (JWT)

JSON Web Tokens consist of three parts separated by dots (.), which are:

  • Header

  • Payload

  • Signature

Therefore, a JWT typically looks like the following: `xxxxx.yyyyy.zzzzz`

The header and the payload are Base64Url encoded. The signature is created using these two, a secret and the hashing algorithm being used (as specified in the header: ES256).

To parse the JWT, you can use one of the libraries listed in the JWT Libraries for Token Signing/Verification section of JWT.io .

2.JSON Web Signature (JWS)

The decrypted payload is signed in JWS (JSON Web Signature) format, similar to the access_token.

  • The signature algorithm used is ES256.

You may download our Public JWKS from Myinfo JWKS URI for verification, after application is created during onboarding on the application details page upon login.

Copy to clipboard

module.exports.verifyJWS = async (compactJWS, jwksUrl) => {
  var jwks = await getJwks(jwksUrl);
   
  try {
    let keyStore = await jose.JWK.asKeyStore(jwks);
   
    let result = await jose.JWS.createVerify(keyStore).verify(compactJWS);
    let payload = JSON.parse(Buffer.from(result.payload).toString());
   
    return payload;
  } catch (error) {
    console.error("Error with verifying JWS:", error);
    throw constant.ERROR_VERIFY_JWS;
  }
};

The code example above verify the JWS and return the JSON object, which is the person's data.

3.JSON Web Encryption (JWE)

  • The encryption is done using your application's public key you provided during onboarding. So the payload's decryption should use your corresponding private key.

  • Current encryption algorithms used:

    • ECDH-ES+A256KW (for content key wrapping)

    • AES256GCM (for content key wrapping)

3.1 What is the JWE (JSON Web Encryption) Compact Serialization format?

The format consists of five parts separated by dots (.), which are:

PART
DESCRIPTION

Header

Contains the encryption algorithms used to produce the

  1. cipher text(encrypted data)

  2. encrypted key (symmetric key encrypted with application's RSA public key)

Encrypted Key

The encrypted symmetric key that was used to encrypt the data.

Initialization Vector

Secure random value used together with the symmetric key to encrypt the data.

Ciphertext

The encrypted data.

Tag

Value used to ensure integrity of the encrypted data and header.

Therefore, a JWE typically looks like the following: aaaaa.bbbbb.ccccc.ddddd.eeeee

3.2 How can I decrypt the JWE?

You will need to use a library that performs JWE decryption to decrypt the Ciphertext. Such libraries are readily available on the internet.

For example, in our sample application (using Node.js), we use the jose.jwe.decrypt method from jose library. If the decryption fails, the library will throw an error the application needs to handle.

3.3 How does JWE library decrypt the JWE in detail?

Firstly, your JWE library will decrypt the Encrypted Key inside the JWE using your application's corresponding Encryption private key. Once it's decrypted, your library can retrieve the symmetric key that is used to encrypt the Ciphertext in JWE. Next, the JWE library will decrypt the Ciphertext using that symmetric key together with the Initialization Vector, Tag and the encryption algorithm in the ASCII-encoded Header.


With reference to myinfo-connector/lib/securityHelper.js sample application connector code:

Copy to clipboard

module.exports.decryptJWEWithKey = async (compactJWE, encryptionPrivateKey) => {
  try {
    let keystore = jose.JWK.createKeyStore();
    let jweParts = compactJWE.split("."); // header.encryptedKey.iv.ciphertext.tag
    if (jweParts.length != 5) {
      throw constant.ERROR_INVALID_DATA_OR_SIGNATURE;
    }
     
    //Session encryption private key should correspond to the session encryption public key passed in to client assertion
    let key = await keystore.add(encryptionPrivateKey, "pem");
     
    let data = {
      "type": "compact",
      "protected": jweParts[0],
      "encrypted_key": jweParts[1],
      "iv": jweParts[2],
      "ciphertext": jweParts[3],
      "tag": jweParts[4],
      "header": JSON.parse(jose.util.base64url.decode(jweParts[0]).toString())
    };
     
    let result = await jose.JWE.createDecrypt(key).decrypt(data);
     
    return result.payload.toString();
  } catch (error) {
    throw constant.ERROR_DECRYPT_JWE;
  }
};

The code example above decrypts the JWE and returns the payload signed in JWS;

Last updated

Was this helpful?