Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
              Microsoft.AspNetCore.Authentication.Certificate innehåller en implementering som liknar certifikatautentisering för ASP.NET Core. Certifikatautentisering sker på TLS-nivå, långt innan det någonsin kommer till ASP.NET Core. Mer exakt är detta en autentiseringshanterare som validerar certifikatet och sedan ger dig en händelse där du kan matcha certifikatet till en ClaimsPrincipal.
Du måstekonfigurera servern för certifikatautentisering, oavsett om det är IIS, KestrelAzure Web Apps eller något annat du använder.
Proxy- och lastbalanseringsscenarier
Certifikatautentisering är ett tillståndskänsligt scenario som främst används där en proxy eller lastbalanserare inte hanterar trafik mellan klienter och servrar. Om en proxy eller lastbalanserare används fungerar certifikatautentisering endast om proxyn eller lastbalanseraren:
- Hanterar autentiseringen.
- Skickar användarautentiseringsinformationen till appen (till exempel i ett begärandehuvud), som agerar på autentiseringsinformationen.
Ett alternativ till certifikatautentisering i miljöer där proxyservrar och lastbalanserare används är Active Directory Federated Services (ADFS) med OpenID Connect (OIDC).
Kom igång
Skaffa ett HTTPS-certifikat, tillämpa det och konfigurera servern så att den kräver certifikat.
I webbappen:
- Lägg till en referens till NuGet-paketet Microsoft.AspNetCore.Authentication.Certificate .
- I Program.csanropar dubuilder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...);. Ange ett ombud förOnCertificateValidatedatt utföra eventuell kompletterande validering på klientcertifikatet som skickas med begäranden. Omvandla den informationen till enClaimsPrincipaloch ställ in den påcontext.Principalegenskapen.
Om autentiseringen misslyckas returnerar den här hanteraren ett 403 (Forbidden) svar i stället för ett 401 (Unauthorized), som du kan förvänta dig. Resonemanget är att autentiseringen ska ske under den första TLS-anslutningen. När den når hanteraren är det för sent. Det går inte att uppgradera anslutningen från en anonym anslutning till en med ett certifikat.
              UseAuthentication krävs för att ställa in HttpContext.User på en ClaimsPrincipal som skapats från certifikatet. Till exempel:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate();
var app = builder.Build();
app.UseAuthentication();
app.MapGet("/", () => "Hello World!");
app.Run();
Föregående exempel visar standardsättet för att lägga till certifikatautentisering. Hanteraren konstruerar ett användarhuvudnamn med hjälp av de vanliga certifikategenskaperna.
Konfigurera certifikatverifiering
              CertificateAuthenticationOptions Hanteraren har några inbyggda valideringar som är de minsta valideringar som du bör utföra på ett certifikat. Var och en av dessa inställningar är aktiverad som standard.
AllowedCertificateTypes = Kedjad, Självsignerad eller Alla (Kedjad | Självsignerad)
Standardvärde: CertificateTypes.Chained
Den här kontrollen verifierar att endast lämplig certifikattyp tillåts. Om appen använder självsignerade certifikat måste det här alternativet anges till CertificateTypes.All eller CertificateTypes.SelfSigned.
ChainTrustValidationMode (läge för kedjevalidering av förtroende)
Standardvärde: X509ChainTrustMode.System
Certifikatet som presenteras av klienten måste länkas till ett betrott rotcertifikat. Den här kontrollen styr vilket förtroendearkiv som innehåller dessa rotcertifikat.
Som standard använder hanteraren systemförtroendearkivet. Om det presenterade klientcertifikatet behöver länkas till ett rotcertifikat som inte visas i systemförtroendearkivet kan det här alternativet anges till X509ChainTrustMode.CustomRootTrust för att hanteraren ska kunna använda CustomTrustStore.
CustomTrustStore
Standardvärde: Tom X509Certificate2Collection
Om hanterarens ChainTrustValidationMode egenskap är inställd på X509ChainTrustMode.CustomRootTrustinnehåller detta X509Certificate2Collection varje certifikat som ska användas för att verifiera klientcertifikatet upp till en betrodd rot, inklusive den betrodda roten.
När klienten visar ett certifikat som ingår i en certifikatkedja CustomTrustStore på flera nivåer måste det innehålla varje utfärdande certifikat i kedjan.
ValideraCertifikatanvändning
Standardvärde: true
Den här kontrollen verifierar att certifikatet som presenteras av klienten har EKU (Client Authentication Extended Key Use) eller inga EKU:er alls. Som specifikationerna säger, om ingen EKU anges, anses alla EKU:er vara giltiga.
ValideraGiltighetsperiod
Standardvärde: true
Den här kontrollen verifierar att certifikatet är inom dess giltighetsperiod. På varje begäran ser hanteraren till att ett certifikat som var giltigt när det visades inte har upphört att gälla under den aktuella sessionen.
Återkallandemarkör
Standardvärde: X509RevocationFlag.ExcludeRoot
En flagga som anger vilka certifikat i kedjan som ska kontrolleras för återkallande.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Återkallelsemode
Standardvärde: X509RevocationMode.Online
En flagga som anger hur återkallningskontroller utförs.
Om du anger en onlinekontroll kan det leda till en lång fördröjning medan certifikatutfärdare kontaktas.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Kan jag konfigurera min app så att den bara kräver ett certifikat på vissa sökvägar?
Det här är inte möjligt. Kom ihåg att certifikatutbytet görs i början av HTTPS-konversationen, det görs av servern innan den första begäran tas emot på den anslutningen så det går inte att omfång baserat på några begärandefält.
Hanterarhändelser
Hanteraren har två händelser:
- 
              OnAuthenticationFailed: Anropas om ett undantag inträffar under autentiseringen och gör att du kan reagera.
- 
              OnCertificateValidated: Anropas efter att certifikatet har verifierats, godkänts och ett standardobjekt har skapats. Med den här händelsen kan du utföra din egen validering och utöka eller ersätta principalen. Exempel är:- Avgöra om certifikatet är känt för dina tjänster. 
- Skapa din egen huvudprincip. Tänk på följande exempel: - 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; } }; });
 
Om du upptäcker att det inkommande certifikatet inte uppfyller din extra validering kontaktar du context.Fail("failure reason") med en felorsak.
För bättre funktionalitet anropar du en tjänst som är registrerad i dependency injection och som ansluter till en databas eller någon annan typ av användardatalager. Få åtkomst till tjänsten med hjälp av den kontext som skickas till delegaten. Tänk på följande exempel:
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;
            }
        };
    });
Konceptuellt är valideringen av certifikatet en auktoriseringsfråga. Det är helt acceptabelt att lägga till en kontroll av till exempel en utfärdare eller fingeravtrycks-ID i en auktoriseringspolicy i stället för inuti OnCertificateValidated.
Konfigurera servern för att kräva certifikat
Kestrel
I Program.cskonfigurerar du Kestrel på följande sätt:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.ConfigureHttpsDefaults(options =>
        options.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});
