Delen via


Certificaatverificatie configureren in ASP.NET Core

Microsoft.AspNetCore.Authentication.Certificate bevat een implementatie die vergelijkbaar is met certificaatverificatie voor ASP.NET Core. Certificaatverificatie vindt plaats op TLS-niveau, lang voordat het ooit ASP.NET Core wordt. Nauwkeuriger is dit een verificatiehandler waarmee het certificaat wordt gevalideerd en u vervolgens een gebeurtenis krijgt waarin u dat certificaat kunt oplossen naar een ClaimsPrincipal.

U moetuw server configureren voor certificaatverificatie, of het nu IIS, KestrelAzure Web Apps of wat u ook gebruikt.

Scenario's voor proxy en load balancer

Certificaatverificatie is een stateful scenario dat voornamelijk wordt gebruikt waarbij een proxy of load balancer geen verkeer tussen clients en servers verwerkt. Als een proxy of load balancer wordt gebruikt, werkt certificaatauthenticatie alleen als de proxy of load balancer:

  • Verzorgt de authenticatie.
  • Geeft de gebruikersverificatiegegevens door aan de app (bijvoorbeeld in een aanvraagheader), die op de verificatiegegevens reageert.

Een alternatief voor certificaatverificatie in omgevingen waarin proxy's en load balancers worden gebruikt, is Active Directory Federated Services (ADFS) met OpenID Connect (OIDC).

Aan de slag

Verkrijg een HTTPS-certificaat, pas dit toe en configureer uw server om certificaten te vereisen.

In de web-app:

  • Voeg een verwijzing toe naar het NuGet-pakket Microsoft.AspNetCore.Authentication.Certificate .
  • In Program.cs, bel builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...);. Geef een gemachtigde op om OnCertificateValidated aanvullende validatie uit te voeren op het clientcertificaat dat is verzonden met aanvragen. Zet deze informatie om in een ClaimsPrincipal en stel deze in op de context.Principal eigenschap.

Als de verificatie mislukt, retourneert deze handler een 403 (Forbidden) antwoord in plaats van een 401 (Unauthorized), zoals u zou verwachten. De redenering is dat de verificatie moet plaatsvinden tijdens de eerste TLS-verbinding. Tegen de tijd dat het de handler bereikt, is het te laat. U kunt de verbinding niet upgraden van een anonieme verbinding naar een verbinding met een certificaat.

UseAuthentication is vereist om HttpContext.User in te stellen op een ClaimsPrincipal gemaakt op basis van het certificaat. Voorbeeld:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate();

var app = builder.Build();

app.UseAuthentication();

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

app.Run();

In het voorgaande voorbeeld ziet u de standaardmethode voor het toevoegen van certificaatverificatie. De handler maakt een gebruikersprincipaal met behulp van de algemene certificaateigenschappen.

Certificaatvalidatie configureren

De CertificateAuthenticationOptions handler heeft een aantal ingebouwde validaties die de minimale validaties zijn die u op een certificaat moet uitvoeren. Elk van deze instellingen is standaard ingeschakeld.

AllowedCertificateTypes = Gekoppeld, Zelfondertekend, of Alle (Gekoppeld | Zelfondertekend)

Standaardwaarde: CertificateTypes.Chained

Met deze controle wordt gecontroleerd of alleen het juiste certificaattype is toegestaan. Als de app zelfondertekende certificaten gebruikt, moet deze optie worden ingesteld op CertificateTypes.All of CertificateTypes.SelfSigned.

ChainTrustValidatieMode

Standaardwaarde: X509ChainTrustMode.System

Het certificaat dat door de client wordt gepresenteerd, moet worden gekoppeld aan een vertrouwd basiscertificaat. Met deze controle bepaalt u welk vertrouwensarchief deze basiscertificaten bevat.

De handler maakt standaard gebruik van het vertrouwensarchief van het systeem. Als het gepresenteerde clientcertificaat moet worden gekoppeld aan een basiscertificaat dat niet wordt weergegeven in het systeemvertrouwensarchief, kan deze optie worden ingesteld op X509ChainTrustMode.CustomRootTrust om de handler te laten gebruiken CustomTrustStore.

CustomTrustStore

Standaardwaarde: Leeg X509Certificate2Collection

Als de ChainTrustValidationMode eigenschap van de handler is ingesteld op X509ChainTrustMode.CustomRootTrust, bevat deze X509Certificate2Collection elk certificaat dat wordt gebruikt om het clientcertificaat te valideren tot aan een vertrouwde root, inclusief de vertrouwde root.

Wanneer de client een certificaat presenteert dat deel uitmaakt van een certificaatketen met meerdere niveaus, CustomTrustStore moet elk verlenend certificaat in de keten bevatten.

Certificaatgebruik valideren

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat dat door de client wordt gepresenteerd, het uitgebreide sleutelgebruik (EKU) voor clientverificatie heeft, of dat er helemaal geen EKU's zijn. Zoals de specificaties zeggen, als er geen EKU is opgegeven, worden alle EKU's als geldig beschouwd.

BevestigGeldigheidsperiode

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat binnen de geldigheidsperiode valt. Bij elke aanvraag zorgt de handler ervoor dat een certificaat dat geldig was toen het werd gepresenteerd, niet is verlopen tijdens de huidige sessie.

Intrekkingsflag

Standaardwaarde: X509RevocationFlag.ExcludeRoot

Een vlag die aangeeft welke certificaten in de keten worden gecontroleerd op intrekking.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Intrekkingsmodus

Standaardwaarde: X509RevocationMode.Online

Een vlag die aangeeft hoe intrekkingscontroles worden uitgevoerd.

Het specificeren van een onlinecontrole kan leiden tot een lange vertraging terwijl er contact wordt opgenomen met de certificaatinstantie.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Kan ik mijn app zo configureren dat alleen een certificaat op bepaalde paden is vereist?

Dit is niet mogelijk. Onthoud dat de certificaatuitwisseling wordt uitgevoerd aan het begin van het HTTPS-gesprek. Dit wordt gedaan door de server voordat de eerste aanvraag op die verbinding wordt ontvangen, zodat het niet mogelijk is om het bereik te bepalen op basis van aanvraagvelden.

Handler-gebeurtenissen

De handler heeft twee gebeurtenissen:

  • OnAuthenticationFailed: Wordt aangeroepen als er een uitzondering optreedt tijdens de verificatie en kunt u reageren.
  • OnCertificateValidated: Aangeroepen nadat het certificaat succesvol gevalideerd is en er een standaardprincipal is gemaakt. Met deze gebeurtenis kunt u uw eigen validatie uitvoeren en de principal uitbreiden of vervangen. Voorbeelden hiervan zijn:
    • Bepalen of het certificaat bekend is bij uw services.

    • Uw eigen principal samenstellen. Bekijk het volgende voorbeeld:

      builder.Services.AddAuthentication(
              CertificateAuthenticationDefaults.AuthenticationScheme)
          .AddCertificate(options =>
          {
              options.Events = new CertificateAuthenticationEvents
              {
                  OnCertificateValidated = context =>
                  {
                      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;
                  }
              };
          });
      

Als het binnenkomende certificaat niet voldoet aan uw extra validatie, geef dan een foutreden door aan context.Fail("failure reason").

Voor een betere functionaliteit, roep een service aan die is geregistreerd in dependency injection en verbinding maakt met een database of een ander type gebruikersopslag. Open de service met behulp van de context die is doorgegeven aan de gemachtigde. Bekijk het volgende voorbeeld:

builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                var validationService = context.HttpContext.RequestServices
                    .GetRequiredService<ICertificateValidationService>();

                if (validationService.ValidateCertificate(context.ClientCertificate))
                {
                    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;
            }
        };
    });

Conceptueel is de validatie van het certificaat een autorisatieprobleem. Het toevoegen van een controle op bijvoorbeeld een uitgever of vingerafdruk in een autorisatiebeleid, in plaats van binnen OnCertificateValidated, is volledig acceptabel.

Configureer uw server zo dat certificaten vereist zijn.

Kestrel

Program.cs Configureer Kestrelals volgt in:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.ConfigureHttpsDefaults(options =>
        options.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});

Opmerking

Eindpunten die zijn gemaakt door Listenaan te roepen voordatConfigureHttpsDefaults worden aangeroepen, worden de standaardwaarden niet toegepast.

IIS

Voer de volgende stappen uit in IIS Manager:

  1. Selecteer uw site op het tabblad Verbindingen .
  2. Dubbelklik op de optie SSL-instellingen in het venster Functiesweergave .
  3. Vink het selectievakje SSL vereisen aan en selecteer de optie Vereisen in de sectie Clientcertificaten.

Clientcertificaatinstellingen in IIS

Azure en aangepaste webproxy's

Raadpleeg de host- en implementatiedocumentatie voor het configureren van de middleware voor het doorsturen van certificaten.

Certificaatverificatie gebruiken in Azure Web Apps

Er is geen doorstuurconfiguratie vereist voor Azure. De doorstuurconfiguratie wordt ingesteld door de Middleware voor het doorsturen van certificaten.

Opmerking

Middleware voor het doorsturen van certificaten is vereist voor dit scenario.

Zie Een TLS/SSL-certificaat gebruiken in uw code in Azure App Service (Documentatie voor Azure) voor meer informatie.

Certificaatverificatie gebruiken in aangepaste webproxy's

De AddCertificateForwarding methode wordt gebruikt om het volgende op te geven:

  • De naam van de clientheader.
  • Hoe het certificaat moet worden geladen (met behulp van de HeaderConverter eigenschap).

