aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Certificate authentication fails validation when intermediate certificate isn't in store against TLS specification

Open mk185147 opened this issue 1 year ago • 4 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

When you configure Kestler to use certificate authentication and a client sends certificate including an intermediate certificate to a server then server will fail the chain validation if it has the root CA in certificate store but doesn't have the intermediate certificate in the certificate store on Windows.

Expected Behavior

The chain validation should succeed, it should read the intermediate certificates from the TLS connection to build the chain.

Today the implementation is against RFC 5246, the RFC states about client certificate message: "This message conveys the client's certificate chain to the server; the server will use it when verifying the CertificateVerify message..." also it describes the structure "This is a sequence (chain) of certificates. The sender's certificate MUST come first in the list. Each following certificate MUST directly certify the one preceding it. Because certificate validation requires that root keys be distributed independently, the self-signed certificate that specifies the root certificate authority MAY be omitted from the chain, under the assumption that the remote end must already possess it in order to validate it in any case." I feel it clearly expects the server to do the validation using the chain that is sent (with the exception of root).

Steps To Reproduce

Create CA, intermediate and child certificates (e.g. like described https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-6.0#create-certificates-in-powershell)

You can use a simple web server with certificate authentication, e.g.:

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate();
builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.ConfigureHttpsDefaults(options =>
        options.ClientCertificateMode = ClientCertificateMode.AllowCertificate);
});

var app = builder.Build();

app.UseAuthentication();
app.UseHttpsRedirection();

app.MapGet("/", () => "Hello World!");

app.Run();

Put the CA into windows store where you have the server.

Then put on a different computer all 3 certificates into certificate stores and call the service from that computer. The service will ask you for a certificate, choose the child certificate.

Exceptions (if any)

No response

.NET Version

6.0.400

Anything else?

This was first risen as a ticket https://github.com/dotnet/aspnetcore/issues/42010 but the ticket was closed based on improvement to my other request "how to work around the issue" which was made more possible using making a connection context available (https://github.com/dotnet/aspnetcore/pull/33953). Nevertheless, the asp .net default code is still not following the TLS specification for client authentication (and it is hard to customize it to follow it).

mk185147 avatar Aug 31 '22 16:08 mk185147

Not follow it the TLS specification? I think it’s fair to say you’d prefer it if there was an easier way to get the full cert chain without writing code but this doesn’t have anything to do with “the specification” AFAIK (happy yo be proven wrong).

Downloading the chain per request is a performance concern (and security concern) that’s why we don’t want to do it by default.

davidfowl avatar Aug 31 '22 20:08 davidfowl

Downloading the chain per request is a performance concern (and security concern) that’s why we don’t want to do it by default.

This isn't downloading the chain though; the client sends the leaf cert and optionally any intermediate CAs (but not the root CA that everything chains off).

I wonder what http.sys does in this regard. It'd be a bit of a rearchitecting, assuming we can get the leaf and intermediate chain. It'd probably be a performance drag, because if we implement a custom chain store, it replaces the default one, not supplements it, so there would be a lot of copying there, or caching with concurrency problems when you see a new intermediate.

blowdart avatar Sep 01 '22 14:09 blowdart

Not follow it the TLS specification? I think it’s fair to say you’d prefer it if there was an easier way to get the full cert chain without writing code but this doesn’t have anything to do with “the specification” AFAIK (happy yo be proven wrong).

See https://www.rfc-editor.org/rfc/rfc5246 , sections 7.4.6. and 7.4.2.

mk185147 avatar Sep 20 '22 08:09 mk185147

Looking at the code behind AddCertificate, it looks like it only has access to the leaf certificate to perform chain validation. So maybe #2708 is needed to fix this issue?

MageFroh avatar Jan 05 '23 09:01 MageFroh