Anmärkning
Slutpunkter som skapas genom att anropa Listeninnan anropar ConfigureHttpsDefaults har inte standardvärdena tillämpade.
IIS
Slutför följande steg i IIS-hanteraren:
- Välj din webbplats på fliken Anslutningar .
- Dubbelklicka på alternativet SSL-inställningar i fönstret Funktionsvy .
- Markera kryssrutan Kräv SSL och välj alternativknappen Kräv i avsnittet Klientcertifikat.
              
               
              
              
            
Azure och anpassade webbproxyservrar
Se dokumentationen om värd- och distributionsdokumentation för hur du konfigurerar mellanprogrammet för certifikatvidarebefordran.
Använda certifikatautentisering i Azure Web Apps
Ingen vidarebefordringskonfiguration krävs för Azure. Konfigurationen för vidarebefordring konfigureras av mellanprogrammet för vidarebefordran av certifikat.
Anmärkning
Mellanprogram för vidarebefordran av certifikat krävs för det här scenariot.
Mer information finns i Använda ett TLS/SSL-certifikat i din kod i Azure App Service (Azure-dokumentation).
Använda certifikatautentisering i anpassade webbproxyservrar
Metoden AddCertificateForwarding används för att ange:
- Namnet på klienthuvudet.
- Hur certifikatet ska läsas in (med hjälp av egenskapen HeaderConverter).
I anpassade webbproxyservrar skickas certifikatet som ett anpassat begärandehuvud, till exempel X-SSL-CERT. Om du vill använda det konfigurerar du vidarebefordring av certifikat i 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;
        }
    };
});
Om appen är omvänd proxy av NGINX med konfigurationen proxy_set_header ssl-client-cert $ssl_client_escaped_cert eller distribueras på Kubernetes med NGINX-ingress skickas klientcertifikatet till appen i URL-kodat form. Om du vill använda certifikatet avkodar du det på följande sätt:
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!;
    };
});
Lägg till mellanprogrammet i Program.cs. 
              UseCertificateForwarding anropas före anropen till UseAuthentication och UseAuthorization:
var app = builder.Build();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
En separat klass kan användas för att implementera valideringslogik. Eftersom samma självsignerade certifikat används i det här exemplet kontrollerar du att endast certifikatet kan användas. Kontrollera att tumavtrycken för både klientcertifikatet och servercertifikatet matchar, annars kan alla certifikat användas och räcker för att autentisera. Detta skulle användas i AddCertificate metoden. Du kan också bekräfta subjektet eller utfärdaren här om du använder mellanliggande eller underordnade certifikat.
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;
    }
}
Implementera en HttpClient med hjälp av ett certifikat och IHttpClientFactory
I följande exempel läggs ett klientcertifikat till i en HttpClientHandler med egenskapen ClientCertificates från hanteraren. Den här hanteraren kan sedan användas i en namngiven instans av HttpClient med metoden ConfigurePrimaryHttpMessageHandler. Det här är konfigurationen i 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;
});
              IHttpClientFactory Kan sedan användas för att hämta den namngivna instansen med hanteraren och certifikatet. Metoden CreateClient med namnet på klienten som definierats i Program.cs används för att hämta instansen. HTTP-begäran kan skickas med klienten efter behov:
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}");
    }
}
Om rätt certifikat skickas till servern returneras data. Om inget certifikat eller fel certifikat skickas returneras en HTTP 403-statuskod.
Skapa certifikat i PowerShell
Att skapa certifikaten är den svåraste delen i konfigurationen av det här flödet. Du kan skapa ett rotcertifikat med powershell-cmdleten New-SelfSignedCertificate . När du skapar certifikatet använder du ett starkt lösenord. Det är viktigt att lägga till parametern KeyUsageProperty och parametern KeyUsage som visas.
Skapa rot-Certifikatutfärdare
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
Anmärkning
Parametervärdet -DnsName måste matcha appens distributionsmål. Till exempel "localhost" för utveckling.
Installera i betrodd rotcertifikat
Rotcertifikatet måste vara betrott på värddatorsystemet. Endast rotcertifikat som skapats av en certifikatutfärdare är betrodda som standard. Information om hur du litar på rotcertifikatet i Windows finns i Windows-dokumentationen eller PowerShell-cmdleten Import-Certificate .
Mellanliggande certifikat
Ett mellanliggande certifikat kan nu skapas från rotcertifikatet. Detta krävs inte för alla användningsfall, men du kan behöva skapa många certifikat eller behöva aktivera eller inaktivera grupper av certifikat. Parametern TextExtension krävs för att ange sökvägens längd i de grundläggande begränsningarna för certifikatet.
Det mellanliggande certifikatet kan sedan läggas till i de betrodda mellanliggande certifikaten i Windowsvärdsystemet.
$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
Skapa ett underordnat SSL-certifikat från ett mellanliggande certifikat
Ett barncertifikat kan skapas från ett mellanliggande certifikat. Det här är den slutliga enheten och behöver inte skapa fler undercertifikat.
$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
Skapa underordnat certifikat från rotcertifikat
Ett barncertifikat kan också skapas direkt från rotcertifikatet.
$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
Exempelrot – mellanliggande certifikat – certifikat
$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
När du använder rot-, mellan- eller underordnade certifikat kan certifikaten verifieras med tumavtryck eller PublicKey efter behov:
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);
}
Cachelagring av certifikatverifiering
.NET 5 eller senare versioner stöder möjligheten att aktivera cachelagring av valideringsresultat. Cachelagringen förbättrar avsevärt prestandan för certifikatautentisering, eftersom validering är en dyr åtgärd.
Som standard inaktiverar certifikatautentisering cachelagring. Om du vill aktivera cachelagring anropar du AddCertificateCache i Program.cs:
builder.Services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate()
    .AddCertificateCache(options =>
    {
        options.CacheSize = 1024;
        options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
    });
Standardimplementeringen för cachelagring lagrar resultat i minnet. Du kan tillhandahålla din egen cache genom att implementera ICertificateValidationCache och registrera den med beroendeinmatning. Till exempel services.AddSingleton<ICertificateValidationCache, YourCache>().
Valfria klientcertifikat
Det här avsnittet innehåller information om appar som måste skydda en delmängd av appen med ett certifikat. En sida eller kontrollant i appen kan Razor till exempel kräva klientcertifikat. Detta innebär utmaningar, såsom exempelvis klientcertifikat:
- Är en TLS-funktion, inte en HTTP-funktion.
- Förhandlas per anslutning och vanligtvis i början av anslutningen innan några HTTP-data är tillgängliga.
Det finns två metoder för att implementera valfria klientcertifikat:
- Använda separata värdnamn (SNI) och omdirigering. Även om det är mer arbete att konfigurera rekommenderas detta eftersom det fungerar i de flesta miljöer och protokoll.
- Omförhandling under en HTTP-begäran. Detta har flera begränsningar och rekommenderas inte.
Separata värdar (SNI)
I början av anslutningen är endast SNI-† känd. Klientcertifikat kan konfigureras per värdnamn så att en värd kräver dem och en annan inte gör det.
- Konfigurera bindning för domänen och underdomänen: - Du kan till exempel konfigurera bindningar på contoso.comochmyClient.contoso.com. Värdencontoso.comkräver inget klientcertifikat, menmyClient.contoso.comgör det.
- Mer information finns i:
 