In aangepaste webproxy's wordt het certificaat doorgegeven als een aangepaste aanvraagheader, bijvoorbeeld X-SSL-CERT. Als u het wilt gebruiken, configureert u het doorsturen van certificaten in Program.cs:

builder.Services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "X-SSL-CERT";

    options.HeaderConverter = headerValue =>
    {
        X509Certificate2? clientCertificate = null;

        if (!string.IsNullOrWhiteSpace(headerValue))
        {
            clientCertificate = new X509Certificate2(StringToByteArray(headerValue));
        }

        return clientCertificate!;

        static byte[] StringToByteArray(string hex)
        {
            var numberChars = hex.Length;
            var bytes = new byte[numberChars / 2];

            for (int i = 0; i < numberChars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            }

            return bytes;
        }
    };
});

nl-NL: Als de app omgekeerd geproxy'd wordt door NGINX met de configuratie proxy_set_header ssl-client-cert $ssl_client_escaped_cert of geïmplementeerd op Kubernetes met behulp van NGINX Ingress, wordt het clientcertificaat doorgegeven aan de app in URL-gecodeerde vorm. Als u het certificaat wilt gebruiken, decodert u het als volgt:

builder.Services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "ssl-client-cert";

    options.HeaderConverter = (headerValue) =>
    {
        X509Certificate2? clientCertificate = null;

        if (!string.IsNullOrWhiteSpace(headerValue))
        {
            clientCertificate = X509Certificate2.CreateFromPem(
                WebUtility.UrlDecode(headerValue));
        }

        return clientCertificate!;
    };
});

Voeg de middleware toe in Program.cs. UseCertificateForwarding wordt aangeroepen vóór de aanroepen naar UseAuthentication en UseAuthorization:

var app = builder.Build();

app.UseCertificateForwarding();

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

Een afzonderlijke klasse kan worden gebruikt om validatielogica te implementeren. Omdat hetzelfde zelfondertekende certificaat in dit voorbeeld wordt gebruikt, moet u ervoor zorgen dat alleen uw certificaat kan worden gebruikt. Controleer of de vingerafdrukken van zowel het clientcertificaat als het servercertificaat overeenkomen, anders kan elk certificaat worden gebruikt en is voldoende om te verifiëren. Dit wordt gebruikt in de AddCertificate methode. U kunt hier ook het onderwerp of de uitgever valideren als u intermediaire of subcertificaten gebruikt.

using System.Security.Cryptography.X509Certificates;

namespace CertAuthSample.Snippets;

public class SampleCertificateValidationService : ICertificateValidationService
{
    public bool ValidateCertificate(X509Certificate2 clientCertificate)
    {
        // Don't hardcode passwords in production code.
        // Use a certificate thumbprint or Azure Key Vault.
        var expectedCertificate = new X509Certificate2(
            Path.Combine("/path/to/pfx"), "1234");

        return clientCertificate.Thumbprint == expectedCertificate.Thumbprint;
    }
}

Een HttpClient implementeren met behulp van een certificaat en IHttpClientFactory

In het volgende voorbeeld wordt een clientcertificaat aan een HttpClientHandler toegevoegd met behulp van de ClientCertificates eigenschap van de handler. Deze handler kan vervolgens worden gebruikt in een benoemd exemplaar van een HttpClient met behulp van de ConfigurePrimaryHttpMessageHandler methode. Dit is ingesteld in Program.cs:

var clientCertificate =
    new X509Certificate2(
      Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");

builder.Services.AddHttpClient("namedClient", c =>
{
}).ConfigurePrimaryHttpMessageHandler(() =>
{
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(clientCertificate);
    return handler;
});

De IHttpClientFactory kan vervolgens worden gebruikt om het benoemde exemplaar op te halen met behulp van de handler en het certificaat. De CreateClient methode met de naam van de client die in Program.cs is gedefinieerd, wordt gebruikt om het exemplaar op te halen. De HTTP-aanvraag kan naar behoefte worden verzonden met behulp van de client:

public class SampleHttpService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public SampleHttpService(IHttpClientFactory httpClientFactory)
        => _httpClientFactory = httpClientFactory;

    public async Task<JsonDocument> GetAsync()
    {
        var httpClient = _httpClientFactory.CreateClient("namedClient");
        var httpResponseMessage = await httpClient.GetAsync("https://example.com");

        if (httpResponseMessage.IsSuccessStatusCode)
        {
            return JsonDocument.Parse(
                await httpResponseMessage.Content.ReadAsStringAsync());
        }

        throw new ApplicationException($"Status code: {httpResponseMessage.StatusCode}");
    }
}

Als het juiste certificaat naar de server wordt verzonden, worden de gegevens geretourneerd. Als er geen certificaat of het verkeerde certificaat wordt verzonden, wordt een HTTP 403-statuscode geretourneerd.

Certificaten maken in PowerShell

Het maken van de certificaten is het moeilijkste onderdeel van het instellen van deze stroom. U kunt een basiscertificaat maken met behulp van de New-SelfSignedCertificate PowerShell-cmdlet. Wanneer u het certificaat maakt, gebruikt u een sterk wachtwoord. Het is belangrijk om de KeyUsageProperty parameter en de KeyUsage parameter toe te voegen, zoals wordt weergegeven.

Basis-CA maken

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath root_ca_dev_damienbod.crt

Opmerking

De -DnsName parameterwaarde moet overeenkomen met het implementatiedoel van de app. Bijvoorbeeld 'localhost' voor ontwikkeling.

Installeren in de vertrouwde basis

Het basiscertificaat moet worden vertrouwd op uw hostsysteem. Alleen basiscertificaten die door een certificeringsinstantie zijn gemaakt, worden standaard vertrouwd. Zie de Windows-documentatie of de Import-Certificate PowerShell-cmdlet voor informatie over het vertrouwen van het basiscertificaat in Windows.

Tussenliggend certificaat

Er kan nu een tussenliggend certificaat worden gemaakt op basis van het basiscertificaat. Dit is niet vereist voor alle use cases, maar mogelijk moet u veel certificaten maken of groepen certificaten activeren of uitschakelen. De TextExtension parameter is vereist om de padlengte in te stellen in de basisbeperkingen van het certificaat.

Het tussenliggende certificaat kan vervolgens worden toegevoegd aan het vertrouwde tussenliggende certificaat in het Windows-hostsysteem.

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint of the root..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "intermediate_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "intermediate_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\intermediate_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath intermediate_dev_damienbod.crt

Een onderliggend certificaat maken op basis van een tussenliggend certificaat

Er kan een kindercertificaat worden gemaakt op basis van het tussenliggende certificaat. Dit is de eindentiteit die geen kindcertificaten hoeft te maken.

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the Intermediate certificate..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Kindcertificaat maken van rootcertificaat

Een kindcertificaat kan ook rechtstreeks vanuit het basiscertificaat worden gemaakt.

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the root cert..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Voorbeeld rootcertificaat - tussenliggend certificaat - certificaat

$mypwdroot = ConvertTo-SecureString -String "1234" -Force -AsPlainText
$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

Get-ChildItem -Path cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwdroot

Export-Certificate -Cert cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 -FilePath root_ca_dev_damienbod.crt

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\0C89639E4E2998A93E423F919B36D4009A0F9991 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 -FilePath child_a_dev_damienbod.crt

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\BA9BF91ED35538A01375EFC212A2F46104B33A44 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_b_from_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_b_from_a_dev_damienbod.com" 

Get-ChildItem -Path cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_b_from_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A -FilePath child_b_from_a_dev_damienbod.crt

Wanneer u de basis-, tussen- of onderliggende certificaten gebruikt, kunnen de certificaten worden gevalideerd met behulp van de vingerafdruk of PublicKey, indien nodig:

using System.Security.Cryptography.X509Certificates;

namespace CertAuthSample.Snippets;

public class SampleCertificateThumbprintsValidationService : ICertificateValidationService
{
    private readonly string[] validThumbprints = new[]
    {
        "141594A0AE38CBBECED7AF680F7945CD51D8F28A",
        "0C89639E4E2998A93E423F919B36D4009A0F9991",
        "BA9BF91ED35538A01375EFC212A2F46104B33A44"
    };

    public bool ValidateCertificate(X509Certificate2 clientCertificate)
        => validThumbprints.Contains(clientCertificate.Thumbprint);
}

Opslaan in cache van certificaatvalidatie

.NET 5 of nieuwere versies ondersteunen de mogelijkheid om caching van validatieresultaten in te schakelen. De caching verbetert de prestaties van certificaatverificatie aanzienlijk, omdat validatie een dure bewerking is.

Certificaatverificatie schakelt standaard caching uit. Om caching in te schakelen, roept u AddCertificateCache aan in Program.cs:

builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate()
    .AddCertificateCache(options =>
    {
        options.CacheSize = 1024;
        options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
    });

De standaard caching-implementatie slaat resultaten op in het geheugen. U kunt uw eigen cache opgeven door ICertificateValidationCache te implementeren en het te registreren via afhankelijkheidsinjectie. Bijvoorbeeld: services.AddSingleton<ICertificateValidationCache, YourCache>().

Optionele clientcertificaten

Deze sectie bevat informatie voor apps die een subset van de app met een certificaat moeten beveiligen. Voor een Razor pagina of controller in de app zijn bijvoorbeeld clientcertificaten vereist. Dit brengt uitdagingen met zich mee als clientcertificaten:

  • Zijn een TLS-functie, geen HTTP-functie.
  • Wordt onderhandeld per verbinding en meestal aan het begin van de verbinding voordat er HTTP-gegevens beschikbaar zijn.

