BoxLang 🚀 A New JVM Dynamic Language Learn More...
Introduction:
While signing a JWT provides a means to establish the authenticity of the JWT contents, encryption provides a way to keep the contents of the JWT unreadable to third parties.
An encrypted JWT is known as JWE (JSON Web Encryption).
Encryption:
Encrypt and decrypt values using the Java based asymmetric and symmetric algorithms.
There are 5 segments in a JWE (JSON Web Encryption), rather than 3, as in a JWT (JSON Web Token) or a JWS (JSON Web Signature).
The segments, in order, are:
The 2nd segment, JWE Encrypted Key, is encrypted, using the RSA-OAEP encryption scheme, which uses an RSA algorithm with the Optimal Asymmetric Encryption Padding (OAEP) method.
The 4th segment, JWE Ciphertext, is encrypted, using the Advanced Encryption Standard (AES) in Galois/Counter Mode (GCM) algorithm with a 256-bit long key. Once decrypted, the JWE Encrypted Key, uses a symmetric key algorithm, specifically designed to be used with Authenticated Encryption with Associated Data (AEAD), to decrypt the JWE Ciphertext or payload.
The associated data, which is part of the AEAD scheme, is stored in the 5th segment (JWE Authentication Tag).
The 3rd and 5th segments, JWE initialization vector and JWE Authentication Tag, are extracted, during the encryption cipher creation, which is used to encrypt segment 4. All segments are Base64 encoded to ensure a successful transit.
The payload is made up of a claim set, which is made up of registered claims and a custom claim:
None of the claims are mandatory, so only pass in the ones that are required for your project
The Java library, used in this installation, requires the Unlimited Strength Java(TM) Cryptography library.
[ system_path_to_coldfusion_installation ]\jre\lib\security\local_policy.jar
[ system_path_to_coldfusion_installation ]\jre\lib\security\US_export_policy.jar
UnlimitedJCEPolicyJDK8
These examples work, out of the box, within the installation directory.
this.javaSettings
/application.cfc
And then restart the Coldfusion Application Server
Use the following argument when initialising the constructor:
new components.jwt.lib.encrypt.Encrypter( ...useJavaLoader = false )
If you experience a well known bug, involving:
com.compoundtheory.classloader.NetworkClassLoader
It may have occured because:
You may wish to use a JavaLoader singleton instance. One has been created in the:
/application.cfc
new components.jwt.lib.encrypt.Encrypter( ...javaLoaderInstance = request.javaloader )
This keeps the JavaLoader in the application scope which may bring performance benefits, depending on the project requirements.
The following working examples can be found in:
/index.cfm
// global variables
jarSystemPath = ExpandPath( "." ) & "\assets\core\lib\chamika-jwt-sign-encrypt\chamika-jwt-sign-encrypt-1.0.8.jar";
javaLoaderClassPath = "components.javaloader.JavaLoader";
Only one custom claim gets parsed
// create claim set
claimset = {
iss = "https://openid.net",
sub = "Charles Robertson",
aud = "https://app-one.com,https://app-two.com",
exp = DateAdd( "s", ( 1000 * 60 * 10 ), Now() ),
nbf = Now(),
iat = Now(),
jti = CreateUUID(),
claim = {
json = SerializeJson(
{
forename = "Charles",
surname = "Robertson"
}
)
}
};
// initialise Encrypter.cfc
JwtSignEncrypt = new components.jwt.lib.encrypt.Encrypter(
claimSet = claimset,
javaLoaderClassPath = javaLoaderClassPath,
jarSystemPath = jarSystemPath
);
// extract secret key
secretKeyEncoded = JwtSignEncrypt.GetSecretKeyEncoded();
// sign & encrypt JWT, using secret key
jwtString = JwtSignEncrypt.Encrypt( secretKeyEncoded = secretKeyEncoded );
// decrypt payload, using JWT and secret key
decryptedJwtString = JwtSignEncrypt.Decrypt( jwtString = jwtString, secretKeyEncoded = secretKeyEncoded );
// format epoch dates
decryptedJwtStringWithParsedDates = {};
for ( key in decryptedJwtString ) {
value = decryptedJwtString[ key ];
if ( ListFindNoCase( "exp,nbf,iat", key ) ) {
date = JwtSignEncrypt.EpochTimeToLocalDate( value );
value = DateFormat( date, "full" ) & " " & TimeFormat( date, "full" );
}
decryptedJwtStringWithParsedDates[ key ] = value;
}
// output payload
WriteDump( var = decryptedJwtStringWithParsedDates );
// check to see whether the JWT has expired
hasExpired = JwtSignEncrypt.HasExpired();
Only one custom claim gets parsed
// create claim set
claimset = {
iss = "https://openid.net",
sub = "Charles Robertson",
aud = "https://app-one.com,https://app-two.com",
exp = DateAdd( "s", ( 1000 * 60 * 10 ), Now() ),
nbf = Now(),
iat = Now(),
jti = CreateUUID(),
claim = {
json = SerializeJson(
{
forename = "Charles",
surname = "Robertson"
}
)
}
};
// initialise Encrypter.cfc
JwtSignEncrypt = new components.jwt.lib.encrypt.Encrypter(
claimSet = claimset,
javaLoaderClassPath = "",
jarSystemPath = "",
useJavaLoader = true,
javaLoaderInstance = request.javaloader
);
// extract secret key
secretKeyEncoded = JwtSignEncrypt.GetSecretKeyEncoded();
// sign & encrypt JWT, using secret key
jwtString = JwtSignEncrypt.Encrypt( secretKeyEncoded = secretKeyEncoded );
// decrypt payload, using JWT and secret key
decryptedJwtString = JwtSignEncrypt.Decrypt( jwtString = jwtString, secretKeyEncoded = secretKeyEncoded );
// format epoch dates
decryptedJwtStringWithParsedDates = {};
for ( key in decryptedJwtString ) {
value = decryptedJwtString[ key ];
if ( ListFindNoCase( "exp,nbf,iat", key ) ) {
date = JwtSignEncrypt.EpochTimeToLocalDate( value );
value = DateFormat( date, "full" ) & " " & TimeFormat( date, "full" );
}
decryptedJwtStringWithParsedDates[ key ] = value;
}
// output payload
WriteDump( var = decryptedJwtStringWithParsedDates );
// check to see whether the JWT has expired
hasExpired = JwtSignEncrypt.HasExpired();
Only one custom claim gets parsed
// create claim set
issuer = "https://openid.net";
subject = "Charles Robertson";
audience = "https://app-one.com,https://app-two.com";
expirationTime = DateAdd( "s", ( 60 * 10 ) , Now() );
notBeforeTime = Now();
issueTime = Now();
jwtID = CreateUUID();
claim = {
json = SerializeJson(
{
forename = "Charles",
surname = "Robertson"
}
)
};
// initialise Encrypter.cfc
JwtSignEncrypt = new components.jwt.lib.encrypt.Encrypter(
iss = issuer,
sub = subject,
aud = audience,
exp = expirationTime,
nbf = notBeforeTime,
iat = issueTime,
jti = jwtID,
claim = claim,
javaLoaderClassPath = javaLoaderClassPath,
jarSystemPath = jarSystemPath
);
// extract secret key
secretKeyEncoded = JwtSignEncrypt.GetSecretKeyEncoded();
// sign & encrypt JWT, using secret key
jwtString = JwtSignEncrypt.Encrypt( secretKeyEncoded = secretKeyEncoded );
// decrypt payload, using JWT and secret key
decryptedJwtString = JwtSignEncrypt.Decrypt( jwtString = jwtString, secretKeyEncoded = secretKeyEncoded );
// format epoch dates
decryptedJwtStringWithParsedDates = {};
for ( key in decryptedJwtString ) {
value = decryptedJwtString[ key ];
if ( ListFindNoCase( "exp,nbf,iat", key ) ) {
date = JwtSignEncrypt.EpochTimeToLocalDate( value );
value = DateFormat( date, "full" ) & " " & TimeFormat( date, "full" );
}
decryptedJwtStringWithParsedDates[ key ] = value;
}
// output payload
WriteDump( var = decryptedJwtStringWithParsedDates );
// check to see whether the JWT has expired
hasExpired = JwtSignEncrypt.HasExpired();
Only one custom claim gets parsed
// initialise Encrypter.cfc
JwtSignEncrypt = new components.jwt.lib.encrypt.Encrypter(
javaLoaderClassPath = javaLoaderClassPath,
jarSystemPath = jarSystemPath
);
// retrieve secret key
secretKeyEncoded = FileReadBinary( ExpandPath( "." ) & "\secretKeyEncoded.txt" );
// retrieve JWT string
jwtString = REReplaceNocase( Trim( FileRead( ExpandPath( "." ) & "\jwtString.txt" ) ), "[\s]+", "", "ALL" );
// decrypt payload, using JWT and secret key
decryptedJwtString = JwtSignEncrypt.Decrypt( jwtString = jwtString, secretKeyEncoded = secretKeyEncoded );
// format epoch dates
decryptedJwtStringWithParsedDates = {};
for ( key in decryptedJwtString ) {
value = decryptedJwtString[ key ];
if ( ListFindNoCase( "exp,nbf,iat", key ) ) {
date = JwtSignEncrypt.EpochTimeToLocalDate( value );
value = DateFormat( date, "full" ) & " " & TimeFormat( date, "full" );
}
decryptedJwtStringWithParsedDates[ key ] = value;
}
// output payload
WriteDump( var = decryptedJwtStringWithParsedDates );
// check to see whether the JWT has expired
hasExpired = JwtSignEncrypt.HasExpired();
Only one custom claim gets parsed
// create claim set
claimset = {
iss = "https://openid.net",
sub = "Charles Robertson",
aud = "https://app-one.com,https://app-two.com",
exp = DateAdd( "s", ( 1000 * 60 * 10 ), Now() ),
nbf = Now(),
iat = Now(),
jti = CreateUUID(),
claim = {
json = SerializeJson(
{
forename = "Charles",
surname = "Robertson"
}
)
}
};
secretKey = Hash( "foo" );
// initialise Encrypter.cfc
JJwtSignEncrypt = new components.jwt.lib.encrypt.Encrypter(
claimSet = claimset,
secretKey = secretKey,
javaLoaderClassPath = javaLoaderClassPath,
jarSystemPath = jarSystemPath
);
// extract secret key
secretKeyEncoded = JwtSignEncrypt.GetSecretKeyEncoded();
// sign & encrypt JWT, using secret key
jwtString = JwtSignEncrypt.Encrypt( secretKeyEncoded = secretKeyEncoded );
// decrypt payload, using JWT and secret key
decryptedJwtString = JwtSignEncrypt.Decrypt( jwtString = jwtString, secretKeyEncoded = secretKeyEncoded );
// format epoch dates
decryptedJwtStringWithParsedDates = {};
for ( key in decryptedJwtString ) {
value = decryptedJwtString[ key ];
if ( ListFindNoCase( "exp,nbf,iat", key ) ) {
date = JwtSignEncrypt.EpochTimeToLocalDate( value );
value = DateFormat( date, "full" ) & " " & TimeFormat( date, "full" );
}
decryptedJwtStringWithParsedDates[ key ] = value;
}
// output payload
WriteDump( var = decryptedJwtStringWithParsedDates );
// check to see whether the JWT has expired
hasExpired = JwtSignEncrypt.HasExpired();
1.1.0
Added new argument to Encrypter.cfc constructor, called:
javaLoaderInstance=request.javaloader
This takes a singleton instance, provided in the application.cfc, which is stored in the application scope and passed to the request scope.
This optional argument will allow the javaloader and its loaded java class to remain in memory, improving performance.
$
box install JWTSignEncrypt