- Du kan till exempel konfigurera bindningar på 
.NET 5 eller senare lägger till mer praktiskt stöd för omdirigering för att hämta valfria klientcertifikat. Mer information finns i Exempel på valfria certifikat.
- För begäranden till webbappen som kräver ett klientcertifikat och inte har något: - Omdirigera till samma sida med hjälp av den skyddade underdomänen för klientcertifikatet.
- Omdirigering till exempel till myClient.contoso.com/requestedPage. Eftersom begäran tillmyClient.contoso.com/requestedPageär ett annat värdnamn äncontoso.com/requestedPageupprättar klienten en annan anslutning och klientcertifikatet tillhandahålls.
- Mer information finns i Introduktion till auktorisering i ASP.NET Core.
 
† Server Name Indication (SNI) är ett TLS-tillägg för att inkludera en virtuell domän som en del av SSL-förhandling. Det innebär i praktiken att det virtuella domännamnet, eller ett värdnamn, kan användas för att identifiera nätverksslutpunkten.
Omförhandling
TLS-omförhandling är en process där klienten och servern kan utvärdera krypteringskraven för en enskild anslutning på nytt, inklusive att begära ett klientcertifikat om det inte har angetts tidigare. TLS-omförhandling är en säkerhetsrisk och rekommenderas inte eftersom:
- I HTTP/1.1-protokollet måste servern först buffra eller konsumera all HTTP-data som befinner sig i transit, såsom POST-begärandekroppar, för att säkerställa att anslutningen är klar för omförhandlingen. Annars kan omförhandlingen sluta svara eller misslyckas.
- HTTP/2 och HTTP/3 förbjuder uttryckligen omförhandling.
- Det finns säkerhetsrisker i samband med omförhandling. TLS 1.3 tog bort omförhandlingen av hela anslutningen och ersatte den med ett nytt tillägg för att endast begära klientcertifikatet efter anslutningens start. Den här mekanismen exponeras via samma API:er och omfattas fortfarande av tidigare begränsningar för buffrings- och HTTP-protokollversioner.
Implementeringen och konfigurationen av den här funktionen varierar beroende på server- och ramverksversion.
IIS
IIS hanterar klientcertifikatförhandlingen åt dig. Ett underavsnitt av programmet kan göra det SslRequireCert möjligt att förhandla om klientcertifikatet för dessa begäranden. Mer information finns i Konfiguration i IIS-dokumentationen .
IIS buffra automatiskt alla begärandetextdata upp till en konfigurerad storleksgräns innan omförhandling. Begäranden som överskrider gränsen avvisas med ett svar på 413. Den här gränsen är som standard 48 KB och kan konfigureras genom att ange uploadReadAheadSize.
HttpSys
HttpSys har två inställningar som styr klientens certifikatförhandling och båda ska anges. Den första är i netsh.exe under http add sslcert clientcertnegotiation=enable/disable. Den här flaggan anger om klientcertifikatet ska förhandlas i början av en anslutning och ska anges till disable för valfria klientcertifikat. Mer information finns i netsh-dokumenten .
Den andra inställningen är ClientCertificateMethod. När det är inställt på AllowRenegotationkan klientcertifikatet omförhandlas under en begäran.
NOTE Programmet bör buffra eller använda begärandets kroppdata innan omförhandlingen görs, annars kan begäran bli osvarbar.
Ett program kan först kontrollera ClientCertificate egenskapen för att se om certifikatet är tillgängligt. Om det inte är tillgängligt, se till att begärandetexten har lästs in innan du anropar GetClientCertificateAsync för att förhandla om ett. Obs! GetClientCertificateAsync Kan returnera ett null-certifikat om klienten avböjer att ange ett.
              NOT Beteendet för ClientCertificate egenskapen ändrades i .NET 6. Mer information finns i det här GitHub-problemet.
Kestrel
Kestrel styr förhandling av klientcertifikat med alternativet ClientCertificateMode .
              ClientCertificateMode.DelayCertificate är det nya alternativet tillgängligt i .NET 6 eller senare. När det är inställt kan en app kontrollera ClientCertificate egenskapen för att se om certifikatet är tillgängligt. Om den inte är tillgänglig kontrollerar du att begärandetexten har förbrukats innan du anropar GetClientCertificateAsync för att förhandla om en. Obs! GetClientCertificateAsync Kan returnera ett null-certifikat om klienten avböjer att ange ett.
              OBS Programmet ska buffra eller använda begärans kroppsdatan innan det försöker omförhandlingen, annars GetClientCertificateAsync kan det utlösa InvalidOperationException: Client stream needs to be drained before renegotiation..
Om du programmatiskt konfigurerar TLS-inställningarna per SNI-värdnamn anropar du den UseHttps överlagring (.NET 6 eller senare) som tar TlsHandshakeCallbackOptions och kontrollerar omförhandlingen av klientcertifikat via TlsHandshakeCallbackContext.AllowDelayedClientCertificateNegotation.
              Microsoft.AspNetCore.Authentication.Certificate innehåller en implementering som liknar certifikatautentisering för ASP.NET Core. Certifikatautentisering sker på TLS-nivå, långt innan det någonsin kommer till ASP.NET Core. Mer exakt är detta en autentiseringshanterare som validerar certifikatet och sedan ger dig en händelse där du kan matcha certifikatet till en ClaimsPrincipal.