Er zijn twee benaderingen voor het implementeren van optionele clientcertificaten:

  1. Gebruik afzonderlijke hostnamen (SNI) en doorsturen. Hoewel er meer werk nodig is om te configureren, wordt dit aanbevolen omdat dit werkt in de meeste omgevingen en protocollen.
  2. Heronderhandeling tijdens een HTTP-aanvraag. Dit heeft verschillende beperkingen en wordt niet aanbevolen.

Afzonderlijke hosts (SNI)

Aan het begin van de verbinding is alleen de SNI-† (Server Name Indication) bekend. Clientcertificaten kunnen per hostnaam worden geconfigureerd, zodat de ene host deze vereist en een andere niet.

.NET 5 of hoger voegt handigere ondersteuning toe voor het omleiden om optionele clientcertificaten te verkrijgen. Zie het voorbeeld van optionele certificaten voor meer informatie.

  • Voor aanvragen voor de web-app waarvoor een clientcertificaat vereist is maar niet aanwezig is:
    • Omleiden naar dezelfde pagina met behulp van het met het clientcertificaat beveiligde subdomein.
    • Bijvoorbeeld omleiden naar myClient.contoso.com/requestedPage. Omdat de aanvraag naar myClient.contoso.com/requestedPage een andere hostnaam heeft dan contoso.com/requestedPage, brengt de client een andere verbinding tot stand en wordt het clientcertificaat verstrekt.
    • Zie Inleiding tot autorisatie in ASP.NET Core voor meer informatie.

† Server Name Indication (SNI) is een TLS-extensie om een virtueel domein op te nemen als onderdeel van SSL-onderhandeling. Dit betekent dat de virtuele domeinnaam of een hostnaam effectief kan worden gebruikt om het netwerkeindpunt te identificeren.

Heronderhandeling

TLS-heronderhandeling is een proces waarmee de client en server de versleutelingsvereisten voor een afzonderlijke verbinding opnieuw kunnen beoordelen, inclusief het aanvragen van een clientcertificaat als dat nog niet eerder is opgegeven. TLS-heronderhandeling is een beveiligingsrisico en wordt niet aanbevolen omdat:

  • In HTTP/1.1 moet de server eerst HTTP-gegevens bufferen of verwerken die in de doorgang zijn, zoals POST-verzoeklichamen om ervoor te zorgen dat de verbinding vrij is voor de heronderhandeling. Anders kan de heronderhandeling stoppen met reageren of mislukken.
  • HTTP/2 en HTTP/3 verbieden expliciet heronderhandeling.
  • Er zijn beveiligingsrisico's verbonden aan heronderhandeling. TLS 1.3 heeft de heronderhandeling van de hele verbinding verwijderd en vervangen door een nieuwe extensie voor alleen het aanvragen van een clientcertificaat nadat de verbinding is gestart. Dit mechanisme wordt weergegeven via dezelfde API's en is nog steeds onderhevig aan de eerdere beperkingen van buffering en HTTP-protocolversies.

De implementatie en configuratie van deze functie variëren per server- en frameworkversie.

IIS

IIS beheert namens u de onderhandeling van het clientcertificaat. Een subsectie van de toepassing kan de SslRequireCert optie inschakelen om te onderhandelen over het clientcertificaat voor deze aanvragen. Zie Configuratie in de IIS-documentatie voor meer informatie.

IIS buffert automatisch eventuele aanvraagbodygegevens tot een geconfigureerde groottelimiet voordat opnieuw wordt onderhandeld. Aanvragen die de limiet overschrijden, worden geweigerd met een 413-antwoord. Deze limiet is standaard ingesteld op 48 kB en kan worden geconfigureerd door de uploadReadAheadSize in te stellen.

HttpSys

HttpSys heeft twee instellingen die de onderhandeling van het clientcertificaat regelen en beide moeten worden ingesteld. De eerste bevindt zich in netsh.exe onder http add sslcert clientcertnegotiation=enable/disable. Deze vlag geeft aan of het clientcertificaat moet worden onderhandeld aan het begin van een verbinding en moet worden ingesteld disable op voor optionele clientcertificaten. Zie de netsh-documenten voor meer informatie.

De andere instelling is ClientCertificateMethod. Wanneer ingesteld op AllowRenegotation, kan het clientcertificaat tijdens een aanvraag opnieuw worden onderhandeld.

NOTITIE De toepassing moet de hoofdtekstgegevens van de aanvraag bufferen of gebruiken voordat de heronderhandeling wordt uitgevoerd, anders reageert de aanvraag mogelijk niet meer.

Een toepassing kan eerst de ClientCertificate eigenschap controleren om te zien of het certificaat beschikbaar is. Als deze niet beschikbaar is, moet u ervoor zorgen dat de requestbody is gebruikt voordat u GetClientCertificateAsync aanroept om er een te onderhandelen. Opmerking GetClientCertificateAsync kan een null-certificaat retourneren als de client weigert er een op te geven.

NOTITIE Het gedrag van de ClientCertificate eigenschap is gewijzigd in .NET 6. Zie dit GitHub-probleem voor meer informatie.

Kestrel

Kestrel beheert onderhandeling van clientcertificaten met de ClientCertificateMode optie.

ClientCertificateMode.DelayCertificate is een nieuwe optie beschikbaar in .NET 6 of hoger. Wanneer deze optie is ingesteld, kan een app de ClientCertificate eigenschap controleren om te zien of het certificaat beschikbaar is. Als deze niet beschikbaar is, zorg ervoor dat de aanvraagbody is verbruikt voordat u GetClientCertificateAsync aanroept om er een te onderhandelen. Opmerking GetClientCertificateAsync kan een null-certificaat retourneren als de client weigert er een op te geven.

OPMERKING De toepassing moet de inhoud van het aanvraagbericht bufferen of consumeren voordat de heronderhandeling wordt uitgevoerd, anders kan GetClientCertificateAsync een InvalidOperationException: Client stream needs to be drained before renegotiation.-fout veroorzaken.

Als u via een programma de TLS-instellingen per SNI-hostnaam configureert, roep dan de UseHttps overload (.NET 6 of hoger) aan die TlsHandshakeCallbackOptions en het clientcertificaat heronderhandeling en controle via TlsHandshakeCallbackContext.AllowDelayedClientCertificateNegotation beheert.

Microsoft.AspNetCore.Authentication.Certificate bevat een implementatie die vergelijkbaar is met certificaatverificatie voor ASP.NET Core. Certificaatverificatie vindt plaats op TLS-niveau, lang voordat het ooit ASP.NET Core wordt. Nauwkeuriger is dit een verificatiehandler waarmee het certificaat wordt gevalideerd en u vervolgens een gebeurtenis krijgt waarin u dat certificaat kunt oplossen naar een ClaimsPrincipal.

Configureer uw server voor certificaatverificatie, of het nu IIS, KestrelAzure Web Apps of wat u ook gebruikt.

Scenario's voor proxy en load balancer

Certificaatverificatie is een stateful scenario dat voornamelijk wordt gebruikt waarbij een proxy of load balancer geen verkeer tussen clients en servers verwerkt. Als een proxy of load balancer wordt gebruikt, werkt certificaatauthenticatie alleen als de proxy of load balancer:

  • Verzorgt de authenticatie.
  • Geeft de gebruikersverificatiegegevens door aan de app (bijvoorbeeld in een aanvraagheader), die op de verificatiegegevens reageert.

Een alternatief voor certificaatverificatie in omgevingen waarin proxy's en load balancers worden gebruikt, is Active Directory Federated Services (ADFS) met OpenID Connect (OIDC).

Aan de slag

Verkrijg een HTTPS-certificaat, pas dit toe en configureer uw server om certificaten te vereisen.

Voeg in uw web-app een verwijzing toe naar het pakket Microsoft.AspNetCore.Authentication.Certificate . Roep vervolgens in de Startup.ConfigureServices methode services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...); uw opties aan en geef een gemachtigde op om OnCertificateValidated eventuele aanvullende validatie uit te voeren op het clientcertificaat dat is verzonden met aanvragen. Zet deze informatie om in een ClaimsPrincipal en stel deze in op de context.Principal eigenschap.

Als de verificatie mislukt, retourneert deze handler een 403 (Forbidden) antwoord in plaats van een 401 (Unauthorized), zoals u zou verwachten. De redenering is dat de verificatie moet plaatsvinden tijdens de eerste TLS-verbinding. Tegen de tijd dat het de handler bereikt, is het te laat. U kunt de verbinding niet upgraden van een anonieme verbinding naar een verbinding met een certificaat.

Voeg ook app.UseAuthentication(); toe in de Startup.Configure methode. Anders wordt het HttpContext.User niet ingesteld op ClaimsPrincipal dat is gemaakt van het certificaat. Voorbeeld:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
        .AddCertificate()
        // Adding an ICertificateValidationCache results in certificate auth caching the results.
        // The default implementation uses a memory cache.
        .AddCertificateCache();

    // All other service configuration
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();

    // All other app configuration
}

In het voorgaande voorbeeld ziet u de standaardmethode voor het toevoegen van certificaatverificatie. De handler maakt een gebruikersprincipaal met behulp van de algemene certificaateigenschappen.

Certificaatvalidatie configureren

De CertificateAuthenticationOptions handler heeft een aantal ingebouwde validaties die de minimale validaties zijn die u op een certificaat moet uitvoeren. Elk van deze instellingen is standaard ingeschakeld.

