VisualStudio 2019 16.8.3
ASP.NET Core 5.0
在上一篇《.NET Core WebAPI 认证授权之JWT(二):HMAC算法实操》中进行了JWT签名常用算法之一HMAC算法实操,本文进行另一种更安全的非对称加密算法RSA实操。
HMAC算法和RSA算法的大致步骤是一样的,区别在于生成Token时使用的算法以及密钥。由于RSA是一种非对称加密算法,它和HMAC加密时使用一个密钥不同,它需要一对密钥,使用私钥加密,然后使用公钥解密。
生成Token的步骤和上一篇差不多,这里只说区别,如有疑问,请看上篇:
区别于上一篇将颁发Token和API分成两个项目,这里我将二者写在同一个项目(根据实际需求自己选择)。
这里和上一篇没什么区别,略过。
同上。
在生成Token之前,我们需要准备一对RSA密钥(公钥和私钥),密钥可以借助网上的工具生成,如支付宝的:https://opendocs.alipay.com/open/291/105971,也可以自己写代码生成。
注意:这里我们使用的是PEM格式的密钥(PKCS#1或PKCS#8都可以),在.NET 5.0中,有直接从PEM格式导入密钥的API。
#region 生成Token RSA
var claims = new[] {
new Claim(ClaimTypes.Name, userName)
};
string rootPath = Directory.GetCurrentDirectory();
string filePath = Path.Combine(rootPath, "key.pem");
if (!System.IO.File.Exists(filePath))
throw new Exception("私钥文件不存在");
string key = System.IO.File.ReadAllText(filePath);
var rsa = RSA.Create();
rsa.ImportFromPem(key.AsSpan());
var creds = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256);
var token = new JwtSecurityToken(issuer: "leo96.com", //发布者 随便写 可用于验证 建议写配置文件中
audience: "leo96.com", //受众 随便写 可用于验证 建议写配置文件中
claims: claims, //声明 存储其他信息
expires: DateTime.Now.AddMinutes(5), //过期时间 可写配置文件中
signingCredentials: creds); //凭证
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
#endregion
和上一篇类似,区别在于这里的key是RSA密钥对中的私钥,通常是从pem文件导入:RSA.Create().ImportFromPem()。
步骤同上一篇一样。添加JWT认证服务、认证中间件、授权中间件、Authorize特性。
这里主要是添加JWT认证服务时略有不同:
#region 添加JWT认证 RSA
string path = Path.Combine(Directory.GetCurrentDirectory(), "key.public.pem");
var rsa = RSA.Create();
rsa.ImportFromPem(File.ReadAllText(path).AsSpan());
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, //是否验证Issuer
ValidateAudience = true, //是否验证Audience
ValidateLifetime = true, //是否验证有效时间
ValidateIssuerSigningKey = true, //是否验证key
ClockSkew = TimeSpan.FormSecends(30), //缓冲过期时间 默认5分钟 有效时间 = 过期时间 + 缓冲过期时间
ValidIssuer = "leo96.com",
ValidAudience = "leo96.com",
IssuerSigningKey = new RsaSecurityKey(rsa)
};
});
#endregion
这里需要从pem文件读取公钥:RSA.Create().ImportFromPem(),进行验证。
.NET自带API可以导出PKCS#1、PKCS#8的私钥和PKCS#1的公钥,但没有导出PKCS#8格式公钥的API,并且不支持PEM格式。
所以这里我用到了第三方Nuget包:RSAExtensions,里面有RSA类的扩展方法,弥补了官方API的不足。
RSAExtensions 功能如下:
● 提供以前Framework有的XML格式的密钥导入和导出
● 提供对PKCS#1、PKCS#8 PEM格式导入和导出支持
● 提供对PKCS#1、PKCS#8、XML格式的统一导入和导出
● 提供对大数据分段加密的支持
使用RSAExtensions生成RSA密钥对:
using RSAExtensions;
private RSA GenerateAndSaveKey(string filePath, bool isPem)
{
string publicKeys, privateKeys, privateKeys_pkcs8, publicKeys_pkcs8, ext;
ext = isPem ? "pem" : "txt";
var rsa = RSA.Create();
privateKeys = rsa.ExportPrivateKey(RSAKeyType.Pkcs1, isPem); //私钥
publicKeys = rsa.ExportPublicKey(RSAKeyType.Pkcs1, isPem); //公钥
privateKeys_pkcs8 = rsa.ExportPrivateKey(RSAKeyType.Pkcs8, isPem); //pkcs8私钥
publicKeys_pkcs8 = rsa.ExportPublicKey(RSAKeyType.Pkcs8, isPem); //pkcs8公钥
File.WriteAllText(System.IO.Path.Combine(filePath, $"key.pkcs8.{ext}"), privateKeys_pkcs8);
File.WriteAllText(System.IO.Path.Combine(filePath, $"key.public.pkcs8.{ext}"), publicKeys_pkcs8);
File.WriteAllText(System.IO.Path.Combine(filePath, $"key.{ext}"), privateKeys);
File.WriteAllText(System.IO.Path.Combine(filePath, $"key.public.{ext}"), publicKeys);
return rsa;
}
此外博主自己写了一个RSA密钥对生成工具(文末附录):
感谢阅读,敬请斧正!
附录一:RSA密钥对生成工具安装包
附录二:系列Demo(弄丢了,卧槽)
附录三:系列目录
《.NET Core WebAPI 认证授权之JWT(一):JWT介绍》
《.NET Core WebAPI 认证授权之JWT(二):HMAC算法实操》
《.NET Core WebAPI 认证授权之JWT(三):RSA算法实操》
《.NET Core WebAPI 认证授权之JWT(四):JWT续期问题》
版权声明:本文由不落阁原创出品,转载请注明出处!
广告位
跟不落阁,学DOTNET!
广告位
2021-02-04 09:21回复