繁体   English   中英

C# 使用 gRPC 的证书身份验证

[英]C# Certificate Authentication with gRPC

我正在 C# 尝试 gRPC,并希望与 as.net core.Net 6 进行身份验证集成。我正在按照Microsoft 上的教程设置 gRPC 应用程序。

在我尝试添加身份验证之前,该应用程序没有任何问题。 我正在使用自签名证书,我已将其添加到我的本地用户存储以及我受信任的根存储中,以防证书因撤销而被拒绝。 我尝试了很多方法来帮助解决问题,但没有成功。 我不确定现在 go 在哪里,如果有任何指点/提示,我将不胜感激。

Kestrel Web 服务器 gRPC 文件

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using grpcServerTest.Services;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;

namespace grpcServerTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Additional configuration is required to successfully run gRPC on macOS.
            // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682

            // Add services to the container.
            builder.Services.AddGrpc();
            //builder.Services.AddAuthorization();
            builder.Services.AddAuthentication(
                CertificateAuthenticationDefaults.AuthenticationScheme)
                .AddCertificate(options =>
                {
                    options.AllowedCertificateTypes = CertificateTypes.All;
                    options.ValidateCertificateUse = false;
                    options.RevocationFlag = System.Security.Cryptography.X509Certificates.X509RevocationFlag.ExcludeRoot;
                    options.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck;
                    options.Events = new CertificateAuthenticationEvents
                    {
                        OnChallenge = context =>
                        {
                            return Task.CompletedTask;
                        },
                        OnAuthenticationFailed = context =>
                        {

                            return Task.CompletedTask;
                        },
                        OnCertificateValidated = context =>
                        {

                            if (true)
                            {
                                var claims = new[]
                                {
                                    new Claim(
                                        ClaimTypes.NameIdentifier,
                                        context.ClientCertificate.Subject,
                                        ClaimValueTypes.String, context.Options.ClaimsIssuer),
                                    new Claim(
                                        ClaimTypes.Name,
                                        context.ClientCertificate.Subject,
                                        ClaimValueTypes.String, context.Options.ClaimsIssuer)
                                };

                                context.Principal = new ClaimsPrincipal(
                                    new ClaimsIdentity(claims, context.Scheme.Name));
                                context.Success();
                            }

                            return Task.CompletedTask;
                        }
                    };
                });

            builder.Services.Configure<KestrelServerOptions>(options =>
            {
                options.ConfigureHttpsDefaults(options =>
                {
                    options.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
                    options.CheckCertificateRevocation = false;
                    //options.ServerCertificate = GetClientCertificate();
                    options.ClientCertificateValidation = (cert, chain, errors) =>
                    {
                        Console.WriteLine("Client Validation Called");
                        errors = System.Net.Security.SslPolicyErrors.None;
                        return true;
                    };
                });
            });

            var app = builder.Build();

            app.UseCertificateForwarding();

            app.UseAuthentication();
            //app.UseAuthorization();

            // Configure the HTTP request pipeline.
            app.MapGrpcService<GreeterService>();
            app.MapGet("/", () => "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");
        
            app.Run();
        }
    }
}

gRPC 客户端代码

// See https://aka.ms/new-console-template for more information
using Grpc.Core;
using Grpc.Net.Client;
using GrpcTest;
using System.Security.Cryptography.X509Certificates;

internal class Program
{
    private static async Task Main(string[] args)
    {
        Console.WriteLine("Hello, World!");

        var x509 = GetClientCertificate();

        var handler = new HttpClientHandler();

        handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls;
        handler.ClientCertificateOptions = ClientCertificateOption.Manual;
        handler.ClientCertificates.Add(x509);
        handler.UseProxy = false;

        Console.ReadKey();
        using var channel = GrpcChannel.ForAddress("https://localhost:7283", new GrpcChannelOptions()
        {
            HttpHandler = handler,
            DisposeHttpClient = true
        });

        var client = new Greeter.GreeterClient(channel);
        var reply = await client.SayHelloAsync(
                          new HelloRequest { Name = "GreeterClient" });
        Console.WriteLine("Greeting: " + reply.Message);
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }

    private static X509Certificate2 GetClientCertificate()
    {
        X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        try
        {
            userCaStore.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
            X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "grpctest", true);
            X509Certificate2 clientCertificate = null!;
            if (findResult.Count == 1)
            {
                clientCertificate = findResult[0];
            }
            else
            {
                throw new Exception("Unable to locate the correct client certificate.");
            }
            return clientCertificate;
        }
        catch
        {
            throw;
        }
        finally
        {
            userCaStore.Close();
        }
    }
}

这是 .NET 6 还是 7?

请参阅 https 中与 gRPC 和全链支持相关的部分:https://learn.microsoft.com/en-us/as.net/core/release-notes/as.netcore-7.0?view=as.netcore-7.0

使用 .NET 6 你可能需要使用类似https://github.com/MarkCiliaVincenti/TlsCertificateLoader

我是一个丁格斯。 我在请求中将公共证书传递给 HttpHandler,而不是实际执行身份验证的公钥和私钥。

我不确定这是否是合适的答案,或者我是否在某个地方有一个我看不到的奇怪配置值,但我能够获得要通过的证书。

这个问题,Kestrel 似乎一直在阻止证书,尽管我试图通过配置默认的 HTTPS 连接来绕过它。 一旦我在构建器中手动创建自己的侦听器并且不依赖默认配置,它似乎可以正常工作。

这是我用于我的构建器的代码,尽管可能有点过分,我不建议在不删除我的安全绕过的情况下将其用于生产。

 builder.Services.Configure<KestrelServerOptions>(options => { options.Listen(IPAddress.Loopback, 8080, listenOptions => { listenOptions.UseConnectionLogging(); listenOptions.UseHttps(options => { options.ClientCertificateMode = ClientCertificateMode.RequireCertificate; options.CheckCertificateRevocation = false; options.AllowAnyClientCertificate(); options.ClientCertificateValidation = (cert, chain, errors) => { Console.WriteLine("Client Validation Called"); errors = System.Net.Security.SslPolicyErrors.None; return true; }; }); }); });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM