Decodificación y verificación del token JWT usando System.IdentityModel.Tokens.Jwt

101

He estado usando la biblioteca JWT para decodificar un Json Web Token y me gustaría cambiar a la implementación oficial de JWT de Microsoft, System.IdentityModel.Tokens.Jwt .

La documentación es muy escasa, por lo que me cuesta trabajo averiguar cómo lograr lo que he estado haciendo con la biblioteca JWT. Con la biblioteca JWT, existe un método Decode que toma el JWT codificado en base64 y lo convierte en JSON, que luego puede deserializarse. Me gustaría hacer algo similar usando System.IdentityModel.Tokens.Jwt, pero después de una buena cantidad de excavación, no puedo averiguar cómo.

Por lo que vale, estoy leyendo el token JWT de una cookie, para usar con el marco de identidad de Google.

Cualquier ayuda sería apreciada.

w.brian
fuente
Aquí hay una respuesta práctica sobre cómo obtener certificados de Google y verificar el token - stackoverflow.com/questions/29757140/…
rothschild86

Respuestas:

147

Dentro del paquete hay una clase llamada JwtSecurityTokenHandlerque deriva de System.IdentityModel.Tokens.SecurityTokenHandler. En WIF, esta es la clase principal para deserializar y serializar tokens de seguridad.

La clase tiene un ReadToken(String)método que tomará su cadena JWT codificada en base64 y devuelve un SecurityTokenque representa el JWT.

El SecurityTokenHandlertambién tiene un ValidateToken(SecurityToken)método que lleva su SecurityTokeny crea una ReadOnlyCollection<ClaimsIdentity>. Por lo general, para JWT, esto contendrá un solo ClaimsIdentityobjeto que tiene un conjunto de reclamos que representan las propiedades del JWT original.

JwtSecurityTokenHandlerdefine algunas sobrecargas adicionales ValidateToken, en particular, tiene una ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)sobrecarga. El TokenValidationParametersargumento le permite especificar el certificado de firma del token (como una lista de X509SecurityTokens). También tiene una sobrecarga que toma el JWT como stringunSecurityToken .

El código para hacer esto es bastante complicado, pero se puede encontrar en el código ( TokenValidationHandlerclase) Global.asax.cx en la muestra del desarrollador llamada "ADAL - Aplicación nativa al servicio REST - Autenticación con ACS a través del cuadro de diálogo del navegador", ubicado en

http://code.msdn.microsoft.com/AAL-Native-App-to-REST-de57f2cc

Alternativamente, la JwtSecurityTokenclase tiene métodos adicionales que no están en la SecurityTokenclase base , como una Claimspropiedad que obtiene las notificaciones contenidas sin pasar por la ClaimsIdentitycolección. También tiene una Payloadpropiedad que devuelve unJwtPayload objeto que le permite obtener el JSON sin procesar del token. Depende de su escenario cuál es el enfoque más adecuado.

La documentación general (es decir, no específica de JWT) para la SecurityTokenHandlerclase está en

http://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.securitytokenhandler.aspx

Dependiendo de su aplicación, puede configurar el controlador JWT en la canalización WIF exactamente como cualquier otro controlador.

Hay 3 muestras en uso en diferentes tipos de aplicación en

http://code.msdn.microsoft.com/site/search?f%5B0%5D.Type=SearchText&f%5B0%5D.Value=aal&f%5B1%5D.Type=User&f%5B1%5D.Value=Azure% 20AD% 20Desarrollador% 20Experiencia% 20Team &f% 5B1% 5D.Text = Azure% 20AD% 20Desarrollador% 20Experiencia% 20Team

Probablemente, uno se adaptará a sus necesidades o al menos se adaptará a ellas.

Mike Goodwin
fuente
3
Realmente aprecio tu respuesta. Entonces, una vez que tengo ClaimsIdentity, ¿cómo lo verifico con una clave pública? Específicamente, estoy tratando de verificar un kit de herramientas de identidad de Google JWT con su clave pública ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian
4
Actualicé mi respuesta: no pude incluir la fuente completa para esto, pero le indiqué la dirección de la muestra de desarrollador adecuada. Espero eso ayude.
Mike Goodwin
4
@ w.brian - Estoy intentando hacer lo mismo. Tengo un token que puedo decodificar y una clave pública que quiero verificar, pero incluso mirando estas muestras estoy luchando por ver cómo hago esto. ¿Tiene alguna sugerencia sobre qué código realmente le ayudó? Gracias.
Barguast
26

