NetCore 之 authenticate by certification
Https介绍:
https是一种应用层协议,本质上说它是HTTP协议的一种变种。HTTPS比HTTP协议安全,因为HTTP是明文传输,而HTTPS是加密传输,加密过程中使用了三种加密算法,分别是证书,对称加密和非对称加密。HTTPS比HTTP多了一层SSL/TLS.
SSL Secure Socket Layer
TLS Transfer Layer Security
加密算法:
证书加密:服务器在使用证书加密之前需要去证书颁发机构申请该服务器的证书,在HTTPS请求过程服务端会把本服务器的证书发送给客户端,客户端进行证书验证,以此来验证服务器的身份。
对称加密:HTTPS请求中,客户端与服务器之间的通信的数据是通过对称加密算法进行加密的。对称加密,即在加密和解密过程中使用同一个私钥进行加密及解密,而且对称加密算法是公开的,所以该私钥是不能泄露的,一旦泄露,对称加密形同虚设。
例如:DES,AES-GCM,ChaCha2-Poly1305
适用场景:大量数据进行加密,速度快;
非对称加密:公钥加密,私钥解密,安全级别高,加密密文长度有限制,适用于对少量数据进行加密,速度较慢。
例如:RSA,DSA,ECDSA,DH,ECDHE
Hash算法:将任意长度的信息转换成较短的固定长度的值,通常其长度要比信息小很多,且算反不可逆。
Https请求过程:
第一次http请求
1、客户端向服务器端发起HTTPS请求,连接到服务器的443端口;
2、服务器将非对称加密的公钥传递给客户端,以证书的形式回传到客户端;
3、服务器接收到该公钥进行验证,验证2中证书,如果失败则Https请求无法继续;如果成功,则上述公钥是合法的。客户端随机生成一个私钥,称为ClientKey,用于对称加密数据。使用前面的公钥对ClientKey进行非对称加密;
第二次http请求
4、进行二次HTTP请求,将加密之后的ClientKey传递给服务器;
5、服务器使用私钥进行解密,得到ClientKey,使用ClientKey对数据进行对称加密;
6、将对称加密的数据传递给客户端,客户端使用非对称解密,得到服务器发送的数据,完成第二次http请求。
Certificate具体在netcore项目中应用
1、申请证书,本文测试demo所以通过windows powershell创建了self-signed cerficate,具体命令如下:
$certname = "APIDemo"
$cert = New-SelfSignedCertificate -Subject "CN=$certname" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256
$pwd = ConvertTo-SecureString -String "password" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:\ApiDemo.pfx" -Password $pwd
2、configuration
"JwtToken": {
"CertName": "APIDemo.pfx",
"CertPassword": "password",
"Issuer": "https://localhost:7172",
"Audience": "https://localhost:7172",
"ExpiredMinutes": 10
}
3、通过certificate 签名生成token
using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text; namespace API.ServiceA; public class JwtTokenService : IJwtTokenService { private readonly IConfiguration _config; public JwtTokenService(IConfiguration config) { this._config = config; } public string GetJwtToken() { var claims = new List<Claim>() { new Claim(JwtRegisteredClaimNames.Sub,"apidemo"), new Claim(JwtRegisteredClaimNames.Name,"apidemo"), new Claim("Scope","API"), new Claim("Role","Admin") }; var certificate = new X509Certificate2(_config["JwtToken:CertName"], _config["JwtToken:CertPassword"]); var credentials = new X509SigningCredentials(certificate, SecurityAlgorithms.RsaSha256); var issuer = _config["JwtToken:Issuer"]; var audience = _config["JwtToken:Audience"]; var expires = DateTime.Now.AddMinutes(Convert.ToDouble(_config["JwtToken:ExpiredMinutes"])); var jwtToken = new JwtSecurityToken(issuer, audience, claims: claims, notBefore: DateTime.Now, expires: expires, signingCredentials: credentials); return new JwtSecurityTokenHandler().WriteToken(jwtToken); } }
4、配置Authentication
using API.ServiceA; using API.Utility; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.WebHost.ConfigureKestrel((env, option) => { option.ConfigureHttpsDefaults(config => { config.ServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("APIDemo.pfx", "password".ToSecureString()); }); }); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var configuration = builder.Configuration; builder.Services.AddTransient<IJwtTokenService, JwtTokenService>(); builder.Services.AddAuthentication(option => { option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = configuration["JwtToken:Issuer"], ValidAudience = configuration["JwtToken:Audience"], IssuerSigningKey = new X509SecurityKey(new System.Security.Cryptography.X509Certificates.X509Certificate2(configuration["JwtToken:CertName"], configuration["JwtToken:CertPassword"])) }; options.Events = new JwtBearerEvents { OnTokenValidated = context => { var token = context.SecurityToken as JwtSecurityToken; var identity = context.Principal?.Identity as ClaimsIdentity; if (!string.IsNullOrEmpty(token?.Issuer)) context.Success(); else context.Fail($"Token invalid"); return Task.CompletedTask; }, OnAuthenticationFailed = context => { context.Fail(context.Exception); return Task.CompletedTask; }, OnForbidden = context => { context.Fail("403 forbidden"); return Task.CompletedTask; }, OnChallenge = context => { var error = context.Error; return Task.CompletedTask; }, OnMessageReceived = context => { var token = context.Token; return Task.CompletedTask; } }; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
5、测试controller
[Route("api/[controller]")] [ApiController] public class JWTController : ControllerBase { private readonly IJwtTokenService _tokenService; public JWTController(IJwtTokenService tokenService) { _tokenService = tokenService; } [HttpGet("token")] public IActionResult Get() { var token = _tokenService.GetJwtToken(); return new JsonResult(token); } }
[Route("api/[controller]")] [ApiController] public class ServiceAController : ControllerBase { [HttpGet("api1")] public IActionResult Get() { return new JsonResult( new { Id = Guid.NewGuid(), Name = "api1", CurrentDateTime = DateTime.Now.ToLongDateString() }); } }
6、Postman test
生成token
测试api