Decodificando e verificando o token JWT usando System.IdentityModel.Tokens.Jwt

101

Tenho usado a biblioteca JWT para decodificar um Json Web Token e gostaria de mudar para a implementação JWT oficial da Microsoft, System.IdentityModel.Tokens.Jwt .

A documentação é muito esparsa, então estou tendo dificuldade em descobrir como realizar o que venho fazendo com a biblioteca JWT. Com a biblioteca JWT, há um método Decode que pega o JWT codificado em base64 e o transforma em JSON, que pode ser desserializado. Eu gostaria de fazer algo semelhante usando System.IdentityModel.Tokens.Jwt, mas depois de uma boa quantidade de pesquisas, não consigo descobrir como.

Pelo que vale a pena, estou lendo o token JWT de um cookie, para uso com a estrutura de identidade do Google.

Qualquer ajuda seria apreciada.

w.brian
fonte
Aqui está uma resposta prática sobre como buscar certificados do Google e verificar o token - stackoverflow.com/questions/29757140/…
rothschild86

Respostas:

147

Dentro do pacote, há uma classe chamada JwtSecurityTokenHandlerque deriva de System.IdentityModel.Tokens.SecurityTokenHandler. No WIF, essa é a classe principal para desserializar e serializar tokens de segurança.

A classe tem um ReadToken(String)método que pegará sua string JWT codificada em base64 e retornará um SecurityTokenque representa o JWT.

O SecurityTokenHandlertambém tem um ValidateToken(SecurityToken)método que pega seu SecurityTokene cria um ReadOnlyCollection<ClaimsIdentity>. Normalmente, para o JWT, ele conterá um único ClaimsIdentityobjeto que possui um conjunto de declarações que representam as propriedades do JWT original.

JwtSecurityTokenHandlerdefine algumas sobrecargas adicionais para ValidateToken, em particular, ter uma ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)sobrecarga. O TokenValidationParametersargumento permite que você especifique o certificado de assinatura de token (como uma lista de X509SecurityTokens). Ele também tem uma sobrecarga que considera o JWT como um em stringvez de um SecurityToken.

O código para fazer isso é um pouco complicado, mas pode ser encontrado no código ( TokenValidationHandlerclasse) Global.asax.cx no exemplo de desenvolvedor chamado "ADAL - Aplicativo nativo para serviço REST - Autenticação com ACS via caixa de diálogo do navegador", localizado em

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

Como alternativa, a JwtSecurityTokenclasse possui métodos adicionais que não estão na SecurityTokenclasse base , como uma Claimspropriedade que obtém as declarações contidas sem passar pela ClaimsIdentitycoleção. Ele também tem uma Payloadpropriedade que retorna um JwtPayloadobjeto que permite obter o JSON bruto do token. Depende do seu cenário qual abordagem é mais apropriada.

A documentação geral (ou seja, não específica do JWT) para a SecurityTokenHandlerclasse está em

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

Dependendo do seu aplicativo, você pode configurar o manipulador JWT no pipeline WIF exatamente como qualquer outro manipulador.

Existem 3 amostras em uso em diferentes tipos de aplicação em

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

Provavelmente, um atenderá às suas necessidades ou, pelo menos, será adaptável a elas.

Mike Goodwin
fonte
3
Eu realmente aprecio sua resposta. Portanto, uma vez que tenho o ClaimsIdentity, como faço para verificá-lo em relação a uma chave pública? Especificamente, estou tentando verificar um JWT do kit de ferramentas de identidade do Google em relação à chave pública ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian
4
Atualizei minha resposta - não consegui encaixar o código-fonte completo para isso, mas apontei para você na direção do exemplo de desenvolvedor apropriado. Espero que ajude.
Mike Goodwin de
4
@ w.brian - Estou tentando fazer o mesmo. Eu tenho um token que posso decodificar e uma chave pública que quero verificar, mas mesmo olhando para esses exemplos, estou lutando para ver como faço isso. Você tem alguma indicação de qual código realmente o ajudou? Obrigado.
Barguast,
26

Estou apenas me perguntando por que usar algumas bibliotecas para a decodificação e verificação do token JWT.

O token JWT codificado pode ser criado usando o seguinte pseudocódigo

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

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

var encodedJWT = payload + "." + signature;

É muito fácil fazer sem nenhuma biblioteca específica. Usando o seguinte 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\":\"[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);
    }
}

A decodificação do token é a versão reversa do código acima. Para verificar a assinatura, você precisará fazer o mesmo e comparar a parte da assinatura com a assinatura calculada.

ATUALIZAÇÃO: para aqueles que têm dificuldade em fazer codificação / decodificação de urlsafe em base64, consulte outra pergunta do SO , e também wiki e RFCs

Regfor
fonte
2
Boa resposta. Embora você mostre a assinatura baseada em HMAC aqui, pode fazer sentido estar ciente de algumas vulnerabilidades críticas em bibliotecas que implementam a verificação de HMAC conforme detalhado no site Auth0 aqui: auth0.com/blog/2015/03/31/…
Sudhanshu Mishra
2
Acho que esta é a melhor resposta. O OP solicitou informações sobre JWT especificamente que este artigo aborda com um exemplo claro ..
webworm
13
Esta resposta explica e demonstra como en código de um JWT quando a questão é bastante clara sobre de codificação. Esta pode ser uma boa resposta, mas é uma resposta a uma pergunta totalmente diferente .
Deltics
2
@Deltics Eu acho que mesmo o diploma de ciência da computação não é necessário para reescrever o algoritmo de codificação para decodificar o token. Se você sabe como codificar - você sabe como decodificar
Regfor
31
A ideia de uma "resposta" é abordar uma questão, não criar um quebra-cabeça esperando que alguém resolva algum tipo de quebra-cabeça de intenção reversa. Por outro lado, saber como codificar não significa necessariamente que você também saiba como decodificar, pois isso também pode envolver o tratamento de tokens de terceiros e a recuperação de chaves para verificar suas assinaturas, em vez de simplesmente usar uma chave para assinar a sua. Em qualquer caso, uma resposta que se não realmente responder à pergunta, por definição, é não o " melhor " resposta quando comparado com um que faz , que é a observação de que eu estava respondendo.
Deltics