Konfigurera servern för certifikatautentisering, oavsett om det är IIS, KestrelAzure Web Apps eller något annat du använder.
Proxy- och lastbalanseringsscenarier
Certifikatautentisering är ett tillståndskänsligt scenario som främst används där en proxy eller lastbalanserare inte hanterar trafik mellan klienter och servrar. Om en proxy eller lastbalanserare används fungerar certifikatautentisering endast om proxyn eller lastbalanseraren:
- Hanterar autentiseringen.
- Skickar användarautentiseringsinformationen till appen (till exempel i ett begärandehuvud), som agerar på autentiseringsinformationen.
Ett alternativ till certifikatautentisering i miljöer där proxyservrar och lastbalanserare används är Active Directory Federated Services (ADFS) med OpenID Connect (OIDC).
Kom igång
Skaffa ett HTTPS-certifikat, tillämpa det och konfigurera servern så att den kräver certifikat.
I webbappen lägger du till en referens till paketet Microsoft.AspNetCore.Authentication.Certificate . Anropa sedan Startup.ConfigureServices i metoden med dina alternativ och ange en delegat för services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...); att utföra eventuell kompletterande validering av klientcertifikatet som skickas med begäranden. Omvandla den informationen till en ClaimsPrincipal och ställ in den på context.Principal egenskapen.
Om autentiseringen misslyckas returnerar den här hanteraren ett 403 (Forbidden) svar i stället för ett 401 (Unauthorized), som du kan förvänta dig. Resonemanget är att autentiseringen ska ske under den första TLS-anslutningen. När den når hanteraren är det för sent. Det går inte att uppgradera anslutningen från en anonym anslutning till en med ett certifikat.
Lägg också till app.UseAuthentication(); i Startup.Configure metoden. Annars kommer HttpContext.User inte att bli inställd till ClaimsPrincipal som skapats från certifikatet. Till exempel:
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
}
Föregående exempel visar standardsättet för att lägga till certifikatautentisering. Hanteraren konstruerar ett användarhuvudnamn med hjälp av de vanliga certifikategenskaperna.
Konfigurera certifikatverifiering
              CertificateAuthenticationOptions Hanteraren har några inbyggda valideringar som är de minsta valideringar som du bör utföra på ett certifikat. Var och en av dessa inställningar är aktiverad som standard.
AllowedCertificateTypes = Kedjad, Självsignerad eller Alla (Kedjad | Självsignerad)
Standardvärde: CertificateTypes.Chained
Den här kontrollen verifierar att endast lämplig certifikattyp tillåts. Om appen använder självsignerade certifikat måste det här alternativet anges till CertificateTypes.All eller CertificateTypes.SelfSigned.
ValideraCertifikatanvändning
Standardvärde: true
Den här kontrollen verifierar att certifikatet som presenteras av klienten har EKU (Client Authentication Extended Key Use) eller inga EKU:er alls. Som specifikationerna säger, om ingen EKU anges, anses alla EKU:er vara giltiga.
ValideraGiltighetsperiod
Standardvärde: true
Den här kontrollen verifierar att certifikatet är inom dess giltighetsperiod. På varje begäran ser hanteraren till att ett certifikat som var giltigt när det visades inte har upphört att gälla under den aktuella sessionen.
Återkallandemarkör
Standardvärde: X509RevocationFlag.ExcludeRoot
En flagga som anger vilka certifikat i kedjan som ska kontrolleras för återkallande.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Återkallelsemode
Standardvärde: X509RevocationMode.Online
En flagga som anger hur återkallningskontroller utförs.
Om du anger en onlinekontroll kan det leda till en lång fördröjning medan certifikatutfärdare kontaktas.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Kan jag konfigurera min app så att den bara kräver ett certifikat på vissa sökvägar?
Det här är inte möjligt. Kom ihåg att certifikatutbytet görs i början av HTTPS-konversationen, det görs av servern innan den första begäran tas emot på den anslutningen så det går inte att omfång baserat på några begärandefält.
Hanterarhändelser
Hanteraren har två händelser:
- 
              OnAuthenticationFailed: Anropas om ett undantag inträffar under autentiseringen och gör att du kan reagera.
- 
              OnCertificateValidated: Anropas efter att certifikatet har verifierats, godkänts och ett standardobjekt har skapats. Med den här händelsen kan du utföra din egen validering och utöka eller ersätta principalen. Exempel är:- Avgöra om certifikatet är känt för dina tjänster. 
- Skapa din egen huvudprincip. Överväg följande exempel i - 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; } }; });
 
Om du upptäcker att det inkommande certifikatet inte uppfyller din extra validering kontaktar du context.Fail("failure reason") med en felorsak.
För verklig funktionalitet vill du förmodligen anropa en tjänst som är registrerad i beroendeinjektion och som ansluter till en databas eller någon annan typ av användarlagring. Få åtkomst till din tjänst med hjälp av den kontext som överförts till din delegat. Överväg följande exempel i 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;
            }
        };
    });
Konceptuellt är valideringen av certifikatet en auktoriseringsfråga. Det är helt acceptabelt att lägga till en kontroll av till exempel en utfärdare eller fingeravtrycks-ID i en auktoriseringspolicy i stället för inuti OnCertificateValidated.
Konfigurera servern för att kräva certifikat
Kestrel
I Program.cskonfigurerar du Kestrel på följande sätt:
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);
            });
        });
}
Anmärkning
Slutpunkter som skapas genom att anropa Listeninnan anropar ConfigureHttpsDefaults har inte standardvärdena tillämpade.
IIS
Slutför följande steg i IIS-hanteraren:
- Välj din webbplats på fliken Anslutningar .
- Dubbelklicka på alternativet SSL-inställningar i fönstret Funktionsvy .
- Markera kryssrutan Kräv SSL och välj alternativknappen Kräv i avsnittet Klientcertifikat.
              
               
              
              
            
Azure och anpassade webbproxyservrar
Se dokumentationen om värd- och distributionsdokumentation för hur du konfigurerar mellanprogrammet för certifikatvidarebefordran.
Använda certifikatautentisering i Azure Web Apps
Ingen vidarebefordringskonfiguration krävs för Azure. Konfigurationen för vidarebefordring konfigureras av mellanprogrammet för vidarebefordran av certifikat.
Anmärkning
Mellanprogram för vidarebefordran av certifikat krävs för det här scenariot.
Mer information finns i Använda ett TLS/SSL-certifikat i din kod i Azure App Service (Azure-dokumentation).
Använda certifikatautentisering i anpassade webbproxyservrar
Metoden AddCertificateForwarding används för att ange:
- Namnet på klienthuvudet.
- Hur certifikatet ska läsas in (med hjälp av egenskapen HeaderConverter).
I anpassade webbproxyservrar skickas certifikatet som ett anpassat begärandehuvud, till exempel X-SSL-CERT. Om du vill använda det konfigurerar du vidarebefordring av certifikat i 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;
}
Om appen är omvänd proxy av NGINX med konfigurationen proxy_set_header ssl-client-cert $ssl_client_escaped_cert eller distribueras på Kubernetes med NGINX-ingress skickas klientcertifikatet till appen i URL-kodat form. Om du vill använda certifikatet avkodar du det på följande sätt:
I 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;
    };
});
Metoden Startup.Configure lägger sedan till mellanprogrammet. 
              UseCertificateForwarding anropas före anropen till UseAuthentication och UseAuthorization:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...
    app.UseRouting();
    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
