Расшифровка и проверка токена JWT с использованием System.IdentityModel.Tokens.Jwt

102

Я использовал библиотеку JWT для декодирования веб-токена Json и хотел бы перейти на официальную реализацию JWT от Microsoft, System.IdentityModel.Tokens.Jwt .

Документация очень скудная, поэтому мне сложно понять, как добиться того, что я делал с библиотекой JWT. В библиотеке JWT есть метод Decode, который берет JWT в кодировке base64 и превращает его в JSON, который затем можно десериализовать. Я хотел бы сделать что-то подобное с помощью System.IdentityModel.Tokens.Jwt, но после изрядного количества копаний не могу понять, как это сделать.

Как бы то ни было, я читаю токен JWT из файла cookie для использования с платформой идентификации Google.

Любая помощь будет оценена.

w.brian
источник
1
stackoverflow.com/questions/52928670/… ?
Роман Покровский
Вот практический ответ о том, как получить сертификаты Google и проверить токен - stackoverflow.com/questions/29757140/…
rothschild86

Ответы:

148

Внутри пакета есть класс, JwtSecurityTokenHandlerпроизводный от System.IdentityModel.Tokens.SecurityTokenHandler. В WIF это основной класс для десериализации и сериализации токенов безопасности.

У класса есть ReadToken(String)метод, который принимает вашу строку JWT в кодировке base64 и возвращает a, SecurityTokenкоторый представляет JWT.

SecurityTokenHandlerТакже есть ValidateToken(SecurityToken)метод , который принимает ваш SecurityTokenи создает ReadOnlyCollection<ClaimsIdentity>. Обычно для JWT это будет один ClaimsIdentityобъект с набором утверждений, представляющих свойства исходного JWT.

JwtSecurityTokenHandlerопределяет некоторые дополнительные перегрузки ValidateToken, в частности, для ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)перегрузки. TokenValidationParametersАргумент позволяет указать сертификат маркеров подписи (в виде списка X509SecurityTokens). Он также имеет перегрузку, которая воспринимает JWT как файл, stringа не как SecurityToken.

Код для этого довольно сложен, но его можно найти в коде ( TokenValidationHandlerклассе) Global.asax.cx в образце разработчика под названием «ADAL - Собственное приложение для службы REST - Аутентификация с ACS через диалог браузера», расположенном по адресу

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

В качестве альтернативы у JwtSecurityTokenкласса есть дополнительные методы, которых нет в базовом SecurityTokenклассе, например Claimsсвойство, которое получает содержащиеся утверждения без прохождения через ClaimsIdentityколлекцию. У него также есть Payloadсвойство, которое возвращает JwtPayloadобъект, который позволяет вам получить необработанный JSON токена. Выбор наиболее подходящего подхода зависит от вашего сценария.

Общая (т.е. не относящаяся к JWT) документация для SecurityTokenHandlerкласса находится по адресу

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

В зависимости от вашего приложения вы можете настроить обработчик JWT в конвейер WIF точно так же, как любой другой обработчик.

Три его образца используются в различных приложениях по адресу:

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

Возможно, один будет соответствовать вашим потребностям или, по крайней мере, будет адаптирован к ним.

Майк Гудвин
источник
3
Я очень ценю твой ответ. Итак, если у меня есть ClaimsIdentity, как мне проверить его по открытому ключу? В частности, я пытаюсь проверить JWT инструментария идентификации Google по их открытому ключу ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian
4
Обновил свой ответ - я не смог вместить полный исходный код для этого, но я указал вам в направлении соответствующего образца разработчика. Надеюсь, поможет.
Майк Гудвин,
4
@ w.brian - Я пытаюсь сделать то же самое. У меня есть токен, который я могу декодировать, и открытый ключ, который я хочу проверить, но даже глядя на эти образцы, я изо всех сил пытаюсь понять, как я это делаю. У вас есть какие-нибудь указания на то, какой код вам действительно помог? Спасибо.
Barguast, 09
27

Мне просто интересно, зачем вообще использовать некоторые библиотеки для декодирования и проверки токена JWT.

Закодированный токен JWT можно создать, используя следующий псевдокод

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

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

var encodedJWT = payload + "." + signature;

Очень легко обойтись без какой-либо конкретной библиотеки. Используя следующий код:

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

Декодирование токена - это обратная версия кода, приведенного выше. Для проверки подписи вам потребуется то же самое и сравнить часть подписи с рассчитанной подписью.

ОБНОВЛЕНИЕ: для тех, кто борется за кодирование / декодирование urlsafe base64, см. Другой вопрос SO , а также вики и RFC

Регфор
источник
2
Хороший ответ. Хотя, поскольку вы показываете здесь подписывание на основе HMAC, возможно, имеет смысл знать о некоторых критических уязвимостях в библиотеках, реализующих проверку HMAC, как подробно описано на сайте Auth0 здесь: auth0.com/blog/2015/03/31/…
Судханшу Мишра
2
Я считаю, что это лучший ответ. OP запросил информацию о JWT, в частности, о чем эта статья обращается с ясным примером ..
webworm
15
Этот ответ объясняет и показывает , как ан кода JWT , когда речь идет о совершенно ясно де кодировании. Это может быть хороший ответ, но это ответ на совершенно другой вопрос .
Deltics
2
@Deltics Я думаю, что даже степень информатики не нужна, чтобы переписать алгоритм кодирования для декодирования токена. Если вы понимаете, как кодировать - вы понимаете, как декодировать
Regfor
33
Идея «ответа» состоит в том, чтобы ответить на вопрос, а не создавать головоломку, ожидая, что кто-то решит какую-то головоломку с обратным намерением. Если вы знаете, как кодировать, это не обязательно означает, что вы также знаете, как декодировать, поскольку это также может включать в себя работу со сторонними токенами и получение ключей для проверки их подписей, в отличие от простого использования ключа для подписи своей собственной. В любом случае ответ, который на самом деле не отвечает на вопрос по определению, не является « лучшим » ответом по сравнению с тем, который дает , а это наблюдение, на которое я отвечал.
Deltics