我在.NET FW中找到了非对称签名的示例,在.NET Core中找到了对称签名的示例,但是我无法弄清楚如何在.NET Core中非对称地验证JWT。给定指向JWK集的URL或给定公共密钥,如何在.NET Core中验证令牌?
答案 0 :(得分:0)
不对称签名和对称签名之间的唯一区别是签名密钥。只需为令牌验证参数构造一个新的ASymmetric安全密钥即可。
假设您要使用RSA算法。让我们使用powershell导出一对RSA密钥,如下所示:
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 2048
$rsa.ToXmlString($true) | Out-File key.private.xml
$rsa.ToXmlString($false) | Out-File key.public.xml
现在,我们将使用两个密钥对令牌进行签名。
由于.NET Core支持rsa.FromXmlString()
api,因此我只需复制@myloveCc's code即可在C#中构造一个RsaParameters
(此工作由以下ParseXmlString()
方法完成):
public static class KeyHelper
{
public static RSAParameters ParseXmlString( string xml){
RSAParameters parameters = new RSAParameters();
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.LoadXml(xml);
if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
{
foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
switch (node.Name)
{
case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
}
}
}
else
{
throw new Exception("Invalid XML RSA key.");
}
return parameters;
}
public static RsaSecurityKey BuildRsaSigningKey(string xml){
var parameters = ParseXmlString(xml);
var rsaProvider = new RSACryptoServiceProvider(2048);
rsaProvider.ImportParameters(parameters);
var key = new RsaSecurityKey(rsaProvider);
return key;
}
}
在这里,我添加了一个BuildRsaSigningKey()
辅助方法来生成一个SecurityKey
。
这是一个使用RSA生成令牌的演示:
public string GenerateToken(DateTime expiry)
{
var tokenHandler = new JwtSecurityTokenHandler();
var Identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "..."),
// ... other claims
});
var xml = "<RSAKeyValue> load...from..local...files...</RSAKeyValue>";
SecurityKey key = KeyHelper.BuildRsaSigningKey(xml);
var Token = new JwtSecurityToken
(
issuer: "test",
audience: "test-app",
claims: Identity.Claims,
notBefore: DateTime.UtcNow,
expires: expiry,
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest)
);
var TokenString = tokenHandler.WriteToken(Token);
return TokenString;
}
要自动验证它,请按以下方式配置JWT承载身份验证:
Services.AddAuthentication(A =>
{
A.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
A.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(O =>
{
var xml = "<RSAKeyValue> load...from..local...files...</RSAKeyValue>";
var key = KeyHelper.BuildRsaSigningKey(xml);
O.RequireHttpsMetadata = false;
O.SaveToken = true;
O.IncludeErrorDetails = true;
O.TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = key,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
// ... other settings
};
});
如果您想手动验证它:
public IActionResult ValidateTokenManually(string jwt)
{
var xml = "<RSAKeyValue>... the keys ...</RSAKeyValue>";
SecurityKey key = KeyHelper.BuildRsaSigningKey(xml);
var validationParameters = new TokenValidationParameters
{
IssuerSigningKey = key,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateLifetime = true,
// ... other settings
};
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(jwt, validationParameters, out var rawValidatedToken);
var securityToken = (JwtSecurityToken)rawValidatedToken;
return Ok(principal);
}
答案 1 :(得分:0)
我最终实现了OpenID Connect Discovery规范,该规范允许您以标准格式发布令牌端点和密钥集端点。然后,我可以使用AddJwtBearer()
AuthenticationBuilder
扩展方法来自动缓存键集,验证令牌并填充ClaimsPrincipal
。
没有人读这本书,不必执行此协议,而应该使用现有的OpenID Server产品,或者如果没有,则使用其中一种流行的开源实现。
另一个答案非常有益,但是它不支持捆绑在密钥集中的多个密钥,并且随后不支持滚动或将密钥集发布为标准JWKS。