Konfigurace ověřování certifikátů v ASP.NET Core

Microsoft.AspNetCore.Authentication.Certificateobsahuje implementaci podobnou ověřování certifikátů pro ASP.NET Core. Ověřování certifikátů probíhá na úrovni protokolu TLS, dlouho předtím, než se ASP.NET Core. Přesněji jde o obslužnou rutinu ověřování, která ověří certifikát a pak vám poskytne událost, kde můžete tento certifikát přeložit na ClaimsPrincipal .

Nakonfigurujte server pro ověřování certifikátů, ať už je to služba IIS, , Azure Web Apps nebo cokoli Kestrel jiného, co používáte.

Scénáře proxy serveru a nástroje pro vyrovnávání zatížení

Ověřování certifikátů je stavový scénář, který se primárně používá v případě, že proxy server nebo nástroj pro vyrovnávání zatížení nezvládá provoz mezi klienty a servery. Pokud se používá proxy server nebo nástroj pro vyrovnávání zatížení, ověřování certifikátů funguje jenom v případě, že proxy server nebo nástroj pro vyrovnávání zatížení:

  • Zpracovává ověřování.
  • Předá aplikaci ověřovací informace uživatele (například v hlavičce požadavku), která funguje s ověřovacími informacemi.

Alternativou k ověřování certifikátů v prostředích, kde se používají proxy a nástroje pro vyrovnávání zatížení, je služba Active Directory Federated Services (ADFS) s OpenID Připojení (OIDC).

Začínáme

Získejte certifikát HTTPS, použijte ho a nakonfigurujte server tak, aby vyžadoval certifikáty.

Ve webové aplikaci přidejte odkaz na balíček Microsoft.AspNetCore.Authentication.Certificate. Potom v metodě zavolejte s vašimi možnostmi a popište delegátovi, aby u klientského certifikátu odeslaného s požadavky provést jakékoli dodatečné Startup.ConfigureServices services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...); OnCertificateValidated ověření. Změnit tyto informace na a ClaimsPrincipal nastavit je u vlastnosti context.Principal .

Pokud se ověření nezdaří, tato obslužná rutina 403 (Forbidden) vrátí odpověď místo , jak můžete 401 (Unauthorized) očekávat. Důvodem je, že ověřování by mělo pro dojít během počátečního připojení TLS. Než obslužná rutina dosáhne, je příliš pozdě. Neexistuje žádný způsob, jak upgradovat připojení z anonymního připojení na připojení s certifikátem.

Přidejte app.UseAuthentication(); také Startup.Configure metodu . V opačném případě se z certifikátu HttpContext.User ClaimsPrincipal nenastaví na hodnotu vytvořenou. Například:

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
}
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
}

Předchozí příklad ukazuje výchozí způsob, jak přidat ověřování certifikátem. Obslužná rutina vytvoří objekt zabezpečení uživatele pomocí společných vlastností certifikátu.

Konfigurace ověření certifikátu

Obslužná CertificateAuthenticationOptions rutina obsahuje některá předdefinová ověření, která jsou minimálními ověřeními, která byste měli s certifikátem provést. Každé z těchto nastavení je ve výchozím nastavení povolené.

AllowedCertificateTypes = Chained, SelfSigned nebo All (zřetězované | SelfSigned)

Výchozí hodnota: CertificateTypes.Chained

Tato kontrola ověří, že je povolený jenom příslušný typ certifikátu. Pokud aplikace používá certifikáty podepsané svým držitelem, musí být tato možnost nastavená na CertificateTypes.All nebo CertificateTypes.SelfSigned .

ValidateCertificateUse

Výchozí hodnota: true

Tato kontrola ověří, zda má certifikát prezentovaný klientem rozšířené použití klíče ověření klienta (EKU) nebo žádné rozšířené klíče EKU. Jak je uvedeno v specifikacích, pokud není zadán žádný EKU, pak se všechny EKU považují za platné.

ValidateValidityPeriod

Výchozí hodnota: true

Tato kontrola ověří, že certifikát je v rámci doby platnosti. U každého požadavku obslužná rutina zajišťuje, že certifikát, který byl platný, když byl prezentován, nevyšel během aktuální relace.

Prodleva odvolání

Výchozí hodnota: X509RevocationFlag.ExcludeRoot

Příznak, který určuje, u kterých certifikátů v řetězu se kontroluje odvolání.

