Skip to content

Instantly share code, notes, and snippets.

@AliKhadivi
Last active September 22, 2023 12:05
Show Gist options
  • Save AliKhadivi/0bb8bdadc762455d2819fbf712f98367 to your computer and use it in GitHub Desktop.
Save AliKhadivi/0bb8bdadc762455d2819fbf712f98367 to your computer and use it in GitHub Desktop.
C# JWT HS256
public enum JwtStatusCode
{
OK,
NotSupported,
NotVaild,
SignNotVaild,
Expired,
UnknownError
}
public class JwtToken
{
public bool IsValid { get; set; }
public JwtStatusCode StatusCode { get; set; }
public JsonElement head { get; set; }
public JsonElement payload { get; set; }
}
public class JwtToken<T>
{
public bool IsValid { get; set; }
public JwtStatusCode StatusCode { get; set; }
public JsonElement head { get; set; }
public T? payload { get; set; }
}
public class JwtOptions
{
public bool ExpireCheck { get; set; } = true;
public bool SignCheck { get; set; } = true;
}
public static class JwtHelper
{
private static string HmacSha256Digest(this string message, string secret)
{
ASCIIEncoding encoding = new();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return Convert.ToBase64String(bytes)
.Replace('+', '-') // 62nd char of encoding
.Replace('/', '_') // 63rd char of encoding
.Replace("=", ""); // Remove any trailing '='s
}
private static string HmacSha512Digest(this string message, string secret)
{
ASCIIEncoding encoding = new();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA512 cryptographer = new(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return Convert.ToBase64String(bytes)
.Replace('+', '-') // 62nd char of encoding
.Replace('/', '_') // 63rd char of encoding
.Replace("=", ""); // Remove any trailing '='s
}
public static string Base64UrlEncode(string input)
{
return Convert.ToBase64String(Encoding.UTF8.GetBytes(input))
.Replace('+', '-') // 62nd char of encoding
.Replace('/', '_') // 63rd char of encoding
.Replace("=", ""); // Remove any trailing '='s
}
public static string Base64UrlDecode(string input)
{
string s = input.Replace('-', '+') // 62nd char of encoding
.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 Encoding.UTF8.GetString(Convert.FromBase64String(s));
}
public static byte[] Base64UrlDecodeBytes(string input)
{
string s = input.Replace('-', '+') // 62nd char of encoding
.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);
}
public static string EncodeHS256(object payload, string secret)
{
var head = new { alg = "HS256", typ = "JWT" };
var headJson = JsonSerializer.Serialize(head);
var payloadJson = JsonSerializer.Serialize(payload);
return Base64UrlEncode(headJson) + "." +
Base64UrlEncode(payloadJson) + "." +
HmacSha256Digest(Base64UrlEncode(headJson) + "." +
Base64UrlEncode(payloadJson), secret);
}
public static string EncodeHS512(object payload, string secret)
{
var head = new { alg = "HS512", typ = "JWT" };
var headJson = JsonSerializer.Serialize(head);
var payloadJson = JsonSerializer.Serialize(payload);
return Base64UrlEncode(headJson) + "." +
Base64UrlEncode(payloadJson) + "." +
HmacSha512Digest(Base64UrlEncode(headJson) + "." +
Base64UrlEncode(payloadJson), secret);
}
public static JwtToken<T> Decode<T>(string token, string secret, JwtOptions? options = null)
{
var data = Decode(token, secret, options);
return new JwtToken<T>()
{
IsValid = data.IsValid,
head = data.head,
StatusCode = data.StatusCode,
payload = data.payload.Deserialize<T>()
};
}
public static JwtToken Decode(string token, string secret, JwtOptions? options = null)
{
var jwt = new JwtToken() { StatusCode = JwtStatusCode.OK, IsValid = false };
options ??= new JwtOptions();
try
{
var parts = token.Split('.');
if (parts.Length != 3)
{
jwt.StatusCode = JwtStatusCode.NotVaild;
return jwt;
}
var headerBytes = Base64UrlDecodeBytes(parts[0]);
var payloadBytes = Base64UrlDecodeBytes(parts[1]);
//var signatureBytes = Convert.FromBase64String(parts[2].Replace("_", "/").Replace("-", "+"));
var header = JsonDocument.Parse(headerBytes);
string sign;
switch (header.RootElement.GetProperty("alg").GetString())
{
case "HS256":
sign = HmacSha256Digest(parts[0] + "." + parts[1], secret);
break;
case "HS512":
sign = HmacSha512Digest(parts[0] + "." + parts[1], secret);
break;
default:
jwt.StatusCode = JwtStatusCode.NotSupported;
return jwt;
}
var payload = JsonDocument.Parse(payloadBytes);
//var sign = HmacSha256Digest(parts[0] + "." + parts[1],secret);
if (sign != parts[2] && options.SignCheck)
{
jwt.StatusCode = JwtStatusCode.SignNotVaild;
return jwt;
}
var date = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (options.ExpireCheck)
{
if (payload.RootElement.TryGetProperty("exp", out JsonElement exp))
{
if (exp.GetInt64() < date)
{
jwt.StatusCode = JwtStatusCode.Expired;
return jwt;
}
}
if (payload.RootElement.TryGetProperty("nbf", out JsonElement nbf))
{
if (nbf.GetInt64() > date)
{
jwt.StatusCode = JwtStatusCode.Expired;
return jwt;
}
}
}
jwt.head = header.RootElement;
jwt.payload = payload.RootElement;
jwt.IsValid = true;
return jwt;
}
catch (Exception)
{
jwt.StatusCode = JwtStatusCode.UnknownError;
return jwt;
}
}
}
@AliKhadivi
Copy link
Author

Usage:

var secret = "123";
var nbf = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var exp = DateTimeOffset.UtcNow.AddHours(5).ToUnixTimeSeconds();

var data = new { nbf, exp, data = "ali" };
var token = jwtHelper.Encode(data, secret);
var jwttoken = jwtHelper.Decode(token, secret);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment