第三十七节:系统证书管理和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;
}
View Code

 (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);
        }
    }
View Code

 (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");
                });
            });
        }
View Code

 (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>();
                });
View Code

(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();
        }
    }
View Code

4. 测试

 将GrpcServer3和MyClient3配置同时启动,查看结果,然后把服务端配置改为 httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate,再次运行查看结果。

测试1:(ClientCertificateMode.AllowCertificate)

测试2:(ClientCertificateMode.RequireCertificate)

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

 

posted @ 2020-07-30 14:48  Yaopengfei  阅读(792)  评论(1编辑  收藏  举报