Kontroly odvolání se provádějí pouze v případě, že je certifikát zřetěován s kořenovým certifikátem.

Režim odvolání

Výchozí hodnota: X509RevocationMode.Online

Příznak, který určuje, jak se provádějí kontroly odvolání.

Zadání online kontroly může způsobit dlouhé zpoždění při kontaktování certifikační autority.

Kontroly odvolání se provádějí pouze v případě, že je certifikát zřetěován s kořenovým certifikátem.

Můžu aplikaci nakonfigurovat tak, aby vyžadovala certifikát jenom na určitých cestách?

To není možné. Mějte na paměti, že výměna certifikátů se provádí na začátku konverzace HTTPS. Server ji provádí před přijetím prvního požadavku na toto připojení, takže není možné vymezený rozsah na základě žádných polí požadavku.

Události obslužné rutiny

Obslužná rutina má dvě události:

  • OnAuthenticationFailed: Volá se, pokud během ověřování dojde k výjimce a umožní vám reagovat.
  • OnCertificateValidated: Volá se po ověření certifikátu, úspěšně se ověří a vytvoří se výchozí objekt zabezpečení. Tato událost vám umožní provést vlastní ověření a rozšířit nebo nahradit objekt zabezpečení. Příklady:
    • Určení, jestli je certifikát pro vaše služby známý

    • Vytvoření vlastního objektu zabezpečení. Zvažte následující příklad v Startup.ConfigureServices souboru :

      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;
                  }
              };
          });
      

Pokud zjistíte, že příchozí certifikát nesplňuje vaše dodatečné ověření, zavolejte context.Fail("failure reason") z důvodu selhání.

Pro skutečné funkce budete pravděpodobně chtít volat službu zaregistrovanou v injektáži závislostí, která se připojuje k databázi nebo jinému typu uživatelského úložiště. Přístup ke službě pomocí kontextu předaly do delegáta. Zvažte následující příklad v Startup.ConfigureServices souboru :

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;
            }
        };
    });

Koncepčně se ověření certifikátu týká autorizace. Přidání kontroly, například vystavitele nebo kryptografického otisku v zásadách autorizace, a ne uvnitř OnCertificateValidated , je naprosto přijatelné.

Konfigurace serveru tak, aby vyžadoval certifikáty

Kestrel

V souboru Program.cs Kestrel nakonfigurujte následujícím způsobem:

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);
            });
        });
}

Poznámka

U koncových bodů Listen vytvořených voláním ConfigureHttpsDefaults před voláním se nebudou uplatňovat výchozí hodnoty.

IIS

Ve Správci služby IIS proveďte následující kroky:

  1. Na kartě Připojení vyberte svůj web.
  2. V okně Zobrazení funkcí poklikejte Nastavení ssl.
  3. Zaškrtněte políčko Vyžadovat SSL a v části Klientské certifikáty vyberte přepínač Vyžadovat.

Nastavení klientského certifikátu ve službě IIS

Azure a vlastní webové servery prox

Informace o konfiguraci middlewaru pro předávání certifikátů najdete v dokumentaci k hostiteli a nasazení.

Použití ověřování certifikátů v Azure Web Apps

Azure nevyžaduje konfiguraci předávání dál. Konfiguraci předávání nastaví middleware pro předávání certifikátů.

Poznámka

Pro tento scénář se vyžaduje middleware pro předávání certifikátů.

Další informace najdete v tématu Použití certifikátu TLS/SSL v kódu v Azure App Service (dokumentace Azure).

Použití ověřování certifikátů ve vlastních webových serverech pro

Metoda AddCertificateForwarding se používá k určení:

  • Název hlavičky klienta.
  • Způsob načtení certifikátu (pomocí HeaderConverter vlastnosti ).

Ve vlastních webových serverech prox se certifikát předává jako vlastní hlavička požadavku, například X-SSL-CERT . Pokud ho chcete použít, nakonfigurujte předávání certifikátů v nástroji 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;
}

Pokud je aplikace reverzním serverem proxied serverem NGINX s konfigurací nebo nasazená v Kubernetes pomocí příchozího přenosu dat NGINX, klientský certifikát se předá aplikaci ve formátu zakódované v adrese proxy_set_header ssl-client-cert $ssl_client_escaped_cert URL. Pokud chcete certifikát použít, dekódovat ho následujícím způsobem:

V Startup.ConfigureServices souboru ( 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;
    };
});

