Kestrel and the chain of trust

Presenting a public key certificate issued by an intermediate certificate authority, using Kestrel, only presents the leaf certificate and does not include the intermediate certificate.

Using Kestrel, the default, cross-platform, web server for ASP.NET Core, to serve secure content will only present the leaf certificate to the client instead of presenting the intermediate certificate and leaf certificate.

Normally, this is not a problem as the client will simply acquire the intermediate certificate and complete the chain. However, some third-party systems will not complete the chain and will expect the intermediate certificate(s) to be included. This is usually indicated by the error "Error: unable to verify the first certificate".

Since the intermediate certificate is not included with the leaf certificate when using Kestrel a Qualys SSL Server Test will cap the grade to B.

Looking at the Qualys test report we can see that an additional download of the intermediate certificate was necessary to complete the certificate chain.

Incomplete chain issues

Kestrel

If we want Kestrel to send the intermediate certificate together with the leaf certificate we will need to add an additional configuration to Kestrel's listen options. Thanks to davidfowl for providing this extension method and snippet below:

builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureEndpointDefaults(listenOptions =>
    {
        listenOptions.UseHttpsWithFullChain("cert.crt", "cert.key");
    });
});

Where UseHttpsWithFullChain is the following extension method:

public static ListenOptions UseHttpsWithFullChain(this ListenOptions listenOptions, string certPath, string keyPath)
    {
        var leafCertWithKey = X509Certificate2
          .CreateFromPemFile(certPath, keyPath);

        var fullChain = new X509Certificate2Collection();
        fullChain.ImportFromPemFile(certPath);

        var options = new SslServerAuthenticationOptions
        {
            ServerCertificateContext = SslStreamCertificateContext
              .Create(leafCertWithKey, fullChain, offline: true)
        };

        return listenOptions.UseHttps(new TlsHandshakeCallbackOptions
        {
            OnConnection = context => new ValueTask<SslServerAuthenticationOptions>(options)
        });
    }

References

https://github.com/dotnet/aspnetcore/issues/36202