From d848369ddc3f31838dac386a8fdfbf2ad495ecd3 Mon Sep 17 00:00:00 2001 From: Alexander Batishchev Date: Tue, 13 Sep 2016 15:07:21 -0700 Subject: [PATCH] Ordering overloads of Encode() and Decode(). Moving private methods to the bottom. (#51) --- JWT/JWT.cs | 192 ++++++++++++++++++++++++++--------------------------- 1 file changed, 95 insertions(+), 97 deletions(-) diff --git a/JWT/JWT.cs b/JWT/JWT.cs index 7447e853c..264da0f68 100644 --- a/JWT/JWT.cs +++ b/JWT/JWT.cs @@ -5,7 +5,7 @@ namespace JWT { - /// + /// /// Provides methods for encoding and decoding JSON Web Tokens. /// public static class JsonWebToken @@ -30,35 +30,15 @@ static JsonWebToken() } /// - /// Creates a JWT given a header, a payload, the signing key, and the algorithm to use. + /// Creates a JWT given a payload, the signing key, and the algorithm to use. /// - /// An arbitrary set of extra headers. Will be augmented with the standard "typ" and "alg" headers. /// An arbitrary payload (must be serializable to JSON via ). - /// The key bytes used to sign the token. + /// The key used to sign the token. /// The hash algorithm to use. /// The generated JWT. - public static string Encode(IDictionary extraHeaders, object payload, byte[] key, JwtHashAlgorithm algorithm) + public static string Encode(object payload, string key, JwtHashAlgorithm algorithm) { - var segments = new List(); - var header = new Dictionary(extraHeaders) - { - { "typ", "JWT" }, - { "alg", algorithm.ToString() } - }; - - var headerBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(header)); - var payloadBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(payload)); - - segments.Add(Base64UrlEncode(headerBytes)); - segments.Add(Base64UrlEncode(payloadBytes)); - - var stringToSign = string.Join(".", segments.ToArray()); - var bytesToSign = Encoding.UTF8.GetBytes(stringToSign); - - var signature = HashAlgorithms[algorithm](key, bytesToSign); - segments.Add(Base64UrlEncode(signature)); - - return string.Join(".", segments.ToArray()); + return Encode(new Dictionary(), payload, Encoding.UTF8.GetBytes(key), algorithm); } /// @@ -87,15 +67,49 @@ public static string Encode(IDictionary extraHeaders, object pay } /// - /// Creates a JWT given a payload, the signing key, and the algorithm to use. + /// Creates a JWT given a header, a payload, the signing key, and the algorithm to use. /// + /// An arbitrary set of extra headers. Will be augmented with the standard "typ" and "alg" headers. /// An arbitrary payload (must be serializable to JSON via ). - /// The key used to sign the token. + /// The key bytes used to sign the token. /// The hash algorithm to use. /// The generated JWT. - public static string Encode(object payload, string key, JwtHashAlgorithm algorithm) + public static string Encode(IDictionary extraHeaders, object payload, byte[] key, JwtHashAlgorithm algorithm) { - return Encode(new Dictionary(), payload, Encoding.UTF8.GetBytes(key), algorithm); + var segments = new List(); + var header = new Dictionary(extraHeaders) + { + { "typ", "JWT" }, + { "alg", algorithm.ToString() } + }; + + var headerBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(header)); + var payloadBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(payload)); + + segments.Add(Base64UrlEncode(headerBytes)); + segments.Add(Base64UrlEncode(payloadBytes)); + + var stringToSign = string.Join(".", segments.ToArray()); + var bytesToSign = Encoding.UTF8.GetBytes(stringToSign); + + var signature = HashAlgorithms[algorithm](key, bytesToSign); + segments.Add(Base64UrlEncode(signature)); + + return string.Join(".", segments.ToArray()); + } + + /// + /// Given a JWT, decode it and return the JSON payload. + /// + /// The JWT. + /// The key that was used to sign the JWT. + /// Whether to verify the signature (default is true). + /// A string containing the JSON payload. + /// Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm. + /// Thrown if the verify parameter was true and the token has an expired exp claim. + public static string Decode(string token, string key, bool verify = true) + { + return Decode(token, Encoding.UTF8.GetBytes(key), verify); } /// @@ -138,48 +152,18 @@ public static string Decode(string token, byte[] key, bool verify = true) return payloadJson; } - private static void Verify(string decodedCrypto, string decodedSignature, string payloadJson) - { - if (decodedCrypto != decodedSignature) - { - throw new SignatureVerificationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature)); - } - - // verify exp claim https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.4 - var payloadData = JsonSerializer.Deserialize>(payloadJson); - object expObj; - if (!payloadData.TryGetValue("exp", out expObj) || expObj == null) - { - return; - } - int expInt; - try - { - expInt = Convert.ToInt32(expObj); - } - catch (FormatException) - { - throw new SignatureVerificationException("Claim 'exp' must be an integer."); - } - var secondsSinceEpoch = Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds); - if (secondsSinceEpoch >= expInt) - { - throw new TokenExpiredException("Token has expired."); - } - } - /// - /// Given a JWT, decode it and return the JSON payload. + /// Given a JWT, decode it and return the payload as an object (by deserializing it with ). /// /// The JWT. /// The key that was used to sign the JWT. /// Whether to verify the signature (default is true). - /// A string containing the JSON payload. + /// An object representing the payload. /// Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm. /// Thrown if the verify parameter was true and the token has an expired exp claim. - public static string Decode(string token, string key, bool verify = true) + public static object DecodeToObject(string token, string key, bool verify = true) { - return Decode(token, Encoding.UTF8.GetBytes(key), verify); + return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify); } /// @@ -194,22 +178,22 @@ public static string Decode(string token, string key, bool verify = true) public static object DecodeToObject(string token, byte[] key, bool verify = true) { var payloadJson = Decode(token, key, verify); - var payloadData = JsonSerializer.Deserialize>(payloadJson); - return payloadData; + return JsonSerializer.Deserialize>(payloadJson); } /// /// Given a JWT, decode it and return the payload as an object (by deserializing it with ). /// + /// The to return /// The JWT. /// The key that was used to sign the JWT. /// Whether to verify the signature (default is true). /// An object representing the payload. /// Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm. /// Thrown if the verify parameter was true and the token has an expired exp claim. - public static object DecodeToObject(string token, string key, bool verify = true) + public static T DecodeToObject(string token, string key, bool verify = true) { - return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify); + return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify); } /// @@ -225,37 +209,10 @@ public static object DecodeToObject(string token, string key, bool verify = true public static T DecodeToObject(string token, byte[] key, bool verify = true) { var payloadJson = Decode(token, key, verify); - var payloadData = JsonSerializer.Deserialize(payloadJson); - return payloadData; - } - - /// - /// Given a JWT, decode it and return the payload as an object (by deserializing it with ). - /// - /// The to return - /// The JWT. - /// The key that was used to sign the JWT. - /// Whether to verify the signature (default is true). - /// An object representing the payload. - /// Thrown if the verify parameter was true and the signature was NOT valid or if the JWT was signed with an unsupported algorithm. - /// Thrown if the verify parameter was true and the token has an expired exp claim. - public static T DecodeToObject(string token, string key, bool verify = true) - { - return DecodeToObject(token, Encoding.UTF8.GetBytes(key), verify); - } - - private static JwtHashAlgorithm GetHashAlgorithm(string algorithm) - { - switch (algorithm) - { - case "HS256": return JwtHashAlgorithm.HS256; - case "HS384": return JwtHashAlgorithm.HS384; - case "HS512": return JwtHashAlgorithm.HS512; - default: throw new SignatureVerificationException("Algorithm not supported."); - } + return JsonSerializer.Deserialize(payloadJson); } - // from JWT spec + /// From JWT spec public static string Base64UrlEncode(byte[] input) { var output = Convert.ToBase64String(input); @@ -265,7 +222,7 @@ public static string Base64UrlEncode(byte[] input) return output; } - // from JWT spec + /// From JWT spec public static byte[] Base64UrlDecode(string input) { var output = input; @@ -281,5 +238,46 @@ public static byte[] Base64UrlDecode(string input) var converted = Convert.FromBase64String(output); // Standard base64 decoder return converted; } + + private static JwtHashAlgorithm GetHashAlgorithm(string algorithm) + { + switch (algorithm) + { + case "HS256": return JwtHashAlgorithm.HS256; + case "HS384": return JwtHashAlgorithm.HS384; + case "HS512": return JwtHashAlgorithm.HS512; + default: throw new SignatureVerificationException("Algorithm not supported."); + } + } + + private static void Verify(string decodedCrypto, string decodedSignature, string payloadJson) + { + if (decodedCrypto != decodedSignature) + { + throw new SignatureVerificationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature)); + } + + // verify exp claim https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.4 + var payloadData = JsonSerializer.Deserialize>(payloadJson); + object expObj; + if (!payloadData.TryGetValue("exp", out expObj) || expObj == null) + { + return; + } + int expInt; + try + { + expInt = Convert.ToInt32(expObj); + } + catch (FormatException) + { + throw new SignatureVerificationException("Claim 'exp' must be an integer."); + } + var secondsSinceEpoch = Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds); + if (secondsSinceEpoch >= expInt) + { + throw new TokenExpiredException("Token has expired."); + } + } } } \ No newline at end of file