第三十七节:系统证书管理和gRPC基于数字证书的认证和授权
一. 证书管理
1. 如何生成证书
(1). 关于阿里云证书和证书的相关概念
详见:https://www.cnblogs.com/yaopengfei/p/10648151.html (抽时间重新配置一遍)
(2). 本地生成测试证书
详见:https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
这里使用案例9:【New-SelfSignedCertificate -Subject "localhost" -TextExtension @("2.5.29.17={text}DNS=localhost&IPAddress=127.0.0.1&IPAddress=::1")】
2. 本地计算机证书管理
(1).查看计算已有的证书
cmd命令行→输入certmgr→可以查看当前用户下的证书以及受信任的颁发机构
(2).证书导入和添加信任
cmd命令行→输入mmc→进入控制台页面(默认空白)→选中文件中的‘添加/删除管理单元’→将'证书'一项添加进去
导入步骤:
A.以管理员的身份运行powershell,运行证书生成指令【New-SelfSignedCertificate -Subject "ypf" -TextExtension @("2.5.29.17={text}DNS=localhost&IPAddress=127.0.0.1&IPAddress=::1")】生成成功,如下图: 并在控制台下→个人→证书 找到刚才生成的证书。
B.将该证书导出:选择需要私钥→输入密码123455→输入名称ypfCert进行导出
C.对该证书添加信任:受信任的根证书颁发机构→证书→进行导入
二. 基于证书的认证和授权
1. 项目准备
GrpcServer3:服务端
MyClient3:客户端(控制台)
2. 服务端配置
(1).新建cert.proto文件,声明获取证书信息的方法GetCertificateInfo,并对其添加链接引用。
代码如下:
syntax = "proto3"; import "google/protobuf/empty.proto"; package certify; //Certifier对应CertifierService实现类 service Certifier { //获取证书信息的方法 rpc GetCertificateInfo (google.protobuf.Empty) returns (CertificateInfoResponse); //获取证书信息的方法(测试不加校验) rpc GetCertificateInfoNoAuth (google.protobuf.Empty) returns (CertificateInfoResponse); } message CertificateInfoResponse { bool hasCertificate = 1; string name = 2; }
(2).新建CertifierService,重写GetCertificateInfo方法,并添加授权校验 [Authorize(AuthenticationSchemes = "Certificate")]
代码如下:
/// <summary> /// cert.proto实现类 /// </summary> public class CertifierService: CertifierBase { [Authorize(AuthenticationSchemes = "Certificate")] public override Task<CertificateInfoResponse> GetCertificateInfo(Empty request, ServerCallContext context) { var httpContext = context.GetHttpContext(); var clientCertificate = httpContext.Connection.ClientCertificate; Console.WriteLine(clientCertificate); var name = string.Join(',', context.AuthContext.PeerIdentity.Select(i => i.Value)); var certificateInfo = new CertificateInfoResponse { HasCertificate = context.AuthContext.IsPeerAuthenticated, Name = name }; return Task.FromResult(certificateInfo); } /// <summary> /// 不加校验 /// 和上面比较不加校验的情况 /// </summary> /// <param name="request"></param> /// <param name="context"></param> /// <returns></returns> public override Task<CertificateInfoResponse> GetCertificateInfoNoAuth(Empty request, ServerCallContext context) { var certificateInfo = new CertificateInfoResponse { Name = "看看能不能通过哦" }; return Task.FromResult(certificateInfo); } }
(3).通过nuget安装程序集【Microsoft.AspNetCore.Authentication.Certificate】,在ConfigureService注册授权和认证中间件,在Configure开启认证和授权中间件,并映射CertifierService服务。
代码如下:
public void ConfigureServices(IServiceCollection services) { services.AddGrpc(); //认证 services.AddAuthentication("Certificate").AddCertificate(options => { // Not recommended in production environments. The example is using a self-signed test certificate. options.RevocationMode = X509RevocationMode.NoCheck; options.AllowedCertificateTypes = CertificateTypes.All; }); //授权 services.AddAuthorization(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); //认证 app.UseAuthentication(); //授权 app.UseAuthorization(); app.UseEndpoints(endpoints => { //映射grpc实现类 endpoints.MapGrpcService<CertifierService>(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); }); }
(4).在Program类中配置允许所有证书:httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
代码如下:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { //允许所有证书 webBuilder.ConfigureKestrel(kestrelOptions => { kestrelOptions.ConfigureHttpsDefaults(httpsOptions => { httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate; }); }); webBuilder.UseStartup<Startup>(); });
(PS:此处也可以配置 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; 对于没有证书请求,直接中断链接,而不是403未授权了)
3. 客户端配置
(1).对cert.proto文件添加服务链接引用,会自动安装相应的程序集(版本可能不是最新的,需要手动更新一下)。
(2).把前面导出来的证书ypfCert.pfx复制进来,并将属性改为"始终复制"。
(3).编写无证书的调用代码 和 有证书的调用代码.
代码如下:
class Program { static async Task Main(string[] args) { Console.WriteLine("----------------------------下面是无证书的请求------------------------------------"); try { var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new Certifier.CertifierClient(channel); var certificateInfo = await client.GetCertificateInfoAsync(new Empty()); Console.WriteLine($"返回信息HasCertificate={certificateInfo.HasCertificate},certificate name={certificateInfo.Name}"); } catch (RpcException ex) { Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}"); } catch (Exception ex) { Console.WriteLine($"无证书:{ex.Message}"); } Console.WriteLine("----------------------------下面是有证书的请求------------------------------------"); try { //证书相关配置 var handler = new HttpClientHandler(); var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location); var certPath = Path.Combine(basePath!, "MyCerts", "ypfCert.pfx"); var clientCertificate = new X509Certificate2(certPath, "123456"); handler.ClientCertificates.Add(clientCertificate); //携带证书创建通道 var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpClient = new HttpClient(handler) }); var client = new Certifier.CertifierClient(channel); var certificateInfo = await client.GetCertificateInfoAsync(new Empty()); Console.WriteLine($"请求成功,返回信息:HasCertificate={certificateInfo.HasCertificate},certificate name={certificateInfo.Name}"); } catch (RpcException ex) { Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}"); } catch (Exception ex) { Console.WriteLine($"有证书:{ex.Message}"); } Console.WriteLine("----------------------------下面是不加校验 不传证书的测试------------------------------------"); try { var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new Certifier.CertifierClient(channel); var certificateInfo = await client.GetCertificateInfoNoAuthAsync(new Empty()); Console.WriteLine($"返回信息certificate name={certificateInfo.Name}"); } catch (RpcException ex) { Console.WriteLine($"gRPC error from calling service: {ex.Status.Detail}"); } catch (Exception ex) { Console.WriteLine($"无证书:{ex.Message}"); } Console.ReadKey(); } }
4. 测试
将GrpcServer3和MyClient3配置同时启动,查看结果,然后把服务端配置改为 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate,再次运行查看结果。
测试1:(ClientCertificateMode.AllowCertificate)
测试2:(ClientCertificateMode.RequireCertificate)
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。