AllowedCertificateTypes = Gekoppeld, Zelfondertekend, of Alle (Gekoppeld | Zelfondertekend)

Standaardwaarde: CertificateTypes.Chained

Met deze controle wordt gecontroleerd of alleen het juiste certificaattype is toegestaan. Als de app zelfondertekende certificaten gebruikt, moet deze optie worden ingesteld op CertificateTypes.All of CertificateTypes.SelfSigned.

Certificaatgebruik valideren

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat dat door de client wordt gepresenteerd, het uitgebreide sleutelgebruik (EKU) voor clientverificatie heeft, of dat er helemaal geen EKU's zijn. Zoals de specificaties zeggen, als er geen EKU is opgegeven, worden alle EKU's als geldig beschouwd.

BevestigGeldigheidsperiode

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat binnen de geldigheidsperiode valt. Bij elke aanvraag zorgt de handler ervoor dat een certificaat dat geldig was toen het werd gepresenteerd, niet is verlopen tijdens de huidige sessie.

Intrekkingsflag

Standaardwaarde: X509RevocationFlag.ExcludeRoot

Een vlag die aangeeft welke certificaten in de keten worden gecontroleerd op intrekking.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Intrekkingsmodus

Standaardwaarde: X509RevocationMode.Online

Een vlag die aangeeft hoe intrekkingscontroles worden uitgevoerd.

Het specificeren van een onlinecontrole kan leiden tot een lange vertraging terwijl er contact wordt opgenomen met de certificaatinstantie.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Kan ik mijn app zo configureren dat alleen een certificaat op bepaalde paden is vereist?

Dit is niet mogelijk. Onthoud dat de certificaatuitwisseling wordt uitgevoerd aan het begin van het HTTPS-gesprek. Dit wordt gedaan door de server voordat de eerste aanvraag op die verbinding wordt ontvangen, zodat het niet mogelijk is om het bereik te bepalen op basis van aanvraagvelden.

Handler-gebeurtenissen

De handler heeft twee gebeurtenissen:

  • OnAuthenticationFailed: Wordt aangeroepen als er een uitzondering optreedt tijdens de verificatie en kunt u reageren.
  • OnCertificateValidated: Aangeroepen nadat het certificaat succesvol gevalideerd is en er een standaardprincipal is gemaakt. Met deze gebeurtenis kunt u uw eigen validatie uitvoeren en de principal uitbreiden of vervangen. Voorbeelden hiervan zijn:
    • Bepalen of het certificaat bekend is bij uw services.

    • Uw eigen principal samenstellen. Bekijk het volgende voorbeeld in Startup.ConfigureServices:

      services.AddAuthentication(
          CertificateAuthenticationDefaults.AuthenticationScheme)
          .AddCertificate(options =>
          {
              options.Events = new CertificateAuthenticationEvents
              {
                  OnCertificateValidated = context =>
                  {
                      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;
                  }
              };
          });
      

Als het binnenkomende certificaat niet voldoet aan uw extra validatie, geef dan een foutreden door aan context.Fail("failure reason").

Voor echte functionaliteit wilt u waarschijnlijk een service aanroepen die is geregistreerd bij afhankelijkheidsinjectie die verbinding maakt met een database of een ander type gebruikersarchief. Open uw service met behulp van de context die is doorgegeven aan uw gemachtigde. Bekijk het volgende voorbeeld in Startup.ConfigureServices:

services.AddAuthentication(
    CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                var validationService =
                    context.HttpContext.RequestServices
                        .GetRequiredService<ICertificateValidationService>();

                if (validationService.ValidateCertificate(
                    context.ClientCertificate))
                {
                    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;
            }
        };
    });

Conceptueel is de validatie van het certificaat een autorisatieprobleem. Het toevoegen van een controle op bijvoorbeeld een uitgever of vingerafdruk in een autorisatiebeleid, in plaats van binnen OnCertificateValidated, is volledig acceptabel.

Configureer uw server zo dat certificaten vereist zijn.

Kestrel

Program.cs Configureer Kestrelals volgt in:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.ConfigureKestrel(o =>
            {
                o.ConfigureHttpsDefaults(o => 
                    o.ClientCertificateMode =  ClientCertificateMode.RequireCertificate);
            });
        });
}

Opmerking

Eindpunten die zijn gemaakt door Listenaan te roepen voordatConfigureHttpsDefaults worden aangeroepen, worden de standaardwaarden niet toegepast.

IIS

Voer de volgende stappen uit in IIS Manager:

  1. Selecteer uw site op het tabblad Verbindingen .
  2. Dubbelklik op de optie SSL-instellingen in het venster Functiesweergave .
  3. Vink het selectievakje SSL vereisen aan en selecteer de optie Vereisen in de sectie Clientcertificaten.

Clientcertificaatinstellingen in IIS

Azure en aangepaste webproxy's

Raadpleeg de host- en implementatiedocumentatie voor het configureren van de middleware voor het doorsturen van certificaten.

Certificaatverificatie gebruiken in Azure Web Apps

Er is geen doorstuurconfiguratie vereist voor Azure. De doorstuurconfiguratie wordt ingesteld door de Middleware voor het doorsturen van certificaten.

Opmerking

Middleware voor het doorsturen van certificaten is vereist voor dit scenario.

Zie Een TLS/SSL-certificaat gebruiken in uw code in Azure App Service (Documentatie voor Azure) voor meer informatie.

Certificaatverificatie gebruiken in aangepaste webproxy's

De AddCertificateForwarding methode wordt gebruikt om het volgende op te geven:

  • De naam van de clientheader.
  • Hoe het certificaat moet worden geladen (met behulp van de HeaderConverter eigenschap).

In aangepaste webproxy's wordt het certificaat doorgegeven als een aangepaste aanvraagheader, bijvoorbeeld X-SSL-CERT. Als u het wilt gebruiken, configureert u het doorsturen van certificaten in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCertificateForwarding(options =>
    {
        options.CertificateHeader = "X-SSL-CERT";
        options.HeaderConverter = (headerValue) =>
        {
            X509Certificate2 clientCertificate = null;

            if(!string.IsNullOrWhiteSpace(headerValue))
            {
                byte[] bytes = StringToByteArray(headerValue);
                clientCertificate = new X509Certificate2(bytes);
            }

            return clientCertificate;
        };
    });
}

private static byte[] StringToByteArray(string hex)
{
    int NumberChars = hex.Length;
    byte[] bytes = new byte[NumberChars / 2];

    for (int i = 0; i < NumberChars; i += 2)
    {
        bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
    }

    return bytes;
}

nl-NL: Als de app omgekeerd geproxy'd wordt door NGINX met de configuratie proxy_set_header ssl-client-cert $ssl_client_escaped_cert of geïmplementeerd op Kubernetes met behulp van NGINX Ingress, wordt het clientcertificaat doorgegeven aan de app in URL-gecodeerde vorm. Als u het certificaat wilt gebruiken, decodert u het als volgt:

In Startup.ConfigureServices (Startup.cs):

services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "ssl-client-cert";
    options.HeaderConverter = (headerValue) =>
    {
        X509Certificate2 clientCertificate = null;

        if (!string.IsNullOrWhiteSpace(headerValue))
        {
            string certPem = WebUtility.UrlDecode(headerValue);
            clientCertificate = X509Certificate2.CreateFromPem(certPem);
        }

        return clientCertificate;
    };
});

De Startup.Configure methode voegt vervolgens de middleware toe. UseCertificateForwarding wordt aangeroepen vóór de aanroepen naar UseAuthentication en UseAuthorization:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    app.UseRouting();

    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Een afzonderlijke klasse kan worden gebruikt om validatielogica te implementeren. Omdat hetzelfde zelfondertekende certificaat in dit voorbeeld wordt gebruikt, moet u ervoor zorgen dat alleen uw certificaat kan worden gebruikt. Controleer of de vingerafdrukken van zowel het clientcertificaat als het servercertificaat overeenkomen, anders kan elk certificaat worden gebruikt en is voldoende om te verifiëren. Dit wordt gebruikt in de AddCertificate methode. U kunt hier ook het onderwerp of de uitgever valideren als u intermediaire of subcertificaten gebruikt.

using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace AspNetCoreCertificateAuthApi
{
    public class MyCertificateValidationService
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            // Do not hardcode passwords in production code
            // Use thumbprint or key vault
            var cert = new X509Certificate2(
                Path.Combine("sts_dev_cert.pfx"), "1234");

            if (clientCertificate.Thumbprint == cert.Thumbprint)
            {
                return true;
            }

            return false;
        }
    }
}

Een HttpClient implementeren met behulp van een certificaat en de HttpClientHandler

De HttpClientHandler kan rechtstreeks in de constructor van de HttpClient klasse worden toegevoegd. Er moet voorzichtig te werk worden gegaan bij het maken van exemplaren van de HttpClient. Het HttpClient certificaat wordt vervolgens met elke aanvraag verzonden.

private async Task<JsonDocument> GetApiDataUsingHttpClientHandler()
{
    var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(cert);
    var client = new HttpClient(handler);

    var request = new HttpRequestMessage()
    {
        RequestUri = new Uri("https://localhost:44379/api/values"),
        Method = HttpMethod.Get,
    };
    var response = await client.SendAsync(request);
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        var data = JsonDocument.Parse(responseContent);
        return data;
    }

    throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
}

Een HttpClient implementeren met behulp van een certificaat en een httpclient met de naam IHttpClientFactory

