Dekodowanie i weryfikacja tokenu JWT przy użyciu System.IdentityModel.Tokens.Jwt

102

Używam biblioteki JWT do dekodowania tokenu internetowego Json i chciałbym przełączyć się na oficjalną implementację JWT firmy Microsoft, System.IdentityModel.Tokens.Jwt .

Dokumentacja jest bardzo skąpa, więc trudno jest mi wymyślić, jak wykonać to, co robiłem z biblioteką JWT. W przypadku biblioteki JWT istnieje metoda Decode, która pobiera token JWT zakodowany w standardzie Base64 i przekształca go w JSON, który można następnie deserializować. Chciałbym zrobić coś podobnego za pomocą System.IdentityModel.Tokens.Jwt, ale po długich poszukiwaniach nie mogę dowiedzieć się, jak to zrobić.

Co jest warte, czytam token JWT z pliku cookie do użytku z platformą tożsamości Google.

Każda pomoc będzie mile widziana.

w.brian
źródło
Oto praktyczna odpowiedź, jak pobrać certyfikaty Google i zweryfikować token - stackoverflow.com/questions/29757140/ ...
rothschild86

Odpowiedzi:

148

W pakiecie znajduje się klasa o nazwie, JwtSecurityTokenHandlerktóra pochodzi od System.IdentityModel.Tokens.SecurityTokenHandler. W WIF jest to podstawowa klasa do deserializacji i serializacji tokenów zabezpieczających.

Klasa ma ReadToken(String)metodę, która pobierze ciąg znaków JWT zakodowany w standardzie Base64 i zwróci wartość, SecurityTokenktóra reprezentuje JWT.

Ma SecurityTokenHandlerrównież ValidateToken(SecurityToken)metodę, która pobiera SecurityTokeni tworzy plik ReadOnlyCollection<ClaimsIdentity>. Zwykle w przypadku tokena JWT będzie to pojedynczy ClaimsIdentityobiekt z zestawem oświadczeń reprezentujących właściwości oryginalnego tokena JWT.

JwtSecurityTokenHandlerdefiniuje kilka dodatkowych przeciążeń ValidateToken, w szczególności ma ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)przeciążenie. TokenValidationParametersArgumentem pozwala określić certyfikat podpisywania tokenu (jako lista X509SecurityTokens). Ma również przeciążenie, które traktuje token JWT jako stringraczej niż plik SecurityToken.

Kod służący do tego jest dość skomplikowany, ale można go znaleźć w kodzie ( TokenValidationHandlerklasie) Global.asax.cx w przykładzie dewelopera o nazwie „ADAL - Natywna aplikacja do usługi REST - Uwierzytelnianie za pomocą usługi ACS za pośrednictwem okna dialogowego przeglądarki”, znajdującej się pod adresem

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

Alternatywnie JwtSecurityTokenklasa ma dodatkowe metody, które nie znajdują się w SecurityTokenklasie bazowej , takie jak Claimswłaściwość, która pobiera zawarte oświadczenia bez przechodzenia przez ClaimsIdentitykolekcję. Ma również Payloadwłaściwość, która zwraca JwtPayloadobiekt, który pozwala uzyskać surowy kod JSON tokenu. To zależy od scenariusza, które podejście jest najbardziej odpowiednie.

Ogólna (tzn. Nie związana z JWT) dokumentacja dla SecurityTokenHandlerklasy znajduje się pod adresem

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

W zależności od aplikacji można skonfigurować procedurę obsługi JWT w potoku WIF dokładnie tak, jak każdą inną procedurę obsługi.

Istnieją 3 próbki tego używanego w różnych typach zastosowań pod adresem

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

Prawdopodobnie jeden będzie odpowiadał Twoim potrzebom lub przynajmniej da się do nich dostosować.

Mike Goodwin
źródło
3
Naprawdę doceniam twoją odpowiedź. Tak więc, gdy mam już identyfikator ClaimsIdentity, jak mogę zweryfikować go pod kątem klucza publicznego? W szczególności próbuję zweryfikować token JWT zestawu narzędzi tożsamości Google z ich kluczem publicznym ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian
4
Zaktualizowałem moją odpowiedź - nie mogłem dopasować pełnego źródła do tego, ale wskazałem ci w kierunku odpowiedniej próbki programisty. Mam nadzieję, że to pomoże.
Mike Goodwin
4
@ w.brian - próbuję zrobić to samo. Mam token, który mogę zdekodować, i klucz publiczny, który chcę zweryfikować, ale nawet patrząc na te próbki, nie wiem, jak to robię. Czy masz jakieś wskazówki, który kod faktycznie Ci pomógł? Dzięki.
Barguast
27

Zastanawiam się tylko, po co w ogóle używać niektórych bibliotek do dekodowania i weryfikacji tokenu JWT.

Zakodowany token JWT można utworzyć za pomocą następującego pseudokodu

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

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

var encodedJWT = payload + "." + signature;

Jest to bardzo łatwe bez żadnej konkretnej biblioteki. Używając następującego kodu:

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

Dekodowanie tokena jest odwróconą wersją powyższego kodu. Aby zweryfikować podpis, należy wykonać ten sam i porównać część podpisu z podpisem obliczonym.

AKTUALIZACJA: Dla tych, którzy zmagają się z kodowaniem / dekodowaniem base64 urlsafe, zobacz inne pytanie SO , a także wiki i RFC

Regfor
źródło
2
Niezła odpowiedź. Chociaż, ponieważ pokazujesz tutaj podpisywanie oparte na HMAC, warto pamiętać o pewnych krytycznych lukach w bibliotekach, które implementują weryfikację HMAC, jak opisano szczegółowo w witrynie Auth0 tutaj: auth0.com/blog/2015/03/31/…
Sudhanshu Mishra
2
Czuję, że to najlepsza odpowiedź. OP zażądał informacji na temat JWT, do których odnosi się ten artykuł z wyraźnym przykładem ..
webworm
15
Ta odpowiedź wyjaśnia i demonstruje sposób en kodu w JWT, gdy pytanie jest dość wyraźnie o de kodowania. To może być fajna odpowiedź, ale jest to odpowiedź na zupełnie inne pytanie .
Deltics
2
@Deltics Myślę, że nawet dyplom informatyki nie jest potrzebny do przepisania algorytmu kodowania do dekodowania tokena. Jeśli rozumiesz, jak kodować - rozumiesz, jak dekodować
data rozpoczęcia
33
Ideą „odpowiedzi” jest odpowiedź na pytanie, a nie układanie łamigłówki przez oczekiwanie, że ktoś rozwiąże jakąś zagadkę o odwrotnej intencji. Bedsides, wiedząc, jak kodować nie nie musi oznaczać, że także wtedy wiedzieć, jak dekodowanie, ponieważ może to również dotyczyć czynienia z 3rd tokenów partyjnych i odzyskiwanie kluczy do weryfikacji podpisów, w przeciwieństwie do po prostu przy użyciu klucza do podpisania własną rękę. W każdym razie odpowiedź, która w rzeczywistości nie odpowiada na to pytanie z definicji, nie jest „ lepszą ” odpowiedzią w porównaniu z odpowiedzią , która odpowiada, czyli obserwacją, na którą odpowiadałem.
Deltics