Net Core JWT签名凭证

时间:2018-02-15 11:59:03

标签: c# authentication asp.net-core jwt

我正在关注本教程: https://jonhilton.net/2017/10/11/secure-your-asp.net-core-2.0-api-part-1---issuing-a-jwt/

这是主要代码:

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["SecurityKey"]));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                    issuer: "yourdomain.com",
                    audience: "yourdomain.com",
                    claims: user.Claims,
                    expires: DateTime.Now.AddMinutes(30),
                    signingCredentials: creds);

我不明白的部分是它在配置文件中查找密钥的位置,但它没有指示该密钥是/应该是什么?

2 个答案:

答案 0 :(得分:1)

一种选择是保留与" kid"相关联的对称签名密钥的存储库。在JWT标题中声称。例如,将密钥文件保存在加密的AWS S3存储桶中。 .NET Core 2 Web服务每隔X分钟从S3存储桶中提取密钥文件。

当请求到达时,"孩子"声明值用于从拉出文件构建的集合中查找关联的对称密钥。

实施例: 配置IssuerSigningKeyResolver函数。

public static IServiceCollection AddJwtValidation(this IServiceCollection services)
{
        IServiceProvider sp = services.BuildServiceProvider();
        ConfigRoot = sp.GetRequiredService<IConfigurationRoot>();

        tokenAudience = ConfigRoot["JwtToken:Audience"];
        tokenIssuer = ConfigRoot["JwtToken:Issuer"];

        SecurityKeyManager.Start();

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
        {
            options.Audience = tokenAudience;
            options.ClaimsIssuer = tokenIssuer;

            options.TokenValidationParameters = new TokenValidationParameters
            {                    
                // The signing key must match!
                ValidateIssuerSigningKey = true,
                RequireSignedTokens = true,
                IssuerSigningKeyResolver = MyIssuerSigningKeyResolver,
                ....

IssuerSigningKeyResolver定义为:

public static List<SecurityKey> MyIssuerSigningKeyResolver(string token, SecurityToken jwtToken, string kid, TokenValidationParameters validationParameters)
{
        List<SecurityKey> keys = new List<SecurityKey>();

        if (validationParameters == null)
        {
            throw new ArgumentNullException("validationParameters");
        }

        if (jwtToken == null)
        {
            throw new ArgumentNullException("securityToken");
        }

        if (!string.IsNullOrEmpty(kid))
        {
            SymmetricSecurityKey key = SecurityKeyManager.GetSecurityKey(kid);

            keys.Add(key);
        }

        return keys;
}

SecurityKeyManager定期从AWS S3中提取密钥文件,并将密钥存储在集合中。

// Example approved-clients.txt file in the approved-clients S3 bucket (us-east-1).
//    //kid,key,active
//    customer1,AAAAAAAAAAAAAAAA,true
//    customer2,BBBBBBBBBBBBBBBB,true
//    customer3,CCCCCCCCCCCCCCCC,true
//    customer4,DDDDDDDDDDDDDDDD,true

namespace My.CoreServices.Security.Jwt
{
    public class SecurityKeyManager
    {
        private const int RELOAD_TIMER_DELAY_SECONDS = 3 * 1000;
        private const int RELOAD_TIMER_PERIOD_MINUTES = 60 * 60 * 1000;

        [DebuggerDisplay("{Kid}  {SymmetricKey}  {Active}")]
        internal class ApprovedClient
        {
            public string Kid { get; set; }
            public bool Active { get; set; }
            public string SymmetricKey { get; set; }
        };

        private static List<SymmetricSecurityKey> securityKeys = new List<SymmetricSecurityKey>();

        private static Timer reloadTimer = null;

        private static object keySync = new object();

        public static void Start()
        {
            // Start a new timer to reload all the security keys every RELOAD_TIMER_PERIOD_MINUTES.
            if (reloadTimer == null)
            {
                reloadTimer = new Timer(async (t) =>
                {
                    try
                    {
                        List<ApprovedClient> approvedClients = new List<ApprovedClient>();

                        Log.Debug("Pulling latest approved client symmetric keys for JWT signature validation");

                        string awsAccessKeyId = JwtConfigure.ConfigRoot["AWS:KeyManagement:AccessKeyId"];
                        string awsSecretAccessKey = fromBase64(JwtConfigure.ConfigRoot["AWS:KeyManagement:SecretAccessKey"]);
                        string awsRegion = JwtConfigure.ConfigRoot["AWS:KeyManagement:Region"];

                        using (var client = new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey, RegionEndpoint.GetBySystemName(awsRegion)))
                        {
                            var request = new GetObjectRequest();
                            request.BucketName = JwtConfigure.ConfigRoot["AWS:KeyManagement:Bucket"];
                            request.Key = JwtConfigure.ConfigRoot["AWS:KeyManagement:Key"];

                            var response = await client.GetObjectAsync(request);

                            using (StreamReader sr = new StreamReader(response.ResponseStream))
                            {
                                while (sr.Peek() > 0)
                                {
                                    string line = await sr.ReadLineAsync();

                                    // Ignore comment lines in the approved-client file
                                    if (!line.StartsWith("//") && !string.IsNullOrEmpty(line))
                                    {
                                        // Each line of the file should only have 3 items:
                                        // kid, key, active
                                        string[] items = line.Split(',');
                                        approvedClients.Add(new ApprovedClient()
                                        {
                                            Kid = items[0],
                                            SymmetricKey = items[1],
                                            Active = Boolean.Parse(items[2])
                                        });
                                    }
                                }
                            }

                        }
                        lock (keySync)
                        {
                            if (approvedClients.Count > 0)
                            {
                                // Clear the security key list and repopulate
                                securityKeys.Clear();

                                foreach (var approvedClient in approvedClients)
                                {
                                    if (approvedClient.Active)
                                    {
                                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(approvedClient.SymmetricKey));
                                        key.KeyId = approvedClient.Kid;
                                        securityKeys.Add(key);
                                    }
                                }
                            }
                        }

                        Log.Information($"Reloaded security keys");
                    }
                    catch (Exception ex)
                    {
                        Log.Warning($"Error getting current security keys - {ex.Message}");
                    }
                }, null, RELOAD_TIMER_DELAY_SECONDS, RELOAD_TIMER_PERIOD_MINUTES);
            }
        }

        public static void Stop()
        {
            if (reloadTimer != null)
            {
                reloadTimer.Dispose();
                reloadTimer = null;
            }
        }

        public static SymmetricSecurityKey GetSecurityKey(string kid)
        {
            SymmetricSecurityKey securityKey = null;

            lock (keySync)
            {
                byte[] keyData = securityKeys.Where(k => k.KeyId == kid).Select(x => x.Key).FirstOrDefault();

                if (keyData != null)
                {
                    securityKey = new SymmetricSecurityKey(keyData);
                    securityKey.KeyId = kid;
                }
            }

            return securityKey;
        }

        private static string fromBase64(string encodedValue)
        {
            byte[] decodedBytes = Convert.FromBase64String(encodedValue);
            return Encoding.UTF8.GetString(decodedBytes);
        }
    }
}

确保为特定用户/客户/等创建JWT时,&#34;孩子&#34;声明在JWT标题中设置。

{
  "alg": "HS256",
  "kid": "customer2",
  "typ": "JWT"
}

&#34;孩子的价值&#34;将作为第三个参数传递给IssuerSigningKeyResolver方法。然后,该孩子将用于查找用于验证JWT签名的关联对称密钥。

答案 1 :(得分:0)

此密钥可以是任何字符串:它是用于加密和解密安全负载的密钥。 来自Wikipedia

  

对称密钥算法是用于加密的算法,其使用相同的加密密钥来加密明文和解密密文。实际上,密钥代表了两个或更多方之间的共享秘密,可用于维护私人信息链接。

关于如何生成有效密钥,您可以在crypto.stackexchange上参考此问题。