Last active
September 22, 2023 12:05
-
-
Save AliKhadivi/0bb8bdadc762455d2819fbf712f98367 to your computer and use it in GitHub Desktop.
C# JWT HS256
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: