Descifrando y verificando el token JWT usando System.IdentityModel.Tokens.Jwt

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

La documentación es muy escasa, así que me está costando trabajo imaginar cómo lograr lo que he estado haciendo con la biblioteca JWT. Con la biblioteca JWT, hay 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 utilizando System.IdentityModel.Tokens.Jwt, pero después de una buena cantidad de tareas de excavación, no puedo entender 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.

Dentro del paquete hay una clase llamada JwtSecurityTokenHandler que 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) que tomará su cadena JWT codificada en base64 y devolverá un SecurityToken que representa el JWT.

El SecurityTokenHandler también tiene un método ValidateToken(SecurityToken) que toma su SecurityToken y crea un ReadOnlyCollection . Por lo general, para JWT, este contendrá un único objeto ClaimsIdentity que tiene un conjunto de declaraciones que representan las propiedades del JWT original.

JwtSecurityTokenHandler define algunas sobrecargas adicionales para ValidateToken , en particular, tiene una ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters) . El argumento TokenValidationParameters permite especificar el certificado de firma de token (como una lista de X509SecurityTokens ). También tiene una sobrecarga que toma el JWT como una string lugar de un SecurityToken .

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

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

Alternativamente, la clase JwtSecurityToken tiene métodos adicionales que no están en la clase base SecurityToken , como una propiedad Claims que obtiene los reclamos contenidos sin pasar por la colección ClaimsIdentity . También tiene una propiedad Payload que devuelve un objeto JwtPayload que le permite obtener el JSON sin formato del token. Depende de su escenario, que enfoque más apropiado.

La documentación general (es decir, no JWT específica) para la clase SecurityTokenHandler 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 de WIF exactamente como cualquier otro controlador.

Hay 3 muestras de este en uso en diferentes tipos de aplicaciones 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% 20Developer% 20Experience% 20Team & f% 5B1% 5D.Text = Azure% 20AD% 20Developer% 20Experience% 20Team

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

Me pregunto por qué usar algunas bibliotecas para la desencoding 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 de hacer 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\":\"jon.doe@eexample.com\",\"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 desencoding del token es una versión invertida del código anterior. Para verificar la firma, necesitará la misma y compare la parte de la firma con la firma calculada.

ACTUALIZACIÓN: Para quienes están luchando cómo hacer la encoding / deencoding de seguridad en la base64 por favor vea otra pregunta de SO , y también wiki y RFCs