Na začátek souboru System.Net přidejte obor názvů pro Startup.cs :

using System.Net;

V 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;
    };
});

Přidejte UrlEncodedPemToByteArray metodu :

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);
}

Metoda Startup.Configure pak přidá middleware. UseCertificateForwarding se volá před voláním a UseAuthentication UseAuthorization :

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

    app.UseRouting();

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

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

K implementaci logiky ověřování lze použít samostatnou třídu. Vzhledem k tomu, že se v tomto příkladu používá stejný certifikát podepsaný svým držitelem, ujistěte se, že je možné použít pouze váš certifikát. Ověřte, že se kryptografický otisk klientského certifikátu i certifikátu serveru shoduje, jinak je možné použít libovolný certifikát a bude stačit k ověření. To by se použilo uvnitř AddCertificate metody . Pokud používáte zprostředkující nebo podřízené certifikáty, můžete tady také ověřit předmět nebo vystavitele.

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;
        }
    }
}

Implementace HttpClient pomocí certifikátu a HttpClientHandler

Objekt HttpClientHandler může být přidán přímo do konstruktoru třídy HttpClient . Při vytváření instancí objektu je třeba HttpClient opatrnost. HttpClientPříkaz pak odešle certifikát s každou žádostí.

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}");
}

Implementace HttpClient pomocí certifikátu a s názvem HttpClient z IHttpClientFactory

V následujícím příkladu je klientský certifikát přidán do HttpClientHandler objektu pomocí vlastnosti z obslužné ClientCertificates rutiny. Tuto obslužnou rutinu lze poté použít v pojmenované instanci HttpClient pomocí ConfigurePrimaryHttpMessageHandler metody . Toto je nastavení v souboru Startup.ConfigureServices :

var clientCertificate = 
    new X509Certificate2(
      Path.Combine(_environment.ContentRootPath, "sts_dev_cert.pfx"), "1234");
 
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
 
services.AddHttpClient("namedClient", c =>
{
}).ConfigurePrimaryHttpMessageHandler(() => handler);

Lze IHttpClientFactory pak použít k získání pojmenované instance s obslužnou rutinou a certifikátem. Metoda s názvem klienta definovaným ve třídě se používá CreateClient k získání Startup instance. Požadavek HTTP je možné odeslat pomocí klienta podle potřeby.

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}");
}

Pokud se na server odesílá správný certifikát, vrátí se data. Pokud není odeslán žádný certifikát nebo nesprávný certifikát, vrátí se stavový kód HTTP 403.

Vytváření certifikátů v PowerShellu

Vytvoření certifikátů je při nastavování tohoto toku nejsnáz. Kořenový certifikát je možné vytvořit pomocí rutiny New-SelfSignedCertificate PowerShellu. Při vytváření certifikátu použijte silné heslo. Je důležité přidat parametr KeyUsageProperty a parametr , jak je KeyUsage znázorněno níže.

Vytvoření kořenové certifikační autority

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

Poznámka

Hodnota -DnsName parametru musí odpovídat cíli nasazení aplikace. Například "localhost" pro vývoj.

Instalace v důvěryhodném kořenovém adresáři

Kořenový certifikát musí být ve vašem hostitelském systému důvěryhodný. Kořenový certifikát, který nebyl vytvořen certifikační autoritou, nebude ve výchozím nastavení důvěryhodný. Následující odkaz vysvětluje, jak toho lze dosáhnout v Windows:

https://social.msdn.microsoft.com/Forums/SqlServer/5ed119ef-1704-4be4-8a4f-ef11de7c8f34/a-certificate-chain-processed-but-terminated-in-a-root-certificate-which-is-not-trusted-by-the

Zprostředkující certifikát

Zprostředkující certifikát je teď možné vytvořit z kořenového certifikátu. To není nutné pro všechny případy použití, ale možná budete muset vytvořit mnoho certifikátů nebo budete muset aktivovat nebo zakázat skupiny certifikátů. Parametr TextExtension je nutný k nastavení délky cesty v základních omezeních certifikátu.

Zprostředkující certifikát je pak možné přidat do důvěryhodného zprostředkujícího certifikátu v Windows systému.

$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

Vytvoření podřízeného certifikátu z zprostředkujícího certifikátu

Podřízený certifikát lze vytvořit z zprostředkujícího certifikátu. Toto je koncová entita a není nutné vytvářet další podřízené certifikáty.

