129 votes

Décodage et vérification du jeton JWT à l'aide de System.IdentityModel.Tokens.Jwt

J'ai utilisé le JWT pour décoder un jeton Web Json, et souhaite passer à l'implémentation JWT officielle de Microsoft, Système.IdentityModel.Tokens.Jwt .

La documentation est très rare, et j'ai du mal à comprendre comment accomplir ce que je faisais avec la bibliothèque JWT. Avec la bibliothèque JWT, il existe une méthode Decode qui prend le JWT codé en base64 et le transforme en JSON qui peut ensuite être désérialisé. J'aimerais faire quelque chose de similaire en utilisant System.IdentityModel.Tokens.Jwt, mais après une bonne dose de recherche, je n'arrive pas à trouver comment.

Pour ce que ça vaut, je lis le jeton JWT à partir d'un cookie, pour l'utiliser avec le cadre d'identité de Google.

Toute aide serait appréciée.

180voto

Mike Goodwin Points 3811

Dans le paquet, il y a une classe appelée JwtSecurityTokenHandler qui découle de System.IdentityModel.Tokens.SecurityTokenHandler . Dans le WIF, il s'agit de la classe de base pour la désérialisation et la sérialisation des jetons de sécurité.

La classe a un ReadToken(String) qui prendra votre chaîne JWT codée en base64 et renverra une méthode SecurityToken qui représente le JWT.

Le site SecurityTokenHandler dispose également d'un ValidateToken(SecurityToken) qui prend votre SecurityToken et crée un ReadOnlyCollection<ClaimsIdentity> . Habituellement, pour JWT, il s'agit d'un simple ClaimsIdentity qui possède un ensemble de revendications représentant les propriétés du JWT original.

JwtSecurityTokenHandler définit quelques surcharges supplémentaires pour ValidateToken En particulier, il a un ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters) surcharge. Le site TokenValidationParameters vous permet de spécifier le certificat de signature de jeton (comme une liste de X509SecurityTokens ). Elle possède également une surcharge qui prend le JWT en tant que string plutôt qu'un SecurityToken .

Le code pour faire cela est plutôt compliqué, mais peut être trouvé dans le code Global.asax.cx ( TokenValidationHandler ) dans l'échantillon de développement intitulé "ADAL - Native App to REST service - Authentication with ACS via Browser Dialog", situé à l'adresse suivante

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

Alternativement, le JwtSecurityToken possède des méthodes supplémentaires qui ne figurent pas dans la classe de base SecurityToken comme une classe Claims qui permet d'obtenir les réclamations contenues sans passer par la propriété ClaimsIdentity collection. Il dispose également d'un Payload qui renvoie un JwtPayload qui vous permet d'accéder au JSON brut du jeton. L'approche la plus appropriée dépend de votre scénario.

La documentation générale (c.-à-d. non spécifique à JWT) pour l'option SecurityTokenHandler La classe est à

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

En fonction de votre application, vous pouvez configurer le gestionnaire JWT dans le pipeline WIF exactement comme tout autre gestionnaire.

Il y a 3 échantillons de ce produit utilisé dans différents types d'applications à l'adresse suivante

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

Il est probable que l'un d'entre eux répondra à vos besoins ou, du moins, s'y adaptera.

35voto

Regfor Points 4259

Je me demande simplement pourquoi utiliser certaines bibliothèques pour le décodage et la vérification des jetons JWT.

Le jeton JWT codé peut être créé à l'aide des éléments suivants pseudo-code suivant

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

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

var encodedJWT = payload + "." + signature;

C'est très facile à faire sans bibliothèque spécifique. En utilisant le code suivant :

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);
    }
}

Le décodage du jeton est une version inversée du code ci-dessus. Pour vérifier la signature, vous devrez faire de même et comparer la partie signature avec la signature calculée.

MISE À JOUR : Pour ceux qui ont des difficultés à effectuer l'encodage/décodage urlsafe base64, veuillez consulter un autre document Question et aussi wiki et RFCs

4voto

Max Voisard Points 1195

J'ai eu des problèmes de version entre System.IdentityModel.Tokens y System.IdentityModel.Tokens.Jwt qui est un problème connu après la version 5.0.0.0 de Jwt. Donc, à la place, j'ai téléchargé la dernière version de Microsoft.IdentityModel.Tokens - note Microsoft - et tout a bien fonctionné. Voici un joli extrait que j'ai réalisé pour valider et décoder un jeton JWT généré par mes soins et analyser son contenu JSON.

using System.Collections.Generic;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

public static void Main()
{
    var key = "qwertyuiopasdfghjklzxcvbnm123456";
    var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(key));

    string token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NDA0MDY1MjIsImV4cCI6MTY3MTk0MjUyMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsImZvbyI6ImJhciJ9.QqcxZWEUt5YLraLRg5550Ls7aMVqm7aCUcbU7uB1qgY";

    TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = securityKey,
        RequireExpirationTime = true,
        ValidateLifetime = true,
        ValidateAudience = true,
        ValidateIssuer = true,
        ValidIssuer = "Online JWT Builder",
        ValidAudience = "www.example.com"
    };

    if (ValidateToken(token, tokenValidationParameters))
    {
        var TokenInfo = new Dictionary<string, string>();
        var handler = new JwtSecurityTokenHandler();
        var jwtSecurityToken = handler.ReadJwtToken(token);
        var claims = jwtSecurityToken.Claims.ToList();

        foreach (var claim in claims)
        {
            TokenInfo.Add(claim.Type, claim.Value);
        }

        string sub = jwtSecurityToken.Subject;
        string iss = jwtSecurityToken.Issuer;
        DateTime iat = jwtSecurityToken.IssuedAt;
        List<string> audiences = new List<string>(jwtSecurityToken.Audiences);
        DateTime exp = jwtSecurityToken.ValidTo;
        string bar;
        bool ifBar = TokenInfo.TryGetValue("foo", out bar);
        Console.WriteLine("Subject: " + sub);
        Console.WriteLine("Issuer: " + iss);
        Console.WriteLine("Issued At: " + iat);
        foreach (var member in audiences)
        {
            Console.WriteLine("Audience: " + member);
        }
        Console.WriteLine("Expiration: " + exp);
        Console.WriteLine("foo: " + bar);
    }
    Console.ReadLine();
}

private static bool ValidateToken(string token, TokenValidationParameters tvp)
{
    try
    {
        var handler = new JwtSecurityTokenHandler();
        SecurityToken securityToken;
        ClaimsPrincipal principal = handler.ValidateToken(token, tvp, out securityToken);
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        return false;
    }
}

Sortie

Subject: jrocket@example.com
Issuer: Online JWT Builder
Issued At: 12/25/2022 4:28:42 AM
Audience: www.example.com
Expiration: 12/25/2022 4:28:42 AM
foo: bar

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X