为什么我得到SecurityTokenSignatureKeyNotFoundException?

时间:2015-06-13 17:26:54

标签: c# asp.net-web-api jwt katana

当我尝试将此JWT(由Azure移动服务发布)作为HTTP标头/授权/承载令牌传递时:

Header:
{
    "alg": "HS256", 
    "typ": "JWT", 
    "kid": "0"
}
Claims:
{
    "ver": 2, 
    "aud": "Facebook", 
    "iss": "urn:microsoft:windows-azure:zumo", 
    "urn:microsoft:credentials": "pYK8b5...", 
    "exp": 1436730730, 
    "uid": "Facebook:10000xxxxxxxxxx"
}

进入我配置的ASP.NET WEB API:

const string issuer = "urn:microsoft:windows-azure:zumo";
byte[] mobileServicesSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:SecretKey"]);

app.UseJwtBearerAuthentication(
    new JwtBearerAuthenticationOptions
    {
      AuthenticationMode = AuthenticationMode.Active,
      AllowedAudiences = new[] { "Facebook" },
      IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
              {
                  new SymmetricKeyIssuerSecurityTokenProvider(issuer,  mobileServicesSecret)
              }
    });

我明白了:

  

类型的第一次机会异常   'System.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException'   发生在System.IdentityModel.Tokens.Jwt.dll

我怀疑这是因为“孩子”财产的存在?

编辑:使用此https://github.com/Magenic/JWTvalidator/tree/master/JwtValidator/JwtValidator,可以验证JWT,因此它没有任何问题。但我真的想用OWIN / Katana。

1 个答案:

答案 0 :(得分:0)

Google建议如下 - Calling the tokeninfo endpoint

  

我们强烈建议您为自己的平台使用Google API客户端库,或调用我们的令牌信息验证端点,而非编写自己的代码来执行这些验证步骤。

     

要使用tokeninfo端点验证ID令牌,请对端点发出HTTPS POST或GET请求,并在id_token参数中传递您的ID令牌。例如,要验证令牌“XYZ123”,请发出以下GET请求:

<强> CustomJwtHandler.cs

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Configuration;
using Newtonsoft.Json;
using System.Net;
using System.Threading.Tasks;
using System.Threading;
using Services.Models;
using System.Security.Claims;

namespace Services
{
    /// <summary>
    ///  This is an implementation of Google JWT verification that
    ///  demonstrates:
    ///    - JWT validation
    /// </summary>
    /// @author kunal.bajpai@gmail.com (Kunal Bajpai)


    public class CustomJwtHandler : DelegatingHandler
    {
        private const string URL_GOOGLE_TOKEN_INFO = "https://www.googleapis.com/oauth2/v3/tokeninfo";

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpStatusCode statusCode;
            string token;

            var authHeader = request.Headers.Authorization;
            if (authHeader == null)
            {
                // Missing authorization header
                return base.SendAsync(request, cancellationToken);
            }

            if (!TryRetrieveToken(request, out token))
            {
                return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
            }

            try
            {
                ValidateToken(token);
                return base.SendAsync(request, cancellationToken);
            }
            catch (SecurityTokenInvalidAudienceException)
            {
                statusCode = HttpStatusCode.Unauthorized;
            }
            catch (SecurityTokenValidationException)
            {
                statusCode = HttpStatusCode.Unauthorized;
            }
            catch (Exception)
            {
                statusCode = HttpStatusCode.InternalServerError;
            }

            return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
        }
        /// <summary>
        /// Validates JWT Token
        /// </summary>
        /// <param name="JwtToken"></param>
        private void ValidateToken(string JwtToken)
        {
            try
            {
                using (WebClient wc = new WebClient())
                {
                    TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(wc.DownloadString(URL_GOOGLE_TOKEN_INFO + "?id_token=" + JwtToken));

                    ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(ExtractClaims(tokenInfo), tokenInfo.Issuer));

                    Thread.CurrentPrincipal = claimsPrincipal;
                    HttpContext.Current.User = claimsPrincipal;
                }
            }
            catch (WebException e)
            {
                HttpStatusCode statusCode = ((HttpWebResponse)e.Response).StatusCode;
                if (statusCode == HttpStatusCode.BadRequest)
                {
                    throw new SecurityTokenValidationException();
                }
                else
                {
                    throw new Exception();
                }
            }
        }

        /// <summary>
        /// Tries to retrieve Token
        /// </summary>
        /// <param name="request"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
        {
            token = null;
            IEnumerable<string> authorizationHeaders;

            if (!request.Headers.TryGetValues("Authorization", out authorizationHeaders) ||
            authorizationHeaders.Count() > 1)
            {
                return false;
            }

            var bearerToken = authorizationHeaders.ElementAt(0);
            token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
            return true;
        }

        private List<Claim> ExtractClaims(TokenInfo tokenInfo)
        {
            List<Claim> claims = new List<Claim> {
                new Claim(ClaimTypes.Name, tokenInfo.Name),
                new Claim(ClaimTypes.Email, tokenInfo.Email),
                new Claim(ClaimTypes.GivenName, tokenInfo.GivenName),
                new Claim(ClaimTypes.Surname, tokenInfo.FamilyName),
                new Claim(ApplicationUser.CLAIM_TYPE_LOCALE, tokenInfo.Locale),
                new Claim(ClaimTypes.NameIdentifier, tokenInfo.ProviderKey, ClaimValueTypes.String, tokenInfo.Issuer),
                new Claim(ApplicationUser.CLAIM_TYPE_EMAIL_CONFIRMED, tokenInfo.IsEmailVerifed.ToString(), ClaimValueTypes.Boolean)
            };

            return claims;
        }
    }
}

<强> TokenInfo.cs

using Microsoft.AspNet.Identity.EntityFramework;
using Newtonsoft.Json;

namespace Services.Models
{
    public class TokenInfo
    {
        [JsonProperty("iss")]
        public string Issuer { get; set; }

        [JsonProperty("aud")]
        public string AudienceClientId { get; set; }

        [JsonProperty("sub")]
        public string ProviderKey { get; set; }

        [JsonProperty("email_verified")]
        public bool IsEmailVerifed { get; set; }

        [JsonProperty("azp")]
        public string AndroidClientId { get; set; }

        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("iat")]
        public long IssuedAt { get; set; }

        [JsonProperty("exp")]
        public long ExpiresAt { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("picture")]
        public string Picture { get; set; }

        [JsonProperty("given_name")]
        public string GivenName { get; set; }

        [JsonProperty("family_name")]
        public string FamilyName { get; set; }

        [JsonProperty("locale")]
        public string Locale { get; set; }

        [JsonProperty("alg")]
        public string Algorithm { get; set; }

        [JsonProperty("kid")]
        public string kid { get; set; }

        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof(ApplicationUser))
            {
                return false;
            }

            ApplicationUser user = (ApplicationUser)obj;
            bool hasLogin = false;

            foreach (IdentityUserLogin login in user.Logins)
            {
                if (login.ProviderKey == ProviderKey)
                {
                    hasLogin = true;
                    break;
                }
            }
            if (!hasLogin) { return false; }

            if (user.FirstName != GivenName) { return false; }
            if (user.LastName != FamilyName) { return false; }
            if (user.Locale != Locale) { return false; }

            return base.Equals(obj);
        }
    }
}

<强> WebApiConfig.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;

namespace Services
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.MessageHandlers.Add(new CustomJwtHandler());
        }
    }
}