Implementing JSON Web Tokens in .NET with a Base 64 URL Encoded key

I wasn’t able to find any good technical examples of how to implement JSON Web Tokens (JWT) for .NET when the key is Base 64 URL encoded according to the JWT spec (http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-A.1, page 35).

John Sheehan’s JWT library on GitHub is a nice starting point, and works well when the key is ASCII encoded already, but it cannot be used without modification if the key is Base 64 URL Encoded.

Here’s the solution:

// URL Encode the string, according to
// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-A.1, page 35
public string Base64UrlEncode(byte[] arg)
{
string s = Convert.ToBase64String(arg); // Regular base64 encoder
s = s.Split('=')[0]; // Remove any trailing '='s
s = s.Replace('+', '-'); // 62nd char of encoding
s = s.Replace('/', '_'); // 63rd char of encoding
return s;
}
public byte[] Base64UrlDecode(string arg)
{
string s = arg;
s = s.Replace('-', '+'); // 62nd char of encoding
s = s.Replace('_', '/'); // 63rd char of encoding
switch (s.Length % 4) // Pad with trailing '='s
{
case 0: break; // No pad chars in this case
case 2: s += "=="; break; // Two pad chars
case 3: s += "="; break; // One pad char
default: throw new System.Exception(
"Illegal base64url string!");
}
return Convert.FromBase64String(s); // Standard base64 decoder
}
// Implementation of http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08,
// section A.1.1, JWS using HMAC SHA-256 (encoding), by J.T. Taylor, www.securevideo.com
public string GetAuthenticationToken(string base64UrlEncodedSecretKey, string userId)
{
// Prepare authentication token
// Get Unix-style expiration date
double unixSeconds = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
double expiry = unixSeconds + (2 * 24 * 60 * 60);
string jwsHeader = "{" +
""typ":"JWT"," +
""alg":"HS256"" +
"}";
byte[] jwsHeaderUtf8Bytes = Encoding.UTF8.GetBytes(jwsHeader);
string encodedJwsHeaderValue = Base64UrlEncode(jwsHeaderUtf8Bytes);
string payloadJson = "{" +
""sub":"" + userId + ""," +
""iss":"service-id"," +
""exp":" + expiry.ToString("0") +
"}";
byte[] jwsPayloadUtf8Bytes = Encoding.UTF8.GetBytes(payloadJson);
string encodedJwsPayloadValue = Base64UrlEncode(jwsPayloadUtf8Bytes);
string jwsSecuredInputValue = encodedJwsHeaderValue + "." + encodedJwsPayloadValue;
byte[] jwsSecuredInputAsciiBytes = Encoding.ASCII.GetBytes(jwsSecuredInputValue);
byte[] secretKeyBytes = Base64UrlDecode(base64UrlEncodedSecretKey);
var hmacSha256 = new HMACSHA256(secretKeyBytes);
byte[] signatureBytes = hmacSha256.ComputeHash(jwsSecuredInputAsciiBytes);
string encodedJwsSignatureValue = Base64UrlEncode(signatureBytes);
string jwt = jwsSecuredInputValue + "." + encodedJwsSignatureValue;
return jwt;
}