$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

Vytvoření podřízeného certifikátu z kořenového certifikátu

Podřízený certifikát lze také vytvořit přímo z kořenového certifikátu.

$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

Příklad kořenového certifikátu – zprostředkující certifikát – certifikát

$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

Při použití kořenových, zprostředkujících nebo podřízených certifikátů je možné certifikáty podle potřeby ověřit pomocí kryptografického otisku nebo klíče 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;
        }
    }
}

Ukládání certifikátů do mezipaměti

ASP.NET Core verze 5.0 a novější podporují možnost ukládání výsledků ověření do mezipaměti. Ukládání do mezipaměti výrazně zvyšuje výkon ověřování certifikátů, protože ověřování je nákladná operace.

Ve výchozím nastavení ověřování certifikátů ukládání do mezipaměti zakáže. Pokud chcete povolit ukládání do mezipaměti, AddCertificateCache volejte v Startup.ConfigureServices :

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

Výchozí implementace ukládání do mezipaměti ukládá výsledky do paměti. Vlastní mezipaměť můžete poskytnout implementací a registrací pomocí ICertificateValidationCache injektáže závislostí. Například, services.AddSingleton<ICertificateValidationCache, YourCache>().

Volitelné klientské certifikáty

Tato část obsahuje informace pro aplikace, které musí chránit podmnožinu aplikace certifikátem. Například stránka nebo Razor kontroler v aplikaci může vyžadovat klientské certifikáty. To představuje problémy související s klientskými certifikáty:

  • Jsou funkce PROTOKOLU TLS, nikoli funkce HTTP.
  • Vyjednávají se pro každé připojení a obvykle na začátku připojení před tím, než jsou k dispozici data HTTP.

Existují dva přístupy k implementaci volitelných klientských certifikátů:

  1. Použití samostatných názvů hostitelů (SNI) a přesměrování Přestože je konfigurace větší, doporučuje se to, protože funguje ve většině prostředí a protokolů.
  2. Znovu se dosažte během požadavku HTTP. To má několik omezení a nedoporučuje se to.

Samostatní hostitelé (SNI)

Na začátku připojení je známá pouze Indikace názvu serveru † (SNI). Klientské certifikáty lze konfigurovat pro každý název hostitele tak, aby je jeden hostitel vyžádá a druhý nikoli.

ASP.NET Core 5 a novější přidává pohodlnější podporu přesměrování za účelem získání volitelných klientských certifikátů. Další informace najdete v ukázce volitelných certifikátů.

  • Pro požadavky na webovou aplikaci, které vyžadují klientský certifikát a nemají jeden:
    • Přesměrovat na stejnou stránku pomocí subdomény chráněné klientským certifikátem.
    • Můžete například přesměrovat na myClient.contoso.com/requestedPage . Vzhledem k tomu, že požadavek na je jiný název hostitele než , klient navádí jiné připojení a klientský myClient.contoso.com/requestedPage contoso.com/requestedPage certifikát je k dispozici.
    • Další informace naleznete v tématu Úvod do autorizace v ASP.NET Core.

† Indikace názvu serveru (SNI) je rozšíření TLS, které zahrnuje virtuální doménu jako součást vyjednávání SSL. To v podstatě znamená, že název virtuální domény nebo název hostitele lze použít k identifikaci koncového bodu sítě.

Znovu se dojednání

Opětovné vyjednávání protokolu TLS je proces, pomocí kterého klient a server mohou znovu vyhodnotit požadavky na šifrování pro jednotlivá připojení, včetně žádosti o klientský certifikát, pokud nebyl dříve poskytnut. Nové vyjednávání o protokolu TLS je bezpečnostní riziko a nedoporučuje se, protože:

  • V HTTP/1.1 musí server nejprve vyrovnávací paměť nebo spotřebovat jakákoli data HTTP, která letí, například těla požadavků POST, aby se ujistil, že je připojení jasné pro nové vyjednávání. Jinak může znovu vyjednávání přestat reagovat nebo selhat.
  • HTTP/2 a HTTP/3 explicitně zakazují nové vyjednávání.
  • S znovusjednáním souvisí bezpečnostní rizika. Protokol TLS 1.3 odebral nové vyjednávání celého připojení a nahradil ho novým rozšířením pro vyžádání pouze klientského certifikátu po zahájení připojení. Tento mechanismus se zveřejňuje prostřednictvím stejných rozhraní API a stále podléhá předchozím omezením ukládání do vyrovnávací paměti a verzí protokolu HTTP.

