Feb 2, 2018

Create JWT token using HMACSHA256 and no external libraries

The following code generates JWT token using HMACSHA256 algorithm. The function Sign() below can be changed to suit other algortihms.

using System.Security.Cryptography;

/// <summary>
/// Generates the JWT token
/// </summary>
private void GetJWT()
{
    Dictionary<string, object> _header;
    Dictionary<string, object> _payload;
    String _secretKey;

    try
    {
        _secretKey = ConfigurationManager.AppSettings[SITE_SECRET_KEY];

        _header = new Dictionary<string, object>
        {
            { "header1", "value1" }
        };

        /// May change as per requirement
        _payload = new Dictionary<string, object>
        {
            { "feature1", "value1" },
            { "feature2", "value2" },
            { "feature3", "value3" }
        };

        /// Creates JWT token
        var token = EncodeBytes(Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(_payload)),
                            Encoding.UTF8.GetBytes(_secretKey),
                            Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(_header)));

    }
    catch (Exception ex)
    {
        Console.Log("Exception in GetToken(): " + ex.Message);
    }
    finally
    {
        _header = null;
        _payload = null;
        _secretKey = null;
    }
}

/// <summary>
/// Encodes given binary data to JWT token and sign it using HMAC256 algorithm.        
/// </summary>
/// <param name="payload">Binary data to encode</param>
/// <param name="key">key for signing</param>
/// <returns>JWT in compact serialization form, digitally signed.</returns>
private string EncodeBytes(byte[] payload, byte[] key, byte[] headers)
{
    var bytesToSign = Encoding.UTF8.GetBytes(Serialize(headers, payload));
    byte[] signature = Sign(bytesToSign, key);

    return Serialize(headers, payload, signature);
}

/// <summary>
/// 
/// </summary>
/// <param name="parts"></param>
/// <returns></returns>
private string Serialize(params byte[][] parts)
{
    var builder = new StringBuilder();
    foreach (var part in parts)
    {
        builder.Append(Base64UrlEncode(part)).Append(".");
    }
    builder.Remove(builder.Length - 1, 1);
    return builder.ToString();
}

/// <summary>
/// 
/// </summary>
/// <param name="securedInput"></param>
/// <param name="key"></param>
/// <returns></returns>
private byte[] Sign(byte[] securedInput, byte[] key)
{
    if (!(key is byte[]))
    {
        Console.Log("Exception in Sign(): HmacUsingSha alg expectes key to be byte[] array.");
        return null;
    }

    using (var sha = new HMACSHA256(key))
    {
        return sha.ComputeHash(securedInput);
    }
}

/// <summary>
/// 
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public string Base64UrlEncode(byte[] input)
{
    var output = Convert.ToBase64String(input);
    output = output.Split('=')[0]; // Remove any trailing '='s
    output = output.Replace('+', '-'); // 62nd char of encoding
    output = output.Replace('/', '_'); // 63rd char of encoding
    return output;
}

No comments: