validatetoken tokens jwtsecuritytokenhandler identitymodel example c# rsa jwt openid-connect jwk

tokens - Verificación de JWT firmado con el algoritmo RS256 usando una clave pública en C#



jwtsecuritytokenhandler validatetoken (3)

Aquí hay un ejemplo que utiliza IdentityModel.Tokens.Jwt para validación:

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters( new RSAParameters() { Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), Exponent = FromBase64Url("AQAB") }); var validationParameters = new TokenValidationParameters { RequireExpirationTime = true, RequireSignedTokens = true, ValidateAudience = false, ValidateIssuer = false, ValidateLifetime = false, IssuerSigningKey = new RsaSecurityKey(rsa) }; SecurityToken validatedSecurityToken = null; var handler = new JwtSecurityTokenHandler(); handler.ValidateToken(tokenStr, validationParameters, out validatedSecurityToken); JwtSecurityToken validatedJwt = validatedSecurityToken as JwtSecurityToken;

Ok, entiendo que la pregunta que estoy haciendo puede ser bastante obvia, pero desafortunadamente no tengo conocimiento sobre este tema y esta tarea parece ser bastante difícil para mí.

Tengo un token de identificación (JWT) devuelto por OpenID Connect Provider. Aquí está:

eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ

Su cabecera y carga útil se decodifican así:

{ "kid":"1e9gdk7", "alg":"RS256" }. { "iss": "http://server.example.com", "sub": "248289761001", "aud": "s6BhdRkqt3", "nonce": "n-0S6_WzA2Mj", "exp": 1311281970, "iat": 1311280970, "c_hash": "LDktKdoQak3Pk0cnXxCltA" }

Desde el descubrimiento del proveedor de IODC, tengo la clave pública (JWK):

{ "kty":"RSA", "kid":"1e9gdk7", "n":"w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ", "e":"AQAB" }

Entonces, la pregunta es, ¿cómo exactamente en C # puedo verificar este JWT usando la clave pública para el algoritmo RS256 que tengo? Sería genial si hubiera un buen tutorial que describiera este procedimiento explícitamente. Sin embargo, un ejemplo de cómo hacerlo utilizando System.IdentityModel.Tokens.Jwt también funcionará bien.

ACTUALIZACIÓN: Entiendo que debo hacer algo como el código a continuación, pero no tengo idea de dónde obtener la '' clave '' para calcular el hash SHA256.

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; string[] tokenParts = tokenStr.Split(''.''); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters( new RSAParameters() { Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), Exponent = FromBase64Url("AQAB") }); HMACSHA256 sha = new HMACSHA256(key); byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + ''.'' + tokenParts[1])); byte[] signature = rsa.Encrypt(hash, false); string strSignature = Base64UrlEncode(signature); if (String.Compare(strSignature, tokenParts[2], false) == 0) return true;


Gracias a Jwilleke , tengo una solución. Para verificar la firma RS256 de un JWT, es necesario usar la clase RSAPKCS1SignatureDeformatter y su método msdn.microsoft.com/en-us/library/0h05c7e2(v=vs.110).aspx .

Aquí está el código exacto para mis datos de muestra:

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; string[] tokenParts = tokenStr.Split(''.''); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.ImportParameters( new RSAParameters() { Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), Exponent = FromBase64Url("AQAB") }); SHA256 sha256 = SHA256.Create(); byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + ''.'' + tokenParts[1])); RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa); rsaDeformatter.SetHashAlgorithm("SHA256"); if (rsaDeformatter.VerifySignature(hash, FromBase64Url(tokenParts[2]))) MessageBox.Show("Signature is verified"); //... static byte[] FromBase64Url(string base64Url) { string padded = base64Url.Length % 4 == 0 ? base64Url : base64Url + "====".Substring(base64Url.Length % 4); string base64 = padded.Replace("_", "/") .Replace("-", "+"); return Convert.FromBase64String(base64); }


Para cualquier persona que esté buscando un método rápido para validar RS256 con una clave pública que tenga "----- COMENZAR LA CLAVE PÚBLICA -----" / "----- TECLA PÚBLICA FINAL ------"

Aquí hay dos métodos con la ayuda de BouncyCastle .

public bool ValidateJasonWebToken(string fullKey, string jwtToken) { try { var rs256Token = fullKey.Replace("-----BEGIN PUBLIC KEY-----", ""); rs256Token = rs256Token.Replace("-----END PUBLIC KEY-----", ""); rs256Token = rs256Token.Replace("/n", ""); Validate(jwtToken, rs256Token); return true; } catch (Exception e) { Console.WriteLine(e); return false; } } private void Validate(string token, string key) { var keyBytes = Convert.FromBase64String(key); // your key here AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes); RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter; RSAParameters rsaParameters = new RSAParameters { Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(), Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned() }; using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(rsaParameters); var validationParameters = new TokenValidationParameters() { RequireExpirationTime = false, RequireSignedTokens = true, ValidateAudience = false, ValidateIssuer = false, IssuerSigningKey = new RsaSecurityKey(rsa) }; var handler = new JwtSecurityTokenHandler(); var result = handler.ValidateToken(token, validationParameters, out var validatedToken); } }

Esta es una combinación de http://codingstill.com/2016/01/verify-jwt-token-signed-with-rs256-using-the-public-key/ y @olaf answer que usa system.IdentityModel.Tokens.Jwt