我正在关注本教程: 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);
我不明白的部分是它在配置文件中查找密钥的位置,但它没有指示该密钥是/应该是什么?
答案 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上参考此问题。