In het volgende voorbeeld wordt een clientcertificaat aan een HttpClientHandler toegevoegd met behulp van de ClientCertificates eigenschap van de handler. Deze handler kan vervolgens worden gebruikt in een benoemd exemplaar van een HttpClient met behulp van de ConfigurePrimaryHttpMessageHandler methode. Dit is ingesteld in Startup.ConfigureServices:

var clientCertificate = 
    new X509Certificate2(
      Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");

services.AddHttpClient("namedClient", c =>
{
}).ConfigurePrimaryHttpMessageHandler(() =>
{
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(clientCertificate);
    return handler;
});

De IHttpClientFactory kan vervolgens worden gebruikt om het benoemde exemplaar op te halen met behulp van de handler en het certificaat. De CreateClient methode met de naam van de client die in de Startup klasse is gedefinieerd, wordt gebruikt om het exemplaar op te halen. De HTTP-aanvraag kan naar behoefte worden verzonden met behulp van de client.

private readonly IHttpClientFactory _clientFactory;

public ApiService(IHttpClientFactory clientFactory)
{
    _clientFactory = clientFactory;
}

private async Task<JsonDocument> GetApiDataWithNamedClient()
{
    var client = _clientFactory.CreateClient("namedClient");

    var request = new HttpRequestMessage()
    {
        RequestUri = new Uri("https://localhost:44379/api/values"),
        Method = HttpMethod.Get,
    };
    var response = await client.SendAsync(request);
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        var data = JsonDocument.Parse(responseContent);
        return data;
    }

    throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
}

Als het juiste certificaat naar de server wordt verzonden, worden de gegevens geretourneerd. Als er geen certificaat of het verkeerde certificaat wordt verzonden, wordt een HTTP 403-statuscode geretourneerd.

Certificaten maken in PowerShell

Het maken van de certificaten is het moeilijkste onderdeel van het instellen van deze stroom. U kunt een basiscertificaat maken met behulp van de New-SelfSignedCertificate PowerShell-cmdlet. Wanneer u het certificaat maakt, gebruikt u een sterk wachtwoord. Het is belangrijk om de KeyUsageProperty parameter en de KeyUsage parameter toe te voegen, zoals wordt weergegeven.

Basis-CA maken

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath root_ca_dev_damienbod.crt

Opmerking

De -DnsName parameterwaarde moet overeenkomen met het implementatiedoel van de app. Bijvoorbeeld 'localhost' voor ontwikkeling.

Installeren in de vertrouwde basis

Het rootcertificaat moet op uw hostsysteem vertrouwd worden. Een basiscertificaat dat niet is gemaakt door een certificeringsinstantie, wordt niet standaard vertrouwd. Zie deze vraag voor informatie over het vertrouwen van het basiscertificaat in Windows.

Tussenliggend certificaat

Er kan nu een tussenliggend certificaat worden gemaakt op basis van het basiscertificaat. Dit is niet vereist voor alle use cases, maar mogelijk moet u veel certificaten maken of groepen certificaten activeren of uitschakelen. De TextExtension parameter is vereist om de padlengte in te stellen in de basisbeperkingen van het certificaat.

Het tussenliggende certificaat kan vervolgens worden toegevoegd aan het vertrouwde tussenliggende certificaat in het Windows-hostsysteem.

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint of the root..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "intermediate_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "intermediate_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\intermediate_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath intermediate_dev_damienbod.crt

Een onderliggend certificaat maken op basis van een tussenliggend certificaat

Er kan een kindercertificaat worden gemaakt op basis van het tussenliggende certificaat. Dit is de eindentiteit die geen kindcertificaten hoeft te maken.

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the Intermediate certificate..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Kindcertificaat maken van rootcertificaat

Een kindcertificaat kan ook rechtstreeks vanuit het basiscertificaat worden gemaakt.

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the root cert..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Voorbeeld rootcertificaat - tussenliggend certificaat - certificaat

$mypwdroot = ConvertTo-SecureString -String "1234" -Force -AsPlainText
$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

Get-ChildItem -Path cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwdroot

Export-Certificate -Cert cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 -FilePath root_ca_dev_damienbod.crt

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\0C89639E4E2998A93E423F919B36D4009A0F9991 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 -FilePath child_a_dev_damienbod.crt

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\BA9BF91ED35538A01375EFC212A2F46104B33A44 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_b_from_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_b_from_a_dev_damienbod.com" 

Get-ChildItem -Path cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_b_from_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A -FilePath child_b_from_a_dev_damienbod.crt

Wanneer u de basis-, tussen- of onderliggende certificaten gebruikt, kunnen de certificaten naar behoefte worden gevalideerd met behulp van de vingerafdruk of PublicKey.

using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace AspNetCoreCertificateAuthApi
{
    public class MyCertificateValidationService 
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            return CheckIfThumbprintIsValid(clientCertificate);
        }

        private bool CheckIfThumbprintIsValid(X509Certificate2 clientCertificate)
        {
            var listOfValidThumbprints = new List<string>
            {
                "141594A0AE38CBBECED7AF680F7945CD51D8F28A",
                "0C89639E4E2998A93E423F919B36D4009A0F9991",
                "BA9BF91ED35538A01375EFC212A2F46104B33A44"
            };

            if (listOfValidThumbprints.Contains(clientCertificate.Thumbprint))
            {
                return true;
            }

            return false;
        }
    }
}

Opslaan in cache van certificaatvalidatie

.NET 5 of nieuwere versies ondersteunen de mogelijkheid om caching van validatieresultaten in te schakelen. De caching verbetert de prestaties van certificaatverificatie aanzienlijk, omdat validatie een dure bewerking is.

Certificaatverificatie schakelt standaard caching uit. Om caching in te schakelen, roept u AddCertificateCache aan in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate()
            .AddCertificateCache(options =>
            {
                options.CacheSize = 1024;
                options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
            });
}

De standaard caching-implementatie slaat resultaten op in het geheugen. U kunt uw eigen cache opgeven door ICertificateValidationCache te implementeren en het te registreren via afhankelijkheidsinjectie. Bijvoorbeeld: services.AddSingleton<ICertificateValidationCache, YourCache>().

Optionele clientcertificaten

Deze sectie bevat informatie voor apps die een subset van de app met een certificaat moeten beveiligen. Voor een Razor pagina of controller in de app zijn bijvoorbeeld clientcertificaten vereist. Dit brengt uitdagingen met zich mee als clientcertificaten:

  • Zijn een TLS-functie, geen HTTP-functie.
  • Wordt onderhandeld per verbinding en meestal aan het begin van de verbinding voordat er HTTP-gegevens beschikbaar zijn.

Er zijn twee benaderingen voor het implementeren van optionele clientcertificaten:

  1. Gebruik afzonderlijke hostnamen (SNI) en doorsturen. Hoewel er meer werk nodig is om te configureren, wordt dit aanbevolen omdat dit werkt in de meeste omgevingen en protocollen.
  2. Heronderhandeling tijdens een HTTP-aanvraag. Dit heeft verschillende beperkingen en wordt niet aanbevolen.

Afzonderlijke hosts (SNI)

Aan het begin van de verbinding is alleen de SNI-† (Server Name Indication) bekend. Clientcertificaten kunnen per hostnaam worden geconfigureerd, zodat de ene host deze vereist en een andere niet.

.NET 5 of hoger voegt handigere ondersteuning toe voor het omleiden om optionele clientcertificaten te verkrijgen. Zie het voorbeeld van optionele certificaten voor meer informatie.

  • Voor aanvragen voor de web-app waarvoor een clientcertificaat vereist is maar niet aanwezig is:
    • Omleiden naar dezelfde pagina met behulp van het met het clientcertificaat beveiligde subdomein.
    • Bijvoorbeeld omleiden naar myClient.contoso.com/requestedPage. Omdat de aanvraag naar myClient.contoso.com/requestedPage een andere hostnaam heeft dan contoso.com/requestedPage, brengt de client een andere verbinding tot stand en wordt het clientcertificaat verstrekt.
    • Zie Inleiding tot autorisatie in ASP.NET Core voor meer informatie.

† Server Name Indication (SNI) is een TLS-extensie om een virtueel domein op te nemen als onderdeel van SSL-onderhandeling. Dit betekent dat de virtuele domeinnaam of een hostnaam effectief kan worden gebruikt om het netwerkeindpunt te identificeren.

Heronderhandeling

TLS-heronderhandeling is een proces waarmee de client en server de versleutelingsvereisten voor een afzonderlijke verbinding opnieuw kunnen beoordelen, inclusief het aanvragen van een clientcertificaat als dat nog niet eerder is opgegeven. TLS-heronderhandeling is een beveiligingsrisico en wordt niet aanbevolen omdat:

  • In HTTP/1.1 moet de server eerst HTTP-gegevens bufferen of verwerken die in de doorgang zijn, zoals POST-verzoeklichamen om ervoor te zorgen dat de verbinding vrij is voor de heronderhandeling. Anders kan de heronderhandeling stoppen met reageren of mislukken.
  • HTTP/2 en HTTP/3 verbieden expliciet heronderhandeling.
  • Er zijn beveiligingsrisico's verbonden aan heronderhandeling. TLS 1.3 heeft de heronderhandeling van de hele verbinding verwijderd en vervangen door een nieuwe extensie voor alleen het aanvragen van een clientcertificaat nadat de verbinding is gestart. Dit mechanisme wordt weergegeven via dezelfde API's en is nog steeds onderhevig aan de eerdere beperkingen van buffering en HTTP-protocolversies.

De implementatie en configuratie van deze functie variëren per server- en frameworkversie.

