一、前言

在上一篇《.NET Core WebAPI 认证授权之JWT(二):HMAC算法实操》中进行了JWT签名常用算法之一HMAC算法实操,本文进行另一种更安全的非对称加密算法RSA实操。

HMAC算法和RSA算法的大致步骤是一样的,区别在于生成Token时使用的算法以及密钥。由于RSA是一种非对称加密算法,它和HMAC加密时使用一个密钥不同,它需要一对密钥:公钥和私钥。

二、生成Token

生成Token的步骤和上一篇差不多,这里只说区别,如有疑问,请看上篇:

1.创建项目 

区别于上一篇将颁发Token和API分成两个项目,这里我将二者写在同一个项目(根据实际需求自己选择)。

.NET Core WebAPI 认证授权之JWT(三):RSA算法实操_java

2.引入Nuget包 

引入Microsoft.AspNetCore.Authentication.JwtBearer,这里和上一篇没什么区别,略过。

3.在AuthenticationController中创建一个登录接口 

public string Login([FromForm] string userName, [FromForm] string password)
{
    if (userName == password) //验证账号密码
    {
        //生成JWT,见第4步。
        return "";
    }
    else
    {
        //返回账号密码错误
        return "";
    }
}

4.生成JWT

在生成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)
};
//私钥文件放在应用根目录 名为key.pem
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); //凭证
//生成的JWT
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
#endregion

和上一篇类似,区别在于这里的key是RSA密钥对中的私钥,通常是从pem文件导入:RSA.Create().ImportFromPem()

三、Token认证和授权

步骤同上一篇一样。添加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
            ValidIssuer = "leo96.com",
            ValidAudience = "leo96.com",
            IssuerSigningKey = new RsaSecurityKey(rsa)
        };
    });
#endregion

这里需要从pem文件读取公钥:RSA.Create().ImportFromPem(),进行验证。

附加:C#生成RSA密钥对

.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);   
    //pkcs8格式私钥
    privateKeys_pkcs8 = rsa.ExportPrivateKey(RSAKeyType.Pkcs8, isPem); 
    //pkcs8格式公钥
    publicKeys_pkcs8 = rsa.ExportPublicKey(RSAKeyType.Pkcs8, isPem);   

    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密钥对生成工具(点击阅读原文,文末附录有链接):

.NET Core WebAPI 认证授权之JWT(三):RSA算法实操_java_02