En separat klass kan användas för att implementera valideringslogik. Eftersom samma självsignerade certifikat används i det här exemplet kontrollerar du att endast certifikatet kan användas. Kontrollera att tumavtrycken för både klientcertifikatet och servercertifikatet matchar, annars kan alla certifikat användas och räcker för att autentisera. Detta skulle användas i AddCertificate metoden. Du kan också bekräfta subjektet eller utfärdaren här om du använder mellanliggande eller underordnade certifikat.
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;
        }
    }
}
Implementera en HttpClient med hjälp av ett certifikat och HttpClientHandler
Det HttpClientHandler kan läggas till direkt i konstruktorn för HttpClient klassen. Var försiktig när du skapar instanser av HttpClient. 
              HttpClient Kommer sedan att skicka certifikatet med varje begäran.
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}");
}
Implementera en HttpClient med hjälp av ett certifikat och en med namnet HttpClient från IHttpClientFactory
I följande exempel läggs ett klientcertifikat till i en HttpClientHandler med egenskapen ClientCertificates från hanteraren. Den här hanteraren kan sedan användas i en namngiven instans av HttpClient med metoden ConfigurePrimaryHttpMessageHandler. Det här är konfigurationen i 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;
});
              IHttpClientFactory Kan sedan användas för att hämta den namngivna instansen med hanteraren och certifikatet. Metoden CreateClient med namnet på klienten som definierats i Startup klassen används för att hämta instansen. HTTP-begäran kan skickas med hjälp av klienten efter behov.
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}");
}
Om rätt certifikat skickas till servern returneras data. Om inget certifikat eller fel certifikat skickas returneras en HTTP 403-statuskod.
Skapa certifikat i PowerShell
Att skapa certifikaten är den svåraste delen i konfigurationen av det här flödet. Du kan skapa ett rotcertifikat med powershell-cmdleten New-SelfSignedCertificate . När du skapar certifikatet använder du ett starkt lösenord. Det är viktigt att lägga till parametern KeyUsageProperty och parametern KeyUsage som visas.
Skapa rot-Certifikatutfärdare
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
Anmärkning
Parametervärdet -DnsName måste matcha appens distributionsmål. Till exempel "localhost" för utveckling.
Installera i betrodd rotcertifikat
Rotcertifikatet behöver ha förtroende på ditt värdsystem. Ett rotcertifikat som inte har skapats av en certifikatutfärdare kommer inte att betros som standardinställning. Information om hur du litar på rotcertifikatet i Windows finns i den här frågan.
Mellanliggande certifikat
Ett mellanliggande certifikat kan nu skapas från rotcertifikatet. Detta krävs inte för alla användningsfall, men du kan behöva skapa många certifikat eller behöva aktivera eller inaktivera grupper av certifikat. Parametern TextExtension krävs för att ange sökvägens längd i de grundläggande begränsningarna för certifikatet.
Det mellanliggande certifikatet kan sedan läggas till i de betrodda mellanliggande certifikaten i Windowsvärdsystemet.
$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
Skapa ett underordnat SSL-certifikat från ett mellanliggande certifikat
Ett barncertifikat kan skapas från ett mellanliggande certifikat. Det här är den slutliga enheten och behöver inte skapa fler undercertifikat.
$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
Skapa underordnat certifikat från rotcertifikat
Ett barncertifikat kan också skapas direkt från rotcertifikatet.
$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
Exempelrot – mellanliggande certifikat – certifikat
$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
När du använder rot-, mellan- eller underordnade certifikat kan certifikaten verifieras med tumavtrycket eller PublicKey efter behov.
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;
        }
    }
}
Cachelagring av certifikatverifiering
.NET 5 eller senare versioner stöder möjligheten att aktivera cachelagring av valideringsresultat. Cachelagringen förbättrar avsevärt prestandan för certifikatautentisering, eftersom validering är en dyr åtgärd.
Som standard inaktiverar certifikatautentisering cachelagring. Om du vill aktivera cachelagring anropar du AddCertificateCache i Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(
        CertificateAuthenticationDefaults.AuthenticationScheme)
            .AddCertificate()
            .AddCertificateCache(options =>
            {
                options.CacheSize = 1024;
                options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
            });
}
Standardimplementeringen för cachelagring lagrar resultat i minnet. Du kan tillhandahålla din egen cache genom att implementera ICertificateValidationCache och registrera den med beroendeinmatning. Till exempel services.AddSingleton<ICertificateValidationCache, YourCache>().
Valfria klientcertifikat
Det här avsnittet innehåller information om appar som måste skydda en delmängd av appen med ett certifikat. En sida eller kontrollant i appen kan Razor till exempel kräva klientcertifikat. Detta innebär utmaningar, såsom exempelvis klientcertifikat:
- Är en TLS-funktion, inte en HTTP-funktion.
- Förhandlas per anslutning och vanligtvis i början av anslutningen innan några HTTP-data är tillgängliga.
Det finns två metoder för att implementera valfria klientcertifikat:
- Använda separata värdnamn (SNI) och omdirigering. Även om det är mer arbete att konfigurera rekommenderas detta eftersom det fungerar i de flesta miljöer och protokoll.
- Omförhandling under en HTTP-begäran. Detta har flera begränsningar och rekommenderas inte.
Separata värdar (SNI)
I början av anslutningen är endast SNI-† känd. Klientcertifikat kan konfigureras per värdnamn så att en värd kräver dem och en annan inte gör det.
- Konfigurera bindning för domänen och underdomänen: - Du kan till exempel konfigurera bindningar på contoso.comochmyClient.contoso.com. Värdencontoso.comkräver inget klientcertifikat, menmyClient.contoso.comgör det.
- Mer information finns i:
 
- Du kan till exempel konfigurera bindningar på 
.NET 5 eller senare lägger till mer praktiskt stöd för omdirigering för att hämta valfria klientcertifikat. Mer information finns i Exempel på valfria certifikat.
- För begäranden till webbappen som kräver ett klientcertifikat och inte har något: - Omdirigera till samma sida med hjälp av den skyddade underdomänen för klientcertifikatet.
- Omdirigering till exempel till myClient.contoso.com/requestedPage. Eftersom begäran tillmyClient.contoso.com/requestedPageär ett annat värdnamn äncontoso.com/requestedPageupprättar klienten en annan anslutning och klientcertifikatet tillhandahålls.
- Mer information finns i Introduktion till auktorisering i ASP.NET Core.
 