IIS

IIS beheert namens u de onderhandeling van het clientcertificaat. Een subsectie van de toepassing kan de SslRequireCert optie inschakelen om te onderhandelen over het clientcertificaat voor deze aanvragen. Zie Configuratie in de IIS-documentatie voor meer informatie.

IIS buffert automatisch eventuele aanvraagbodygegevens tot een geconfigureerde groottelimiet voordat opnieuw wordt onderhandeld. Aanvragen die de limiet overschrijden, worden geweigerd met een 413-antwoord. Deze limiet is standaard ingesteld op 48 kB en kan worden geconfigureerd door de uploadReadAheadSize in te stellen.

HttpSys

HttpSys heeft twee instellingen die de onderhandeling van het clientcertificaat regelen en beide moeten worden ingesteld. De eerste bevindt zich in netsh.exe onder http add sslcert clientcertnegotiation=enable/disable. Deze vlag geeft aan of het clientcertificaat moet worden onderhandeld aan het begin van een verbinding en moet worden ingesteld disable op voor optionele clientcertificaten. Zie de netsh-documenten voor meer informatie.

De andere instelling is ClientCertificateMethod. Wanneer ingesteld op AllowRenegotation, kan het clientcertificaat tijdens een aanvraag opnieuw worden onderhandeld.

NOTITIE De toepassing moet de hoofdtekstgegevens van de aanvraag bufferen of gebruiken voordat de heronderhandeling wordt uitgevoerd, anders reageert de aanvraag mogelijk niet meer.

Er is een bekend probleem waarbij het inschakelen AllowRenegotation ervoor kan zorgen dat de heronderhandeling synchroon plaatsvindt bij het openen van de ClientCertificate eigenschap. Roep de GetClientCertificateAsync methode aan om dit te voorkomen. Dit is opgelost in .NET 6. Zie dit GitHub-probleem voor meer informatie. Opmerking GetClientCertificateAsync kan een null-certificaat retourneren als de client weigert er een op te geven.

Kestrel

Kestrel beheert onderhandeling van clientcertificaten met de ClientCertificateMode optie.

Voor .NET 5 of eerder Kestrel biedt geen ondersteuning voor opnieuw onderhandelen na het starten van een verbinding om een clientcertificaat te verkrijgen. Deze functie is toegevoegd in .NET 6.

Microsoft.AspNetCore.Authentication.Certificate bevat een implementatie die vergelijkbaar is met certificaatverificatie voor ASP.NET Core. Certificaatverificatie vindt plaats op TLS-niveau, lang voordat het ooit ASP.NET Core wordt. Nauwkeuriger is dit een verificatiehandler waarmee het certificaat wordt gevalideerd en u vervolgens een gebeurtenis krijgt waarin u dat certificaat kunt oplossen naar een ClaimsPrincipal.

Configureer uw server voor certificaatverificatie, of het nu IIS, KestrelAzure Web Apps of wat u ook gebruikt.

Scenario's voor proxy en load balancer

Certificaatverificatie is een stateful scenario dat voornamelijk wordt gebruikt waarbij een proxy of load balancer geen verkeer tussen clients en servers verwerkt. Als een proxy of load balancer wordt gebruikt, werkt certificaatauthenticatie alleen als de proxy of load balancer:

  • Verzorgt de authenticatie.
  • Geeft de gebruikersverificatiegegevens door aan de app (bijvoorbeeld in een aanvraagheader), die op de verificatiegegevens reageert.

Een alternatief voor certificaatverificatie in omgevingen waarin proxy's en load balancers worden gebruikt, is Active Directory Federated Services (ADFS) met OpenID Connect (OIDC).

Aan de slag

Verkrijg een HTTPS-certificaat, pas dit toe en configureer uw server om certificaten te vereisen.

Voeg in uw web-app een verwijzing toe naar het pakket Microsoft.AspNetCore.Authentication.Certificate . Roep vervolgens in de Startup.ConfigureServices methode services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...); uw opties aan en geef een gemachtigde op om OnCertificateValidated eventuele aanvullende validatie uit te voeren op het clientcertificaat dat is verzonden met aanvragen. Zet deze informatie om in een ClaimsPrincipal en stel deze in op de context.Principal eigenschap.

Als de verificatie mislukt, retourneert deze handler een 403 (Forbidden) antwoord in plaats van een 401 (Unauthorized), zoals u zou verwachten. De redenering is dat de verificatie moet plaatsvinden tijdens de eerste TLS-verbinding. Tegen de tijd dat het de handler bereikt, is het te laat. U kunt de verbinding niet upgraden van een anonieme verbinding naar een verbinding met een certificaat.

Voeg ook app.UseAuthentication(); toe in de Startup.Configure methode. Anders wordt het HttpContext.User niet ingesteld op ClaimsPrincipal dat is gemaakt van het certificaat. Voorbeeld:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
        .AddCertificate();

    // All other service configuration
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseAuthentication();

    // All other app configuration
}

In het voorgaande voorbeeld ziet u de standaardmethode voor het toevoegen van certificaatverificatie. De handler maakt een gebruikersprincipaal met behulp van de algemene certificaateigenschappen.

Certificaatvalidatie configureren

De CertificateAuthenticationOptions handler heeft een aantal ingebouwde validaties die de minimale validaties zijn die u op een certificaat moet uitvoeren. Elk van deze instellingen is standaard ingeschakeld.

AllowedCertificateTypes = Gekoppeld, Zelfondertekend, of Alle (Gekoppeld | Zelfondertekend)

Standaardwaarde: CertificateTypes.Chained

Met deze controle wordt gecontroleerd of alleen het juiste certificaattype is toegestaan. Als de app zelfondertekende certificaten gebruikt, moet deze optie worden ingesteld op CertificateTypes.All of CertificateTypes.SelfSigned.

Certificaatgebruik valideren

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat dat door de client wordt gepresenteerd, het uitgebreide sleutelgebruik (EKU) voor clientverificatie heeft, of dat er helemaal geen EKU's zijn. Zoals de specificaties zeggen, als er geen EKU is opgegeven, worden alle EKU's als geldig beschouwd.

BevestigGeldigheidsperiode

Standaardwaarde: true

Met deze controle wordt gecontroleerd of het certificaat binnen de geldigheidsperiode valt. Bij elke aanvraag zorgt de handler ervoor dat een certificaat dat geldig was toen het werd gepresenteerd, niet is verlopen tijdens de huidige sessie.

Intrekkingsflag

Standaardwaarde: X509RevocationFlag.ExcludeRoot

Een vlag die aangeeft welke certificaten in de keten worden gecontroleerd op intrekking.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Intrekkingsmodus

Standaardwaarde: X509RevocationMode.Online

Een vlag die aangeeft hoe intrekkingscontroles worden uitgevoerd.

Het specificeren van een onlinecontrole kan leiden tot een lange vertraging terwijl er contact wordt opgenomen met de certificaatinstantie.

Intrekkingscontroles worden alleen uitgevoerd wanneer het certificaat is gekoppeld aan een basiscertificaat.

Kan ik mijn app zo configureren dat alleen een certificaat op bepaalde paden is vereist?

Dit is niet mogelijk. Onthoud dat de certificaatuitwisseling wordt uitgevoerd aan het begin van het HTTPS-gesprek. Dit wordt gedaan door de server voordat de eerste aanvraag op die verbinding wordt ontvangen, zodat het niet mogelijk is om het bereik te bepalen op basis van aanvraagvelden.

Handler-gebeurtenissen

De handler heeft twee gebeurtenissen:

  • OnAuthenticationFailed: Wordt aangeroepen als er een uitzondering optreedt tijdens de verificatie en kunt u reageren.
  • OnCertificateValidated: Aangeroepen nadat het certificaat succesvol gevalideerd is en er een standaardprincipal is gemaakt. Met deze gebeurtenis kunt u uw eigen validatie uitvoeren en de principal uitbreiden of vervangen. Voorbeelden hiervan zijn:
    • Bepalen of het certificaat bekend is bij uw services.

    • Uw eigen principal samenstellen. Bekijk het volgende voorbeeld in Startup.ConfigureServices:

      services.AddAuthentication(
          CertificateAuthenticationDefaults.AuthenticationScheme)
          .AddCertificate(options =>
          {
              options.Events = new CertificateAuthenticationEvents
              {
                  OnCertificateValidated = context =>
                  {
                      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;
                  }
              };
          });
      

Als het binnenkomende certificaat niet voldoet aan uw extra validatie, geef dan een foutreden door aan context.Fail("failure reason").

Voor echte functionaliteit wilt u waarschijnlijk een service aanroepen die is geregistreerd bij afhankelijkheidsinjectie die verbinding maakt met een database of een ander type gebruikersarchief. Open uw service met behulp van de context die is doorgegeven aan uw gemachtigde. Bekijk het volgende voorbeeld in Startup.ConfigureServices:

services.AddAuthentication(
    CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                var validationService =
                    context.HttpContext.RequestServices
                        .GetRequiredService<ICertificateValidationService>();

                if (validationService.ValidateCertificate(
                    context.ClientCertificate))
                {
                    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;
            }
        };
    });

Conceptueel is de validatie van het certificaat een autorisatieprobleem. Het toevoegen van een controle op bijvoorbeeld een uitgever of vingerafdruk in een autorisatiebeleid, in plaats van binnen OnCertificateValidated, is volledig acceptabel.

Configureer uw server zo dat certificaten vereist zijn.

Kestrel

