【问题标题】:Certificate Authentication events not triggered in ASP .NET Core 3.1 APIASP .NET Core 3.1 API 中未触发证书身份验证事件
【发布时间】:2021-04-12 13:29:04
【问题描述】:

我想在我的 .NET Core 3.1 API 上实现证书身份验证。我按照微软在这里列出的步骤进行操作:https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0

但是,我认为“OnCertificateValidated”和“OnAuthenticationFailed”事件甚至不会触发。断点永远不会被命中,我什至尝试添加应该在那里中断的代码只是为了测试,但它永远不会失败(很可能是因为它永远不会到达那里)

我正在尝试验证客户端证书 CN,并且我只想允许某些 CN 能够调用我的 API。

您可以在下面找到我的代码。我做错了什么?

启动

    public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(
            CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate(options =>
            {
                options.AllowedCertificateTypes = CertificateTypes.All;
                options.Events = new CertificateAuthenticationEvents
                {
                    OnCertificateValidated = context =>
                    {
                        var validationService =
                        context.HttpContext.RequestServices.GetRequiredService<ICertificateValidationService>();

                        if (validationService.ValidateCertificate(context.ClientCertificate))
                        {
                            context.Success();
                        }
                        else
                        {
                            context.Fail($"Unrecognized client certificate: {context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false)}");
                        }
                        int test = Convert.ToInt32("test");
                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        int test = Convert.ToInt32("test");
                        context.Fail($"Invalid certificate");
                        return Task.CompletedTask;
                    }
                };
            });


        services.AddHealthChecks();
        services.AddControllers(setupAction =>
        {
            setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status406NotAcceptable));
            setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status500InternalServerError));
            setupAction.ReturnHttpNotAcceptable = true;  
            
        }).AddXmlDataContractSerializerFormatters();

        services.AddScoped<IJobServiceRepository, JobServiceRepository>();
        services.AddScoped<ICaptureServiceRepository, CaptureServiceRepository>();

        services.Configure<KTAEndpointConfig>(Configuration.GetSection("KTAEndpointConfig"));

        services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

        services.AddVersionedApiExplorer(setupAction =>
        {
            setupAction.GroupNameFormat = "'v'VV";
        });

        services.AddApiVersioning(setupAction =>
        {
            setupAction.AssumeDefaultVersionWhenUnspecified = true;
            setupAction.DefaultApiVersion = new ApiVersion(1, 0);
            setupAction.ReportApiVersions = true;
        });

        var apiVersionDecriptionProvider = services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();

        services.AddSwaggerGen(setupAction =>
        {
            foreach (var description in apiVersionDecriptionProvider.ApiVersionDescriptions)
            {
                setupAction.SwaggerDoc($"TotalAgilityOpenAPISpecification{description.GroupName}", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "TotalAgility API",
                    Version = description.ApiVersion.ToString(),
                    Description = "Kofax TotalAgility wrapper API to allow creating KTA jobs and uploading documents",
                    Contact = new Microsoft.OpenApi.Models.OpenApiContact()
                    {
                        Email = "shivam.sharma@rbc.com",
                        Name = "Shivam Sharma"
                    }
                });

                setupAction.OperationFilter<AddRequiredHeaderParameter>();

                var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlCommentsFullPath = Path.Combine(AppContext.BaseDirectory, xmlCommentsFile);
                setupAction.IncludeXmlComments(xmlCommentsFullPath);
            }

            setupAction.DocInclusionPredicate((documentName, apiDescription) =>
            {
                var actionApiVersionModel = apiDescription.ActionDescriptor
                .GetApiVersionModel(ApiVersionMapping.Explicit | ApiVersionMapping.Implicit);

                if (actionApiVersionModel == null)
                {
                    return true;
                }

                if (actionApiVersionModel.DeclaredApiVersions.Any())
                {
                    return actionApiVersionModel.DeclaredApiVersions.Any(v =>
                    $"TotalAgilityOpenAPISpecificationv{v.ToString()}" == documentName);
                }
                return actionApiVersionModel.ImplementedApiVersions.Any(v =>
                    $"TotalAgilityOpenAPISpecificationv{v.ToString()}" == documentName);

            });
        });

        services.AddHttpsRedirection((httpsOpts) =>
        {
            //httpsOpts.HttpsPort = 443;
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider apiVersionDescriptionProvider)
    {
        app.UseApiExceptionHandler();

        app.UseHeaderLogContextMiddleware();

        app.UseHttpsRedirection();

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseSwagger(setupAction =>
        {
            setupAction.SerializeAsV2 = true;
        });

        app.UseSwaggerUI(setupAction =>
        {

            foreach (var decription in apiVersionDescriptionProvider.ApiVersionDescriptions)
            {
                setupAction.SwaggerEndpoint($"/swagger/TotalAgilityOpenAPISpecification{decription.GroupName}/swagger.json",
                decription.GroupName.ToUpperInvariant());
            }

            //setupAction.SwaggerEndpoint("/swagger/TotalAgilityOpenAPISpecification/swagger.json",
            //"TotalAgility API");

            setupAction.RoutePrefix = "";
        });

        app.UseSerilogRequestLogging();

        app.UseHeaderValidation();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireAuthorization();
            endpoints.MapHealthChecks("/health");
        });

    }
}

证书验证服务

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace TotalAgility_API.Services
{
    public class CertificateValidationService : ICertificateValidationService
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger<CertificateValidationService> _logger;

        public CertificateValidationService(IConfiguration configuration, ILogger<CertificateValidationService> logger)
        {
            _logger = logger;
            _configuration = configuration;
        }

        public bool ValidateCertificate(X509Certificate2 clientCert)
        {
            List<string> allowedCNs = new List<string>();
            _configuration.GetSection("AllowedClientCertCNList").Bind(allowedCNs);

            string cn = clientCert.GetNameInfo(X509NameType.SimpleName, false);
            if (allowedCNs.Contains(cn))
            {
                return true;
            }
            else
            {
                _logger.LogWarning("Invalid Cert CN: {CN}", cn);
                return false;
            }
        }
    }
}

appsettings.json

    {
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    }
  },
  "AllowedHosts": "*",
  "AllowedClientCertCNList": [
    "1",
    "2",
    "3",
    "4"
  ]
}

任何帮助将不胜感激。我是新手,不知道如何继续。

【问题讨论】:

  • 我建议将日志记录级别设置为调试并查看结果,因为证书身份验证处理程序会发出日志条目。您确定您在请求中传递了客户端证书吗?
  • 你有过什么运气吗?我遇到了与 OnCertificateValidated 和 OnAuthenticationFailed 未命中相同的问题。我确实发现还有另一个事件:OnChallenge,您可以将其包括在内。那个被我击中了。我建议添加它以查看它是否被击中。虽然我不确定如果是的话该怎么办。毫无疑问,微软关于该事件的文档没有帮助。

标签: asp.net-core authentication certificate


【解决方案1】:

我在 IIS Express 上遇到了类似问题,仅触发了“OnChallenge”,但没有触发“OnCertificateValidated”或“OnAuthenticationFailed”,并且不断收到 403 状态响应。

This answer 从另一个问题解决了我的情况(IIS Express)。在 .vs\config\applicationhost.config 中设置访问 sslFlags 以使用客户端证书:

<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />

【讨论】:

    猜你喜欢
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 2017-09-11
    • 2020-06-16
    • 2020-10-15
    • 2020-06-05
    相关资源
    最近更新 更多