† Server Name Indication (SNI) är ett TLS-tillägg för att inkludera en virtuell domän som en del av SSL-förhandling. Det innebär i praktiken att det virtuella domännamnet, eller ett värdnamn, kan användas för att identifiera nätverksslutpunkten.
Omförhandling
TLS-omförhandling är en process där klienten och servern kan utvärdera krypteringskraven för en enskild anslutning på nytt, inklusive att begära ett klientcertifikat om det inte har angetts tidigare. TLS-omförhandling är en säkerhetsrisk och rekommenderas inte eftersom:
- I HTTP/1.1-protokollet måste servern först buffra eller konsumera all HTTP-data som befinner sig i transit, såsom POST-begärandekroppar, för att säkerställa att anslutningen är klar för omförhandlingen. Annars kan omförhandlingen sluta svara eller misslyckas.
- HTTP/2 och HTTP/3 förbjuder uttryckligen omförhandling.
- Det finns säkerhetsrisker i samband med omförhandling. TLS 1.3 tog bort omförhandlingen av hela anslutningen och ersatte den med ett nytt tillägg för att endast begära klientcertifikatet efter anslutningens start. Den här mekanismen exponeras via samma API:er och omfattas fortfarande av tidigare begränsningar för buffrings- och HTTP-protokollversioner.
Implementeringen och konfigurationen av den här funktionen varierar beroende på server- och ramverksversion.
IIS
IIS hanterar klientcertifikatförhandlingen åt dig. Ett underavsnitt av programmet kan göra det SslRequireCert möjligt att förhandla om klientcertifikatet för dessa begäranden. Mer information finns i Konfiguration i IIS-dokumentationen .
IIS buffra automatiskt alla begärandetextdata upp till en konfigurerad storleksgräns innan omförhandling. Begäranden som överskrider gränsen avvisas med ett svar på 413. Den här gränsen är som standard 48 KB och kan konfigureras genom att ange uploadReadAheadSize.
HttpSys
HttpSys har två inställningar som styr klientens certifikatförhandling och båda ska anges. Den första är i netsh.exe under http add sslcert clientcertnegotiation=enable/disable. Den här flaggan anger om klientcertifikatet ska förhandlas i början av en anslutning och ska anges till disable för valfria klientcertifikat. Mer information finns i netsh-dokumenten .
Den andra inställningen är ClientCertificateMethod. När det är inställt på AllowRenegotationkan klientcertifikatet omförhandlas under en begäran.
NOTE Programmet bör buffra eller använda begärandets kroppdata innan omförhandlingen görs, annars kan begäran bli osvarbar.
Det finns ett känt problem där aktivering AllowRenegotation kan leda till att omförhandlingen sker synkront vid åtkomst till ClientCertificate egenskapen. 
              GetClientCertificateAsync Anropa metoden för att undvika detta. Detta har åtgärdats i .NET 6. Mer information finns i det här GitHub-problemet. Obs! GetClientCertificateAsync Kan returnera ett null-certifikat om klienten avböjer att ange ett.
Kestrel
Kestrel styr förhandling av klientcertifikat med alternativet ClientCertificateMode .
För .NET 5 eller tidigare stöder inte Kestrel förnyad förhandling efter att en anslutning har påbörjats för att erhålla ett klientcertifikat. Den här funktionen har lagts till i .NET 6.
              Microsoft.AspNetCore.Authentication.Certificate innehåller en implementering som liknar certifikatautentisering för ASP.NET Core. Certifikatautentisering sker på TLS-nivå, långt innan det någonsin kommer till ASP.NET Core. Mer exakt är detta en autentiseringshanterare som validerar certifikatet och sedan ger dig en händelse där du kan matcha certifikatet till en ClaimsPrincipal.
Konfigurera servern för certifikatautentisering, oavsett om det är IIS, KestrelAzure Web Apps eller något annat du använder.
Proxy- och lastbalanseringsscenarier
Certifikatautentisering är ett tillståndskänsligt scenario som främst används där en proxy eller lastbalanserare inte hanterar trafik mellan klienter och servrar. Om en proxy eller lastbalanserare används fungerar certifikatautentisering endast om proxyn eller lastbalanseraren:
- Hanterar autentiseringen.
- Skickar användarautentiseringsinformationen till appen (till exempel i ett begärandehuvud), som agerar på autentiseringsinformationen.
Ett alternativ till certifikatautentisering i miljöer där proxyservrar och lastbalanserare används är Active Directory Federated Services (ADFS) med OpenID Connect (OIDC).
Kom igång
Skaffa ett HTTPS-certifikat, tillämpa det och konfigurera servern så att den kräver certifikat.
I webbappen lägger du till en referens till paketet Microsoft.AspNetCore.Authentication.Certificate . Anropa sedan Startup.ConfigureServices i metoden med dina alternativ och ange en delegat för services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...); att utföra eventuell kompletterande validering av klientcertifikatet som skickas med begäranden. Omvandla den informationen till en ClaimsPrincipal och ställ in den på context.Principal egenskapen.
Om autentiseringen misslyckas returnerar den här hanteraren ett 403 (Forbidden) svar i stället för ett 401 (Unauthorized), som du kan förvänta dig. Resonemanget är att autentiseringen ska ske under den första TLS-anslutningen. När den når hanteraren är det för sent. Det går inte att uppgradera anslutningen från en anonym anslutning till en med ett certifikat.
Lägg också till app.UseAuthentication(); i Startup.Configure metoden. Annars kommer HttpContext.User inte att bli inställd till ClaimsPrincipal som skapats från certifikatet. Till exempel:
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
}
Föregående exempel visar standardsättet för att lägga till certifikatautentisering. Hanteraren konstruerar ett användarhuvudnamn med hjälp av de vanliga certifikategenskaperna.
Konfigurera certifikatverifiering
              CertificateAuthenticationOptions Hanteraren har några inbyggda valideringar som är de minsta valideringar som du bör utföra på ett certifikat. Var och en av dessa inställningar är aktiverad som standard.
AllowedCertificateTypes = Kedjad, Självsignerad eller Alla (Kedjad | Självsignerad)
Standardvärde: CertificateTypes.Chained
Den här kontrollen verifierar att endast lämplig certifikattyp tillåts. Om appen använder självsignerade certifikat måste det här alternativet anges till CertificateTypes.All eller CertificateTypes.SelfSigned.
ValideraCertifikatanvändning
Standardvärde: true
Den här kontrollen verifierar att certifikatet som presenteras av klienten har EKU (Client Authentication Extended Key Use) eller inga EKU:er alls. Som specifikationerna säger, om ingen EKU anges, anses alla EKU:er vara giltiga.
ValideraGiltighetsperiod
Standardvärde: true
Den här kontrollen verifierar att certifikatet är inom dess giltighetsperiod. På varje begäran ser hanteraren till att ett certifikat som var giltigt när det visades inte har upphört att gälla under den aktuella sessionen.
Återkallandemarkör
Standardvärde: X509RevocationFlag.ExcludeRoot
En flagga som anger vilka certifikat i kedjan som ska kontrolleras för återkallande.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Återkallelsemode
Standardvärde: X509RevocationMode.Online
En flagga som anger hur återkallningskontroller utförs.
Om du anger en onlinekontroll kan det leda till en lång fördröjning medan certifikatutfärdare kontaktas.
Återkallningskontroller utförs endast när certifikatet är kopplat till ett rotcertifikat.
Kan jag konfigurera min app så att den bara kräver ett certifikat på vissa sökvägar?
Det här är inte möjligt. Kom ihåg att certifikatutbytet görs i början av HTTPS-konversationen, det görs av servern innan den första begäran tas emot på den anslutningen så det går inte att omfång baserat på några begärandefält.
Hanterarhändelser
Hanteraren har två händelser:
- 
              OnAuthenticationFailed: Anropas om ett undantag inträffar under autentiseringen och gör att du kan reagera.
