繁体   English   中英

OnCertificateValidated 未运行 - 自签名证书客户端身份验证 - ASP.NET Core 和 Kestrel

[英]OnCertificateValidated not running - Self-Signed Certificate Client Authentication - ASP.NET Core and Kestrel

我想使用基于证书的身份验证对连接到在 Kestrel 上运行的 ASP.NET Core Web API (.NET 5) 的客户端进行身份验证。

在我的Startup.cs我在ConfigureServices有以下内容:

services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.AllowedCertificateTypes = CertificateTypes.All;
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                // More code to verify certificates
            },
            OnAuthenticationFailed = context =>
            {
                // More code
            }
        };
    });

// Other services

Configure

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthentication();

app.UseEndpoints(endpoints =>
{
    // Endpoints
});

Program.cs我包含了:

webBuilder.ConfigureKestrel(o =>
{
    o.ConfigureHttpsDefaults(o =>
        o.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});

如果我连接到API的浏览器,它提示我要证书,但我选择一个证书后,无论是OnCertificateValidated也不OnAuthenticationFailed事件被触发。 经过一些进一步的测试,我意识到Startup.cs AddCertificate调用中的整个选项配置委托永远不会运行。 这让我觉得我缺少某种 Kestrel 的配置,但我不知道那是什么。 请注意,我的 Web API 不使用 IIS 托管。 我还需要做什么才能使用基于自签名证书的身份验证?

到目前为止,我拥有的代码基于此处文档中的说明: https : //docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0

好的,所以最后我能够解决我自己的问题。 解决它有两个不同的部分,但最终只需要对我的项目代码进行一些小的修改。

识别客户端证书

首先,服务器未将自签名客户端证书识别为有效证书。 这可以通过以下任一方式解决: 1. 将所有客户端证书(或对所有证书进行签名的根 CA)添加到操作系统的可信证书存储中,或者 2. 将ClientCertificateValidation回调添加到 kestrel 以确定证书是否为接受或拒绝。

#2 的示例(对Program.csConfigureHttpsDefaults lambda 的调整)如下:

webBuilder.ConfigureKestrel(o =>
{
    o.ConfigureHttpsDefaults(opts =>
    {
        opts.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        opts.ClientCertificateValidation = (cert, chain, policyErrors) =>
        {
            // Certificate validation logic here
            // Return true if the certificate is valid or false if it is invalid
        };
    });
});

作为旁注,调用opts.AllowAnyClientCertificate()是添加始终返回 true 的ClientCertificateValidation回调的简写,使所有自签名证书都有效。


所需授权

应用这些方法之一后,我的 API 将接受来自有效证书的查询,但我在OnCertificateValidated事件中的额外证书验证逻辑仍未运行。 这是因为,根据对ASP.NET Core 问题 #14033 的评论,除非为正在访问的端点启用授权,否则此事件的额外证书验证将永远不会运行。 这是有道理的,因为此事件通常用于根据主题上的 ASP.NET Core Docs的证书生成ClaimsPrincipal 将 ASP.NET 设置为使用授权并要求对 API 调用进行授权(例如,通过将[Authorize]属性应用于所有控制器)会导致对这些 API 调用运行额外的身份验证检查。

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthentication();

// Adding this and adding the [Authorize] attribute
// to controllers fixes the problem.
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    // Endpoints
});

在此之后,每个连接都会调用我的OnCertificateValidated事件,并且我能够执行额外的身份验证逻辑并拒绝无效证书。

接受的答案对我很有帮助(谢谢!)但它并没有完全解决我的问题。

我发现ClientCertificateValidation函数不是验证(和拒绝)证书的唯一地方。 使用AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme). AddCertificate... AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme). AddCertificate...还导致在处理ClientCertificateValidation之后触发另一个验证。

为了解决我的问题,我必须在CertificateAuthenticationOptions:行中配置CustomTrustStore CertificateAuthenticationOptions:

AddCertificate(options =>
{
    options.AllowedCertificateTypes = CertificateTypes.All;
    options.ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust;
    options.CustomTrustStore = new X509Certificate2Collection { rootCert };
    options.RevocationMode = X509RevocationMode.NoCheck;
    options.Events = new CertificateAuthenticationEvents
    {
        OnCertificateValidated = context =>
        {
            if (validationService.ValidateCertificate(context.ClientCertificate))
            {
                context.Success();
            }
            else
            {
                context.Fail("invalid cert");
            }

            return Task.CompletedTask;
        },
        OnAuthenticationFailed = context =>
        {
            context.Fail("invalid cert");
            return Task.CompletedTask;
        }
    };
});

rootCert是通过以下方式启动的:

var rootCert = new X509Certificate2("RootCert.pfx", "1234");

并且RootCert.pfx作为文件添加到项目中。

由于我们现在使用AddAuthentication(.).AddCertificate的内置验证,我们应该使用AllowAnyCertificate()禁用较早的验证(此验证不知道我们的自定义根信任):

builder.WebHost.ConfigureKestrel(kso =>
{
    kso.ConfigureHttpsDefaults(cao => {
        cao.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        cao.AllowAnyClientCertificate();
        cao.CheckCertificateRevocation = false;
    });
});

暂无
暂无

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

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