Me pregunto por qué usar algunas bibliotecas para la decodificación y verificación de tokens JWT.

El token JWT codificado se puede crear usando el siguiente pseudocódigo

var headers = base64URLencode(myHeaders);
var claims = base64URLencode(myClaims);
var payload = header + "." + claims;

var signature = base64URLencode(HMACSHA256(payload, secret));

var encodedJWT = payload + "." + signature;

Es muy fácil hacerlo sin una biblioteca específica. Usando el siguiente código:

using System;
using System.Text;
using System.Security.Cryptography;

public class Program
{   
    // More info: https://stormpath.com/blog/jwt-the-right-way/
    public static void Main()
    {           
        var header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
        var claims = "{\"sub\":\"1047986\",\"email\":\"[email protected]\",\"given_name\":\"John\",\"family_name\":\"Doe\",\"primarysid\":\"b521a2af99bfdc65e04010ac1d046ff5\",\"iss\":\"http://example.com\",\"aud\":\"myapp\",\"exp\":1460555281,\"nbf\":1457963281}";

        var b64header = Convert.ToBase64String(Encoding.UTF8.GetBytes(header))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");
        var b64claims = Convert.ToBase64String(Encoding.UTF8.GetBytes(claims))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        var payload = b64header + "." + b64claims;
        Console.WriteLine("JWT without sig:    " + payload);

        byte[] key = Convert.FromBase64String("mPorwQB8kMDNQeeYO35KOrMMFn6rFVmbIohBphJPnp4=");
        byte[] message = Encoding.UTF8.GetBytes(payload);

        string sig = Convert.ToBase64String(HashHMAC(key, message))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        Console.WriteLine("JWT with signature: " + payload + "." + sig);        
    }

    private static byte[] HashHMAC(byte[] key, byte[] message)
    {
        var hash = new HMACSHA256(key);
        return hash.ComputeHash(message);
    }
}

La decodificación del token es la versión inversa del código anterior. Para verificar la firma, necesitará la misma y comparar la parte de la firma con la firma calculada.

ACTUALIZACIÓN: Para aquellos que tienen dificultades para realizar la codificación / decodificación segura de URL en base64, consulte otra pregunta SO , y también wiki y RFC

Regfor
fuente
2
Buena respuesta. Aunque, dado que muestra la firma basada en HMAC aquí, puede tener sentido estar al tanto de algunas vulnerabilidades críticas en las bibliotecas que implementan la verificación de HMAC como se detalla en el sitio de Auth0 aquí: auth0.com/blog/2015/03/31/…
Sudhanshu Mishra
2
Siento que esta es la mejor respuesta. El OP solicitó información sobre JWT específicamente que este artículo aborda con un ejemplo claro ..
webworm
13
Esta respuesta explica y demuestra cómo es el código de una JWT cuando la pregunta es bastante clara acerca de la codificación. Esta puede ser una buena respuesta, pero es una respuesta a una pregunta completamente diferente .
Deltics
2
@Deltics Creo que ni siquiera se necesita un título en informática para reescribir el algoritmo de codificación para decodificar el token. Si entiende cómo codificar
sabe
31
La idea de una "respuesta" es abordar una pregunta, no plantear un rompecabezas esperando que alguien resuelva algún tipo de rompecabezas de intención inversa. Al lado de la cama, saber cómo codificar no significa necesariamente que también sepa cómo decodificar, ya que esto también puede implicar tratar con tokens de terceros y recuperar claves para verificar sus firmas, en lugar de simplemente usar una clave para firmar la suya. En cualquier caso, una respuesta que no responde realmente a la pregunta por definición no es la " mejor " respuesta en comparación con una que , que es la observación a la que estaba respondiendo.
Deltics