Implementace a konfigurace této funkce se liší podle verze serveru a architektury.

IIS

Služba IIS spravuje vyjednávání klientského certifikátu vaším jménem. Dílčí část aplikace může povolit možnost SslRequireCert vyjednat klientský certifikát pro tyto požadavky. Podrobnosti najdete v části Konfigurace v dokumentaci ke službě IIS.

Služba IIS automaticky uloží data textu požadavku do vyrovnávací paměti až do nakonfigurovaného limitu velikosti před tím, než se znovu projedná. Požadavky, které tento limit překročí, se zamítnou s odpovědí 413. Výchozí hodnota tohoto limitu je 48 MB a je možné ji nakonfigurovat nastavením parametru uploadReadAheadSize.

HttpSys

HttpSys má dvě nastavení, která řídí vyjednávání klientských certifikátů, a obě by se měly nastavit. První je v netsh.exe http add sslcert clientcertnegotation=enable/disable části . Tento příznak označuje, jestli se má klientský certifikát na začátku připojení nesnížovat, a měl by být nastavený na pro disable volitelné klientské certifikáty. Podrobnosti najdete v dokumentu netsh.

Druhé nastavení je ClientCertificateMethod . Pokud je nastavená na hodnotu , je možné během žádosti AllowRenegotation znovu projednat klientský certifikát.

POZNÁMKA: Aplikace by měla před pokusem o znovusjednání do vyrovnávací paměti nebo spotřebovat jakákoli data textu požadavku, jinak se může stát, že požadavek přestane reagovat.

Aplikace může nejprve zkontrolovat ClientCertificate vlastnost a zjistit, jestli je certifikát k dispozici. Pokud není k dispozici, před voláním pro vyjednání se ujistěte, že se spotřeboval GetClientCertificateAsync text požadavku. Poznámka: Pokud klient odmítne certifikát zadat, může GetClientCertificateAsync vrátit nulový certifikát.

POZNÁMKA: Chování vlastnosti se ClientCertificate v rozhraní .NET 6 změnilo, a podívejte se, https://github.com/aspnet/Announcements/issues/466 jestli pracujete s předchozími verzemi.

Existuje známý problém, kdy povolení může způsobit synchronní vyjednávání při AllowRenegotation přístupu k ClientCertificate vlastnosti. Pokud se GetClientCertificateAsync tomu chcete vyhnout, zavolejte metodu . Tento problém byl vyřešen v rozhraní .NET 6, viz https://github.com/aspnet/Announcements/issues/466 . Poznámka: Pokud klient odmítne certifikát zadat, může GetClientCertificateAsync vrátit nulový certifikát.

Kestrel

Kestrel řídí vyjednávání klientských certifikátů pomocí ClientCertificateMode možnosti .

Kestrel.Https.ClientCertificateMode.DelayCertificate je nová možnost dostupná v rozhraní .NET 6 nebo novějším. Při nastavení může aplikace zkontrolovat vlastnost ClientCertificate a zjistit, jestli je certifikát dostupný. Pokud není k dispozici, před voláním metody pro vyjednání se ujistěte, že se spotřeboval GetClientCertificateAsync text požadavku. Poznámka: Pokud klient odmítne certifikát zadat, může GetClientCertificateAsync vrátit nulový certifikát.

POZNÁMKA: Aplikace by před pokusem o znovusouzení měla do vyrovnávací paměti nebo spotřebovat jakákoli data textu požadavku, jinak GetClientCertificateAsync může vyvolat InvalidOperationExeption: Client stream needs to be drained before renegotiation. výjimku .

Pokud programově konfigurujete nastavení protokolu TLS na hostitele, je v .NET 6 a novějším k dispozici nové přetížení UseHttps, které přebírá a řídí opětovné vyjednávání klientských certifikátů Server.Kestrel.Https.TlsHandshakeCallbackOptions prostřednictvím Kestrel.Https.TlsHandshakeCallbackContext.AllowDelayedClientCertificateNegotation .

Pro .NET 5 a starší nepodporuje po zahájení připojení nové vyjednávání za účelem Kestrel získání klientského certifikátu. Tato funkce byla přidána v .NET 6.

Dotazy, komentáře a další připomínky k volitelným klientským certifikátům zanechte v tomto GitHub diskusi.