- 
              OnCertificateValidated: Anropas efter att certifikatet har verifierats, godkänts och ett standardobjekt har skapats. Med den här händelsen kan du utföra din egen validering och utöka eller ersätta principalen. Exempel är:- Avgöra om certifikatet är känt för dina tjänster. 
- Skapa din egen huvudprincip. Överväg följande exempel i - 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; } }; });
 
Om du upptäcker att det inkommande certifikatet inte uppfyller din extra validering kontaktar du context.Fail("failure reason") med en felorsak.
För verklig funktionalitet vill du förmodligen anropa en tjänst som är registrerad i beroendeinjektion och som ansluter till en databas eller någon annan typ av användarlagring. Få åtkomst till din tjänst med hjälp av den kontext som överförts till din delegat. Överväg följande exempel i 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;
            }
        };
    });
Konceptuellt är valideringen av certifikatet en auktoriseringsfråga. Det är helt acceptabelt att lägga till en kontroll av till exempel en utfärdare eller fingeravtrycks-ID i en auktoriseringspolicy i stället för inuti OnCertificateValidated.
Konfigurera servern för att kräva certifikat
Kestrel
I Program.cskonfigurerar du Kestrel på följande sätt:
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);
            });
        });
}
Anmärkning
Slutpunkter som skapas genom att anropa Listeninnan anropar ConfigureHttpsDefaults har inte standardvärdena tillämpade.
IIS
Slutför följande steg i IIS-hanteraren:
- Välj din webbplats på fliken Anslutningar .
- Dubbelklicka på alternativet SSL-inställningar i fönstret Funktionsvy .
- Markera kryssrutan Kräv SSL och välj alternativknappen Kräv i avsnittet Klientcertifikat.
              
               
              
              
            
Azure och anpassade webbproxyservrar
Se dokumentationen om värd- och distributionsdokumentation för hur du konfigurerar mellanprogrammet för certifikatvidarebefordran.
Använda certifikatautentisering i Azure Web Apps
Ingen vidarebefordringskonfiguration krävs för Azure. Konfigurationen för vidarebefordring konfigureras av mellanprogrammet för vidarebefordran av certifikat.
Anmärkning
Mellanprogram för vidarebefordran av certifikat krävs för det här scenariot.
Mer information finns i Använda ett TLS/SSL-certifikat i din kod i Azure App Service (Azure-dokumentation).
Använda certifikatautentisering i anpassade webbproxyservrar
Metoden AddCertificateForwarding används för att ange:
- Namnet på klienthuvudet.
- Hur certifikatet ska läsas in (med hjälp av egenskapen HeaderConverter).
I anpassade webbproxyservrar skickas certifikatet som ett anpassat begärandehuvud, till exempel X-SSL-CERT. Om du vill använda det konfigurerar du vidarebefordring av certifikat i 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;
}
Om appen är omvänd proxy av NGINX med konfigurationen proxy_set_header ssl-client-cert $ssl_client_escaped_cert eller distribueras på Kubernetes med NGINX-ingress skickas klientcertifikatet till appen i URL-kodat form. Om du vill använda certifikatet avkodar du det på följande sätt:
Lägg till namnområdet för System.Net överst i Startup.cs:
using System.Net;
I 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;
    };
});
              UrlEncodedPemToByteArray Lägg till metoden:
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);
}
Metoden Startup.Configure lägger sedan till mellanprogrammet. 
              UseCertificateForwarding anropas före anropen till UseAuthentication och UseAuthorization:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...
    app.UseRouting();
    app.UseCertificateForwarding();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
En separat klass kan användas för att implementera valideringslogik. Eftersom samma självsignerade certifikat används i det här exemplet kontrollerar du att endast certifikatet kan användas. Kontrollera att tumavtrycken för både klientcertifikatet och servercertifikatet matchar, annars kan alla certifikat användas och räcker för att autentisera. Detta skulle användas i AddCertificate metoden. Du kan också bekräfta subjektet eller utfärdaren här om du använder mellanliggande eller underordnade certifikat.
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;
        }
    }
}
Implementera en HttpClient med hjälp av ett certifikat och HttpClientHandler
Det HttpClientHandler kan läggas till direkt i konstruktorn för HttpClient klassen. Var försiktig när du skapar instanser av HttpClient. 
              HttpClient Kommer sedan att skicka certifikatet med varje begäran.
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}");
}
Implementera en HttpClient med hjälp av ett certifikat och en med namnet HttpClient från IHttpClientFactory
I följande exempel läggs ett klientcertifikat till i en HttpClientHandler med egenskapen ClientCertificates från hanteraren. Den här hanteraren kan sedan användas i en namngiven instans av HttpClient med metoden ConfigurePrimaryHttpMessageHandler. Det här är konfigurationen i 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;
});
              IHttpClientFactory Kan sedan användas för att hämta den namngivna instansen med hanteraren och certifikatet. Metoden CreateClient med namnet på klienten som definierats i Startup klassen används för att hämta instansen. HTTP-begäran kan skickas med hjälp av klienten efter behov.
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}");
}
Om rätt certifikat skickas till servern returneras data. Om inget certifikat eller fel certifikat skickas returneras en HTTP 403-statuskod.
Skapa certifikat i PowerShell
Att skapa certifikaten är den svåraste delen i konfigurationen av det här flödet. Du kan skapa ett rotcertifikat med powershell-cmdleten New-SelfSignedCertificate . När du skapar certifikatet använder du ett starkt lösenord. Det är viktigt att lägga till parametern KeyUsageProperty och parametern KeyUsage som visas.
Skapa rot-Certifikatutfärdare
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
Anmärkning
Parametervärdet -DnsName måste matcha appens distributionsmål. Till exempel "localhost" för utveckling.
Installera i betrodd rotcertifikat
Rotcertifikatet behöver ha förtroende på ditt värdsystem. Ett rotcertifikat som inte har skapats av en certifikatutfärdare kommer inte att betros som standardinställning. Information om hur du litar på rotcertifikatet i Windows finns i den här frågan.
Mellanliggande certifikat
Ett mellanliggande certifikat kan nu skapas från rotcertifikatet. Detta krävs inte för alla användningsfall, men du kan behöva skapa många certifikat eller behöva aktivera eller inaktivera grupper av certifikat. Parametern TextExtension krävs för att ange sökvägens längd i de grundläggande begränsningarna för certifikatet.
Det mellanliggande certifikatet kan sedan läggas till i de betrodda mellanliggande certifikaten i Windowsvärdsystemet.
$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
Skapa ett underordnat SSL-certifikat från ett mellanliggande certifikat
Ett barncertifikat kan skapas från ett mellanliggande certifikat. Det här är den slutliga enheten och behöver inte skapa fler undercertifikat.
$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
Skapa underordnat certifikat från rotcertifikat
Ett barncertifikat kan också skapas direkt från rotcertifikatet.
$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
Exempelrot – mellanliggande certifikat – certifikat
$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
När du använder rot-, mellan- eller underordnade certifikat kan certifikaten verifieras med tumavtrycket eller PublicKey efter behov.
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;
        }
    }
}
Valfria klientcertifikat
Det här avsnittet innehåller information om appar som måste skydda en delmängd av appen med ett certifikat. En sida eller kontrollant i appen kan Razor till exempel kräva klientcertifikat. Detta innebär utmaningar, såsom exempelvis klientcertifikat:
- Är en TLS-funktion, inte en HTTP-funktion.
- Förhandlas per anslutning och vanligtvis i början av anslutningen innan några HTTP-data är tillgängliga.
Det finns två metoder för att implementera valfria klientcertifikat:
- Använda separata värdnamn (SNI) och omdirigering. Även om det är mer arbete att konfigurera rekommenderas detta eftersom det fungerar i de flesta miljöer och protokoll.
- Omförhandling under en HTTP-begäran. Detta har flera begränsningar och rekommenderas inte.
Separata värdar (SNI)
I början av anslutningen är endast SNI-† känd. Klientcertifikat kan konfigureras per värdnamn så att en värd kräver dem och en annan inte gör det.
- Konfigurera bindning för domänen och underdomänen: - Du kan till exempel konfigurera bindningar på contoso.comochmyClient.contoso.com. Värdencontoso.comkräver inget klientcertifikat, menmyClient.contoso.comgör det.
- Mer information finns i: - 
              
              Kestrel webbserver i ASP.NET Core: - ListenOptions.UseHttps
