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

101

J'utilise la bibliothèque JWT pour décoder un jeton Web Json et je souhaite passer à l'implémentation officielle JWT de Microsoft, System.IdentityModel.Tokens.Jwt .

La documentation est très rare, donc j'ai du mal à comprendre comment accomplir ce que j'ai fait avec la bibliothèque JWT. Avec la bibliothèque JWT, il existe une méthode Decode qui prend le JWT encodé 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 pas mal de fouilles, je ne peux pas comprendre comment.

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

Toute aide serait appréciée.

avec Brian
la source
Voici une réponse pratique sur la façon de récupérer les certificats Google et de vérifier le jeton - stackoverflow.com/questions/29757140/…
rothschild86

Réponses:

147

Dans le package, il y a une classe appelée JwtSecurityTokenHandlerqui dérive de System.IdentityModel.Tokens.SecurityTokenHandler. Dans WIF, il s'agit de la classe principale pour la désérialisation et la sérialisation des jetons de sécurité.

La classe a une ReadToken(String)méthode qui prendra votre chaîne JWT encodée en base64 et retourne a SecurityTokenqui représente le JWT.

Le a SecurityTokenHandlerégalement une ValidateToken(SecurityToken)méthode qui prend votre SecurityTokenet crée un fichier ReadOnlyCollection<ClaimsIdentity>. Habituellement, pour JWT, celui-ci contiendra un seul ClaimsIdentityobjet qui a un ensemble de revendications représentant les propriétés du JWT d'origine.

JwtSecurityTokenHandlerdéfinit des surcharges supplémentaires car ValidateToken, en particulier, il a une ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)surcharge. L' TokenValidationParametersargument vous permet de spécifier le certificat de signature de jeton (sous forme de liste de X509SecurityTokens). Il a également une surcharge qui prend le JWT stringplutôt que comme un SecurityToken.

Le code pour ce faire est assez compliqué, mais peut être trouvé dans le code Global.asax.cx ( TokenValidationHandlerclasse) dans l'exemple de développeur appelé «ADAL - Service Native App to REST - Authentification avec ACS via Browser Dialog», situé à l'adresse

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

Sinon, la JwtSecurityTokenclasse a des méthodes supplémentaires qui ne sont pas sur la SecurityTokenclasse de base , comme une Claimspropriété qui obtient les revendications contenues sans passer par la ClaimsIdentitycollection. Il possède également une Payloadpropriété qui renvoie un JwtPayloadobjet qui vous permet d'accéder au JSON brut du jeton. Cela dépend de votre scénario de l'approche la plus appropriée.

La documentation générale (c'est-à-dire non spécifique à JWT) pour la SecurityTokenHandlerclasse 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 n'importe quel autre gestionnaire.

Il y en a 3 exemples en cours d'utilisation dans différents types d'applications à

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

Probablement, l'un répondra à vos besoins ou du moins sera adaptable à eux.

Mike Goodwin
la source
3
J'apprécie vraiment votre réponse. Donc, une fois que j'ai la ClaimsIdentity, comment puis-je la vérifier par rapport à une clé publique? Plus précisément, j'essaie de vérifier un JWT de boîte à outils d'identité Google par rapport à leur clé publique ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian
4
Mise à jour de ma réponse - Je ne pouvais pas insérer la source complète pour cela, mais je vous ai orienté vers l'exemple de développeur approprié. J'espère que ça aide.
Mike Goodwin
4
@ w.brian - J'essaye de faire de même. J'ai un jeton que je peux décoder et une clé publique que je veux vérifier, mais même en regardant ces exemples, j'ai du mal à voir comment je fais cela. Avez-vous des indications sur le code qui vous a réellement aidé? Merci.
Barguast
26

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 du pseudocode suivant

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

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

var encodedJWT = payload + "." + signature;

Il est très facile de se passer de 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\":\"[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);
    }
}

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

MISE À JOUR: Pour ceux qui ont du mal à faire l'encodage / décodage urlsafe base64, veuillez consulter une autre question SO , ainsi que le wiki et les RFC

Regfor
la source
2
Bonne réponse. Bien que puisque vous montrez la signature basée sur HMAC ici, il peut être judicieux d'être conscient de certaines vulnérabilités critiques dans les bibliothèques qui implémentent la vérification HMAC comme détaillé sur le site Auth0 ici: auth0.com/blog/2015/03/31/…
Sudhanshu Mishra
2
Je pense que c'est la meilleure réponse. L'OP a demandé des informations sur JWT spécifiquement auxquelles cet article traite avec un exemple clair ..
webworm
13
Cette réponse explique et montre comment en un code de JWT lorsque la question est tout à fait clairement au sujet de codage. C'est peut-être une bonne réponse, mais c'est une réponse à une question entièrement différente .
Deltics
2
@Deltics Je pense que même un diplôme en informatique n'est pas nécessaire pour réécrire l'algorithme de codage pour décoder le jeton. Si vous comprenez comment encoder - vous comprenez comment décoder
Reg pour le
31
L'idée d'une «réponse» est d'aborder une question, pas de poser un casse-tête en s'attendant à ce que quelqu'un résolve une sorte de casse-tête à contre-intention. Au chevet, savoir encoder ne signifie pas nécessairement que vous savez également comment décoder, car cela peut également impliquer de traiter des jetons tiers et de récupérer des clés pour vérifier leurs signatures, plutôt que d'utiliser simplement une clé pour signer la vôtre. En tout état de cause, une réponse qui ne répond pas réellement à la question par définition n'est pas la " meilleure " réponse par rapport à une réponse qui le fait , ce à quoi je répondais.
Deltics