Encoding And Decoding Base64url Values In ColdFusion
I've been using base64-encoding in ColdFusion for a long time; but, until yesterday, I had never heard of the term, base64url. Over the weekend, I was doing some research and development (R&D) with JSON Web Tokens (JWT) and I saw that the specification kept referring to "base64url" encoded values. It turns out that base64url is a variant of the standard base64 encoding that uses a slightly different character set. In fact, as I read on the Wikipedia page, there are a number of variants, all revolving around the special handling of the "+" and "/" characters in the core base64 character set.
The alpha-numeric portion of the base64 character set is fairly benign. But, the characters "+", "/", and "=" (padding) can have an overloaded meaning in different context. For example, in the context of a URL, these characters have a special meaning. And, if you want to use them outside of that special meaning, you have to encode them. To get around this awkwardness, the base64url variant replaces these overloaded characters with other characters that are URL and filename safe:
-
- becomes -.
- / becomes _.
- = is removed.
Encoding and decoding these values, in ColdFusion, means that have to add a small layer of finagling on top of the base character and binary value encoding functions. To demonstrate, I'm going to encode and decode an input value using the base64url variant:
<cfscript>
// Set up our input to take through the base64url-encoding lifecycle.
input = "MC Lyte: I rock the party that rocks the body!";
// Encode and then subsequently decode the base64url value to make sure that both
// the input and the output match after encoding.
encodedValue = base64urlEncode( input );
decodedValue = base64urlDecode( encodedValue );
// Output our results.
writeOutput( "Input: #input# <br />" );
writeOutput( "Encoded: #encodedValue# <br />" );
writeOutput( "Decoded: #decodedValue# <br />" );
writeOutput( "Matches: #yesNoFormat( ! compare( input, decodedValue ) )# <br />" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I decode the base64url value into its original UTF-8 string.
*
* @value I am the UTF-8 string to decode.
* @output false
*/
public string function base64urldecode( required string value ) {
// First, we need to get this base64url value back to the original base64 format.
// To do this, we have to re-add the standard base64 characters.
value = replace( value, "-", "+", "all" );
value = replace( value, "_", "/", "all" );
// Part of the original coding stripped out the padding characters at the end of
// the base64 value. We need to add these back in, otherwise ColdFusion won't be
// able to parse the value.
value &= repeatString( "=", ( 4 - ( len( value ) % 4 ) ) );
// Once we have the valid base64 input, we can get the binary representation.
var bytes = binaryDecode( value, "base64" );
// And, from the binary, we can re-encode the value as the original UTF-8 string.
var decodedValue = charsetEncode( bytes, "utf-8" );
return( decodedValue );
}
/**
* I encode the given UTF-8 string using base64url encoding.
*
* @value I am the UTF-8 string to encode.
* @output false
*/
public string function base64urlEncode( required string value ) {
// Get the binary representation of the UTF-8 string.
var bytes = charsetDecode( value, "utf-8" );
// Encode the binary using the core base64 character set.
var encodedValue = binaryEncode( bytes, "base64" );
// Replace the characters that are not allowed in the base64url format. The
// characters [+, /, =] are removed for URL-based base64 values because they
// have significant meaning in the context of URL paths and query-strings.
encodedValue = replace( encodedValue, "+", "-", "all" );
encodedValue = replace( encodedValue, "/", "_", "all" );
encodedValue = replace( encodedValue, "=", "", "all" );
return( encodedValue );
}
</cfscript>
As you can see, we're still using the core charset and binary ColdFusion functions; but, we're adding and removing the restricted base64url characters as well. When we run the above code, we get the following page output:
Input: MC Lyte: I rock the party that rocks the body!
Encoded: TUMgTHl0ZTogSSByb2NrIHRoZSBwYXJ0eSB0aGF0IHJvY2tzIHRoZSBib2R5IQ
Decoded: MC Lyte: I rock the party that rocks the body!
Matches: Yes
From the output, you can't tell that the restricted characters were part of the underlying base64 encoding; but, you can see that we were able to bring the string through the encoding and decoding lifecycle. This will prove useful when I start dealing with JSON (JavaScript Object Notation) Web Tokens in ColdFusion.
Want to use code from this post? Check out the license.
Reader Comments