- ClientCertificateMode
- Obs! Kestrel Stöder för närvarande inte flera TLS-konfigurationer på en bindning. Du behöver två bindningar med unika IP-adresser eller portar. Mer information finns i det här GitHub-problemet.
 
- IIS
- HTTP.sys: Konfigurera Windows Server
 
- 
              
              Kestrel webbserver i ASP.NET Core: 
 
- Du kan till exempel konfigurera bindningar på 
.NET 5 eller senare lägger till mer praktiskt stöd för omdirigering för att hämta valfria klientcertifikat. Mer information finns i Exempel på valfria certifikat.
- För begäranden till webbappen som kräver ett klientcertifikat och inte har något: - Omdirigera till samma sida med hjälp av den skyddade underdomänen för klientcertifikatet.
- Omdirigering till exempel till myClient.contoso.com/requestedPage. Eftersom begäran tillmyClient.contoso.com/requestedPageär ett annat värdnamn äncontoso.com/requestedPageupprättar klienten en annan anslutning och klientcertifikatet tillhandahålls.
- Mer information finns i Introduktion till auktorisering i ASP.NET Core.
 
† Server Name Indication (SNI) är ett TLS-tillägg för att inkludera en virtuell domän som en del av SSL-förhandling. Det innebär i praktiken att det virtuella domännamnet, eller ett värdnamn, kan användas för att identifiera nätverksslutpunkten.
Omförhandling
TLS-omförhandling är en process där klienten och servern kan utvärdera krypteringskraven för en enskild anslutning på nytt, inklusive att begära ett klientcertifikat om det inte har angetts tidigare. TLS-omförhandling är en säkerhetsrisk och rekommenderas inte eftersom:
- I HTTP/1.1-protokollet måste servern först buffra eller konsumera all HTTP-data som befinner sig i transit, såsom POST-begärandekroppar, för att säkerställa att anslutningen är klar för omförhandlingen. Annars kan omförhandlingen sluta svara eller misslyckas.
- HTTP/2 och HTTP/3 förbjuder uttryckligen omförhandling.
- Det finns säkerhetsrisker i samband med omförhandling. TLS 1.3 tog bort omförhandlingen av hela anslutningen och ersatte den med ett nytt tillägg för att endast begära klientcertifikatet efter anslutningens start. Den här mekanismen exponeras via samma API:er och omfattas fortfarande av tidigare begränsningar för buffrings- och HTTP-protokollversioner.
Implementeringen och konfigurationen av den här funktionen varierar beroende på server- och ramverksversion.
IIS
IIS hanterar klientcertifikatförhandlingen åt dig. Ett underavsnitt av programmet kan göra det SslRequireCert möjligt att förhandla om klientcertifikatet för dessa begäranden. Mer information finns i Konfiguration i IIS-dokumentationen .
IIS buffra automatiskt alla begärandetextdata upp till en konfigurerad storleksgräns innan omförhandling. Begäranden som överskrider gränsen avvisas med ett svar på 413. Den här gränsen är som standard 48 KB och kan konfigureras genom att ange uploadReadAheadSize.
HttpSys
HttpSys har två inställningar som styr klientens certifikatförhandling och båda ska anges. Den första är i netsh.exe under http add sslcert clientcertnegotiation=enable/disable. Den här flaggan anger om klientcertifikatet ska förhandlas i början av en anslutning och ska anges till disable för valfria klientcertifikat. Mer information finns i netsh-dokumenten .
Den andra inställningen är ClientCertificateMethod. När det är inställt på AllowRenegotationkan klientcertifikatet omförhandlas under en begäran.
NOTE Programmet bör buffra eller använda begärandets kroppdata innan omförhandlingen görs, annars kan begäran bli osvarbar.
Det finns ett känt problem där aktivering AllowRenegotation kan leda till att omförhandlingen sker synkront vid åtkomst till ClientCertificate egenskapen. 
              GetClientCertificateAsync Anropa metoden för att undvika detta. Detta har åtgärdats i .NET 6. Mer information finns i det här GitHub-problemet. Obs! GetClientCertificateAsync Kan returnera ett null-certifikat om klienten avböjer att ange ett.
Kestrel
Kestrel styr förhandling av klientcertifikat med alternativet ClientCertificateMode .
För .NET 5 eller tidigare stöder inte Kestrel förnyad förhandling efter att en anslutning har påbörjats för att erhålla ett klientcertifikat. Den här funktionen har lagts till i .NET 6.
Lämna frågor, kommentarer och annan feedback om valfria klientcertifikat i det här GitHub-diskussionsproblemet .
ASP.NET Core