Program.cs Configureer Kestrelals volgt in:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.ConfigureKestrel(o =>
            {
                o.ConfigureHttpsDefaults(o => 
                    o.ClientCertificateMode =  ClientCertificateMode.RequireCertificate);
            });
        });
}

Opmerking

Eindpunten die zijn gemaakt door Listenaan te roepen voordatConfigureHttpsDefaults worden aangeroepen, worden de standaardwaarden niet toegepast.

IIS

Voer de volgende stappen uit in IIS Manager:

  1. Selecteer uw site op het tabblad Verbindingen .
  2. Dubbelklik op de optie SSL-instellingen in het venster Functiesweergave .
  3. Vink het selectievakje SSL vereisen aan en selecteer de optie Vereisen in de sectie Clientcertificaten.

Clientcertificaatinstellingen in IIS

Azure en aangepaste webproxy's

Raadpleeg de host- en implementatiedocumentatie voor het configureren van de middleware voor het doorsturen van certificaten.

Certificaatverificatie gebruiken in Azure Web Apps

Er is geen doorstuurconfiguratie vereist voor Azure. De doorstuurconfiguratie wordt ingesteld door de Middleware voor het doorsturen van certificaten.

Opmerking

Middleware voor het doorsturen van certificaten is vereist voor dit scenario.

Zie Een TLS/SSL-certificaat gebruiken in uw code in Azure App Service (Documentatie voor Azure) voor meer informatie.

Certificaatverificatie gebruiken in aangepaste webproxy's

De AddCertificateForwarding methode wordt gebruikt om het volgende op te geven:

  • De naam van de clientheader.
  • Hoe het certificaat moet worden geladen (met behulp van de HeaderConverter eigenschap).

In aangepaste webproxy's wordt het certificaat doorgegeven als een aangepaste aanvraagheader, bijvoorbeeld X-SSL-CERT. Als u het wilt gebruiken, configureert u het doorsturen van certificaten in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCertificateForwarding(options =>
    {
        options.CertificateHeader = "X-SSL-CERT";
        options.HeaderConverter = (headerValue) =>
        {
            X509Certificate2 clientCertificate = null;

            if(!string.IsNullOrWhiteSpace(headerValue))
            {
                byte[] bytes = StringToByteArray(headerValue);
                clientCertificate = new X509Certificate2(bytes);
            }

            return clientCertificate;
        };
    });
}

private static byte[] StringToByteArray(string hex)
{
    int NumberChars = hex.Length;
    byte[] bytes = new byte[NumberChars / 2];

    for (int i = 0; i < NumberChars; i += 2)
    {
        bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
    }

    return bytes;
}

nl-NL: Als de app omgekeerd geproxy'd wordt door NGINX met de configuratie proxy_set_header ssl-client-cert $ssl_client_escaped_cert of geïmplementeerd op Kubernetes met behulp van NGINX Ingress, wordt het clientcertificaat doorgegeven aan de app in URL-gecodeerde vorm. Als u het certificaat wilt gebruiken, decodert u het als volgt:

Voeg de naamruimte voor System.Net toe aan het begin van Startup.cs:

using System.Net;

In Startup.ConfigureServices:

services.AddCertificateForwarding(options =>
{
    options.CertificateHeader = "ssl-client-cert";
    options.HeaderConverter = (headerValue) =>
    {
        X509Certificate2 clientCertificate = null;

        if (!string.IsNullOrWhiteSpace(headerValue))
        {
            var bytes = UrlEncodedPemToByteArray(headerValue);
            clientCertificate = new X509Certificate2(bytes);
        }

        return clientCertificate;
    };
});

Voeg de UrlEncodedPemToByteArray methode toe:

private static byte[] UrlEncodedPemToByteArray(string urlEncodedBase64Pem)
{
    var base64Pem = WebUtility.UrlDecode(urlEncodedBase64Pem);
    var base64Cert = base64Pem
        .Replace("-----BEGIN CERTIFICATE-----", string.Empty)
        .Replace("-----END CERTIFICATE-----", string.Empty)
        .Trim();

    return Convert.FromBase64String(base64Cert);
}

De Startup.Configure methode voegt vervolgens de middleware toe. UseCertificateForwarding wordt aangeroepen vóór de aanroepen naar UseAuthentication en UseAuthorization:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    app.UseRouting();

    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Een afzonderlijke klasse kan worden gebruikt om validatielogica te implementeren. Omdat hetzelfde zelfondertekende certificaat in dit voorbeeld wordt gebruikt, moet u ervoor zorgen dat alleen uw certificaat kan worden gebruikt. Controleer of de vingerafdrukken van zowel het clientcertificaat als het servercertificaat overeenkomen, anders kan elk certificaat worden gebruikt en is voldoende om te verifiëren. Dit wordt gebruikt in de AddCertificate methode. U kunt hier ook het onderwerp of de uitgever valideren als u intermediaire of subcertificaten gebruikt.

using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace AspNetCoreCertificateAuthApi
{
    public class MyCertificateValidationService
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            // Do not hardcode passwords in production code
            // Use thumbprint or key vault
            var cert = new X509Certificate2(
                Path.Combine("sts_dev_cert.pfx"), "1234");

            if (clientCertificate.Thumbprint == cert.Thumbprint)
            {
                return true;
            }

            return false;
        }
    }
}

Een HttpClient implementeren met behulp van een certificaat en de HttpClientHandler

De HttpClientHandler kan rechtstreeks in de constructor van de HttpClient klasse worden toegevoegd. Er moet voorzichtig te werk worden gegaan bij het maken van exemplaren van de HttpClient. Het HttpClient certificaat wordt vervolgens met elke aanvraag verzonden.

private async Task<JsonDocument> GetApiDataUsingHttpClientHandler()
{
    var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(cert);
    var client = new HttpClient(handler);

    var request = new HttpRequestMessage()
    {
        RequestUri = new Uri("https://localhost:44379/api/values"),
        Method = HttpMethod.Get,
    };
    var response = await client.SendAsync(request);
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        var data = JsonDocument.Parse(responseContent);
        return data;
    }

    throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
}

Een HttpClient implementeren met behulp van een certificaat en een httpclient met de naam IHttpClientFactory

In het volgende voorbeeld wordt een clientcertificaat aan een HttpClientHandler toegevoegd met behulp van de ClientCertificates eigenschap van de handler. Deze handler kan vervolgens worden gebruikt in een benoemd exemplaar van een HttpClient met behulp van de ConfigurePrimaryHttpMessageHandler methode. Dit is ingesteld in Startup.ConfigureServices:

var clientCertificate = 
    new X509Certificate2(
      Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");

services.AddHttpClient("namedClient", c =>
{
}).ConfigurePrimaryHttpMessageHandler(() =>
{
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(clientCertificate);
    return handler;
});

De IHttpClientFactory kan vervolgens worden gebruikt om het benoemde exemplaar op te halen met behulp van de handler en het certificaat. De CreateClient methode met de naam van de client die in de Startup klasse is gedefinieerd, wordt gebruikt om het exemplaar op te halen. De HTTP-aanvraag kan naar behoefte worden verzonden met behulp van de client.

private readonly IHttpClientFactory _clientFactory;

public ApiService(IHttpClientFactory clientFactory)
{
    _clientFactory = clientFactory;
}

private async Task<JsonDocument> GetApiDataWithNamedClient()
{
    var client = _clientFactory.CreateClient("namedClient");

    var request = new HttpRequestMessage()
    {
        RequestUri = new Uri("https://localhost:44379/api/values"),
        Method = HttpMethod.Get,
    };
    var response = await client.SendAsync(request);
    if (response.IsSuccessStatusCode)
    {
        var responseContent = await response.Content.ReadAsStringAsync();
        var data = JsonDocument.Parse(responseContent);
        return data;
    }

    throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
}

Als het juiste certificaat naar de server wordt verzonden, worden de gegevens geretourneerd. Als er geen certificaat of het verkeerde certificaat wordt verzonden, wordt een HTTP 403-statuscode geretourneerd.

Certificaten maken in PowerShell

Het maken van de certificaten is het moeilijkste onderdeel van het instellen van deze stroom. U kunt een basiscertificaat maken met behulp van de New-SelfSignedCertificate PowerShell-cmdlet. Wanneer u het certificaat maakt, gebruikt u een sterk wachtwoord. Het is belangrijk om de KeyUsageProperty parameter en de KeyUsage parameter toe te voegen, zoals wordt weergegeven.

Basis-CA maken

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath root_ca_dev_damienbod.crt

Opmerking

De -DnsName parameterwaarde moet overeenkomen met het implementatiedoel van de app. Bijvoorbeeld 'localhost' voor ontwikkeling.

Installeren in de vertrouwde basis

Het rootcertificaat moet op uw hostsysteem vertrouwd worden. Een basiscertificaat dat niet is gemaakt door een certificeringsinstantie, wordt niet standaard vertrouwd. Zie deze vraag voor informatie over het vertrouwen van het basiscertificaat in Windows.

Tussenliggend certificaat

Er kan nu een tussenliggend certificaat worden gemaakt op basis van het basiscertificaat. Dit is niet vereist voor alle use cases, maar mogelijk moet u veel certificaten maken of groepen certificaten activeren of uitschakelen. De TextExtension parameter is vereist om de padlengte in te stellen in de basisbeperkingen van het certificaat.

Het tussenliggende certificaat kan vervolgens worden toegevoegd aan het vertrouwde tussenliggende certificaat in het Windows-hostsysteem.

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint of the root..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "intermediate_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "intermediate_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\intermediate_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath intermediate_dev_damienbod.crt

Een onderliggend certificaat maken op basis van een tussenliggend certificaat

Er kan een kindercertificaat worden gemaakt op basis van het tussenliggende certificaat. Dit is de eindentiteit die geen kindcertificaten hoeft te maken.

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the Intermediate certificate..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Kindcertificaat maken van rootcertificaat

Een kindcertificaat kan ook rechtstreeks vanuit het basiscertificaat worden gemaakt.

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\"The thumbprint from the root cert..." )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Get-ChildItem -Path cert:\localMachine\my\"The thumbprint..." | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\"The thumbprint..." -FilePath child_a_dev_damienbod.crt

