使用System.IdentityModel.Tokens.Jwt解码和验证JWT令牌

时间:2013-09-07 20:55:39

标签: .net wif jwt

我一直在使用JWT库来解码Json Web Token,并希望切换到Microsoft的官方JWT实现,System.IdentityModel.Tokens.Jwt

文档很稀疏,所以我很难弄清楚如何完成我在JWT库中所做的工作。使用JWT库,有一个Decode方法,它采用base64编码的JWT并将其转换为JSON,然后可以对其进行反序列化。我想使用System.IdentityModel.Tokens.Jwt做类似的事情,但经过大量的挖掘,无法弄清楚如何。

为了它的价值,我正在从cookie中读取JWT令牌,以便与Google的身份框架一起使用。

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:128)

在包中有一个名为JwtSecurityTokenHandler的类,它派生自System.IdentityModel.Tokens.SecurityTokenHandler。在WIF中,这是用于反序列化和序列化安全令牌的核心类。

该类有一个ReadToken(String)方法,它将采用base64编码的JWT字符串并返回代表JWT的SecurityToken

SecurityTokenHandler还有ValidateToken(SecurityToken)方法,可以使用SecurityToken并创建ReadOnlyCollection<ClaimsIdentity>。通常对于JWT,这将包含一个ClaimsIdentity对象,该对象具有一组声明,表示原始JWT的属性。

JwtSecurityTokenHandlerValidateToken定义了一些额外的重载,特别是它有ClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)重载。 TokenValidationParameters参数允许您指定令牌签名证书(作为X509SecurityTokens的列表)。它还有一个重载,它将JWT作为string而不是SecurityToken

执行此操作的代码相当复杂,但可以在开发人员示例中的Global.asax.cx代码(TokenValidationHandler类)中找到,称为“ADAL - 本地应用程序到REST服务 - 通过ACS进行身份验证“浏览器对话框”,位于

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

或者,JwtSecurityToken类具有不在基础SecurityToken类上的其他方法,例如Claims属性,无需通过ClaimsIdentity即可获取所包含的声明收集。它还有一个Payload属性,它返回一个JwtPayload对象,可以让你获得令牌的原始JSON。这取决于您的场景哪种方法最合适。

SecurityTokenHandler类的一般(即非JWT特定)文档位于

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

根据您的应用程序,您可以将JWT处理程序配置为WIF管道,就像任何其他处理程序一样。

在不同类型的应用中使用了3个样本

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

可能会满足您的需求,或至少适应它们。

答案 1 :(得分:22)

我只是想知道为什么要使用一些库进行JWT令牌解码和验证。

可以使用following pseudocode

创建编码的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);
    }
}

令牌解码是上面代码的反转版本。要验证签名,您需要相同的签名部分和计算签名。

更新:对于那些如何进行base64 url​​safe编码/解码的人,请参阅另一个SO question,以及wiki和RFC