Voorbeeld rootcertificaat - tussenliggend certificaat - certificaat

$mypwdroot = ConvertTo-SecureString -String "1234" -Force -AsPlainText
$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

New-SelfSignedCertificate -DnsName "root_ca_dev_damienbod.com", "root_ca_dev_damienbod.com" -CertStoreLocation "cert:\LocalMachine\My" -NotAfter (Get-Date).AddYears(20) -FriendlyName "root_ca_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature

Get-ChildItem -Path cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 | Export-PfxCertificate -FilePath C:\git\root_ca_dev_damienbod.pfx -Password $mypwdroot

Export-Certificate -Cert cert:\localMachine\my\0C89639E4E2998A93E423F919B36D4009A0F9991 -FilePath root_ca_dev_damienbod.crt

$rootcert = ( Get-ChildItem -Path cert:\LocalMachine\My\0C89639E4E2998A93E423F919B36D4009A0F9991 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_a_dev_damienbod.com" -Signer $rootcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_a_dev_damienbod.com" -KeyUsageProperty All -KeyUsage CertSign, CRLSign, DigitalSignature -TextExtension @("2.5.29.19={text}CA=1&pathlength=1")

Get-ChildItem -Path cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\BA9BF91ED35538A01375EFC212A2F46104B33A44 -FilePath child_a_dev_damienbod.crt

$parentcert = ( Get-ChildItem -Path cert:\LocalMachine\My\BA9BF91ED35538A01375EFC212A2F46104B33A44 )

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "child_b_from_a_dev_damienbod.com" -Signer $parentcert -NotAfter (Get-Date).AddYears(20) -FriendlyName "child_b_from_a_dev_damienbod.com" 

Get-ChildItem -Path cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A | Export-PfxCertificate -FilePath C:\git\AspNetCoreCertificateAuth\Certs\child_b_from_a_dev_damienbod.pfx -Password $mypwd

Export-Certificate -Cert cert:\localMachine\my\141594A0AE38CBBECED7AF680F7945CD51D8F28A -FilePath child_b_from_a_dev_damienbod.crt

Wanneer u de basis-, tussen- of onderliggende certificaten gebruikt, kunnen de certificaten naar behoefte worden gevalideerd met behulp van de vingerafdruk of PublicKey.

using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace AspNetCoreCertificateAuthApi
{
    public class MyCertificateValidationService 
    {
        public bool ValidateCertificate(X509Certificate2 clientCertificate)
        {
            return CheckIfThumbprintIsValid(clientCertificate);
        }

        private bool CheckIfThumbprintIsValid(X509Certificate2 clientCertificate)
        {
            var listOfValidThumbprints = new List<string>
            {
                "141594A0AE38CBBECED7AF680F7945CD51D8F28A",
                "0C89639E4E2998A93E423F919B36D4009A0F9991",
                "BA9BF91ED35538A01375EFC212A2F46104B33A44"
            };

            if (listOfValidThumbprints.Contains(clientCertificate.Thumbprint))
            {
                return true;
            }

            return false;
        }
    }
}

Optionele clientcertificaten

Deze sectie bevat informatie voor apps die een subset van de app met een certificaat moeten beveiligen. Voor een Razor pagina of controller in de app zijn bijvoorbeeld clientcertificaten vereist. Dit brengt uitdagingen met zich mee als clientcertificaten:

  • Zijn een TLS-functie, geen HTTP-functie.
  • Wordt onderhandeld per verbinding en meestal aan het begin van de verbinding voordat er HTTP-gegevens beschikbaar zijn.

Er zijn twee benaderingen voor het implementeren van optionele clientcertificaten:

  1. Gebruik afzonderlijke hostnamen (SNI) en doorsturen. Hoewel er meer werk nodig is om te configureren, wordt dit aanbevolen omdat dit werkt in de meeste omgevingen en protocollen.
  2. Heronderhandeling tijdens een HTTP-aanvraag. Dit heeft verschillende beperkingen en wordt niet aanbevolen.

Afzonderlijke hosts (SNI)

Aan het begin van de verbinding is alleen de SNI-† (Server Name Indication) bekend. Clientcertificaten kunnen per hostnaam worden geconfigureerd, zodat de ene host deze vereist en een andere niet.

.NET 5 of hoger voegt handigere ondersteuning toe voor het omleiden om optionele clientcertificaten te verkrijgen. Zie het voorbeeld van optionele certificaten voor meer informatie.

  • Voor aanvragen voor de web-app waarvoor een clientcertificaat vereist is maar niet aanwezig is:
    • Omleiden naar dezelfde pagina met behulp van het met het clientcertificaat beveiligde subdomein.
    • Bijvoorbeeld omleiden naar myClient.contoso.com/requestedPage. Omdat de aanvraag naar myClient.contoso.com/requestedPage een andere hostnaam heeft dan contoso.com/requestedPage, brengt de client een andere verbinding tot stand en wordt het clientcertificaat verstrekt.
    • Zie Inleiding tot autorisatie in ASP.NET Core voor meer informatie.

† Server Name Indication (SNI) is een TLS-extensie om een virtueel domein op te nemen als onderdeel van SSL-onderhandeling. Dit betekent dat de virtuele domeinnaam of een hostnaam effectief kan worden gebruikt om het netwerkeindpunt te identificeren.

Heronderhandeling

TLS-heronderhandeling is een proces waarmee de client en server de versleutelingsvereisten voor een afzonderlijke verbinding opnieuw kunnen beoordelen, inclusief het aanvragen van een clientcertificaat als dat nog niet eerder is opgegeven. TLS-heronderhandeling is een beveiligingsrisico en wordt niet aanbevolen omdat:

  • In HTTP/1.1 moet de server eerst HTTP-gegevens bufferen of verwerken die in de doorgang zijn, zoals POST-verzoeklichamen om ervoor te zorgen dat de verbinding vrij is voor de heronderhandeling. Anders kan de heronderhandeling stoppen met reageren of mislukken.
  • HTTP/2 en HTTP/3 verbieden expliciet heronderhandeling.
  • Er zijn beveiligingsrisico's verbonden aan heronderhandeling. TLS 1.3 heeft de heronderhandeling van de hele verbinding verwijderd en vervangen door een nieuwe extensie voor alleen het aanvragen van een clientcertificaat nadat de verbinding is gestart. Dit mechanisme wordt weergegeven via dezelfde API's en is nog steeds onderhevig aan de eerdere beperkingen van buffering en HTTP-protocolversies.

De implementatie en configuratie van deze functie variëren per server- en frameworkversie.

IIS

IIS beheert namens u de onderhandeling van het clientcertificaat. Een subsectie van de toepassing kan de SslRequireCert optie inschakelen om te onderhandelen over het clientcertificaat voor deze aanvragen. Zie Configuratie in de IIS-documentatie voor meer informatie.

IIS buffert automatisch eventuele aanvraagbodygegevens tot een geconfigureerde groottelimiet voordat opnieuw wordt onderhandeld. Aanvragen die de limiet overschrijden, worden geweigerd met een 413-antwoord. Deze limiet is standaard ingesteld op 48 kB en kan worden geconfigureerd door de uploadReadAheadSize in te stellen.

HttpSys

HttpSys heeft twee instellingen die de onderhandeling van het clientcertificaat regelen en beide moeten worden ingesteld. De eerste bevindt zich in netsh.exe onder http add sslcert clientcertnegotiation=enable/disable. Deze vlag geeft aan of het clientcertificaat moet worden onderhandeld aan het begin van een verbinding en moet worden ingesteld disable op voor optionele clientcertificaten. Zie de netsh-documenten voor meer informatie.

De andere instelling is ClientCertificateMethod. Wanneer ingesteld op AllowRenegotation, kan het clientcertificaat tijdens een aanvraag opnieuw worden onderhandeld.

NOTITIE De toepassing moet de hoofdtekstgegevens van de aanvraag bufferen of gebruiken voordat de heronderhandeling wordt uitgevoerd, anders reageert de aanvraag mogelijk niet meer.

Er is een bekend probleem waarbij het inschakelen AllowRenegotation ervoor kan zorgen dat de heronderhandeling synchroon plaatsvindt bij het openen van de ClientCertificate eigenschap. Roep de GetClientCertificateAsync methode aan om dit te voorkomen. Dit is opgelost in .NET 6. Zie dit GitHub-probleem voor meer informatie. Opmerking GetClientCertificateAsync kan een null-certificaat retourneren als de client weigert er een op te geven.

Kestrel

Kestrel beheert onderhandeling van clientcertificaten met de ClientCertificateMode optie.

Voor .NET 5 of eerder Kestrel biedt geen ondersteuning voor opnieuw onderhandelen na het starten van een verbinding om een clientcertificaat te verkrijgen. Deze functie is toegevoegd in .NET 6.

Laat vragen, opmerkingen en andere feedback over optionele clientcertificaten in dit GitHub-discussieprobleem achter.