Konfigurowanie uwierzytelniania certyfikatów w programie ASP.NET Core
Microsoft.AspNetCore.Authentication.Certificate
zawiera implementację podobną do uwierzytelniania certyfikatów dla ASP.NET Core. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Dokładniej jest to procedura obsługi uwierzytelniania, która weryfikuje certyfikat, a następnie daje zdarzenie, w którym można rozpoznać ten certyfikat jako ClaimsPrincipal
.
Serwer należy skonfigurować pod kątem uwierzytelniania certyfikatów, niezależnie od tego, czy są to usługi IIS, Kestrel, Azure Web Apps, czy też inne, których używasz.
Scenariusze serwera proxy i modułu równoważenia obciążenia
Uwierzytelnianie certyfikatu jest scenariuszem stanowym używanym głównie w przypadku, gdy serwer proxy lub moduł równoważenia obciążenia nie obsługuje ruchu między klientami i serwerami. Jeśli używany jest serwer proxy lub moduł równoważenia obciążenia, uwierzytelnianie certyfikatu działa tylko wtedy, gdy serwer proxy lub moduł równoważenia obciążenia:
- Obsługuje uwierzytelnianie.
- Przekazuje informacje o uwierzytelnianiu użytkownika do aplikacji (na przykład w nagłówku żądania), która działa na podstawie informacji uwierzytelniania.
Alternatywą dla uwierzytelniania certyfikatów w środowiskach, w których są używane serwery proxy i moduły równoważenia obciążenia, jest usługa Active Directory Federated Services (ADFS) z protokołem OpenID Connect (OIDC).
Rozpocznij
Uzyskaj certyfikat HTTPS, zastosuj go i skonfiguruj serwer tak, aby wymagał certyfikatów.
W aplikacji internetowej:
- Dodaj odwołanie do pakietu NuGet Microsoft.AspNetCore.Authentication.Certificate .
- W
Program.cs
pliku wywołaj metodębuilder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...);
. Podaj pełnomocnika,OnCertificateValidated
aby wykonać dodatkową walidację certyfikatu klienta wysłanego z żądaniami. Zmień te informacje naClaimsPrincipal
wartość i ustaw ją wecontext.Principal
właściwości .
Jeśli uwierzytelnianie zakończy się niepowodzeniem 403 (Forbidden)
, ta procedura obsługi zwróci odpowiedź raczej jako 401 (Unauthorized)
, jak można się spodziewać. Przyczyną jest to, że uwierzytelnianie powinno nastąpić podczas początkowego połączenia TLS. Do czasu dotarcia do programu obsługi jest za późno. Nie ma możliwości uaktualnienia połączenia z połączenia anonimowego do jednego z certyfikatem.
UseAuthentication
parametr jest wymagany do ustawienia HttpContext.User
wartości utworzonej ClaimsPrincipal
na podstawie certyfikatu. Na przykład:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate();
var app = builder.Build();
app.UseAuthentication();
app.MapGet("/", () => "Hello World!");
app.Run();
W poprzednim przykładzie pokazano domyślny sposób dodawania uwierzytelniania certyfikatu. Procedura obsługi konstruuje podmiot zabezpieczeń użytkownika przy użyciu typowych właściwości certyfikatu.
Konfigurowanie weryfikacji certyfikatu
Procedura CertificateAuthenticationOptions
obsługi ma pewne wbudowane weryfikacje, które są minimalnymi weryfikacjami, które należy wykonać na certyfikacie. Każde z tych ustawień jest domyślnie włączone.
AllowedCertificateTypes = Chained, SelfSigned lub All (Łańcuchowe | SelfSigned)
Wartość domyślna: CertificateTypes.Chained
To sprawdzenie sprawdza, czy dozwolony jest tylko odpowiedni typ certyfikatu. Jeśli aplikacja korzysta z certyfikatów z podpisem własnym, ta opcja musi być ustawiona na CertificateTypes.All
lub CertificateTypes.SelfSigned
.
ChainTrustValidationMode
Wartość domyślna: X509ChainTrustMode.System
Certyfikat przedstawiony przez klienta musi połączyć łańcuch z zaufanym certyfikatem głównym. To sprawdzanie steruje magazynem zaufania zawierającym te certyfikaty główne.
Domyślnie program obsługi używa magazynu zaufania systemu. Jeśli przedstawiony certyfikat klienta musi połączyć łańcuch z certyfikatem głównym, który nie jest wyświetlany w magazynie zaufania systemu, tę opcję można ustawić na X509ChainTrustMode.CustomRootTrustTrust , aby program obsługi używał elementu CustomTrustStore
.
CustomTrustStore
Wartość domyślna: Pusta X509Certificate2Collection
Jeśli właściwość programu obsługi ChainTrustValidationMode jest ustawiona na X509ChainTrustMode.CustomRootTrust
wartość , X509Certificate2Collection zawiera każdy certyfikat, który będzie używany do weryfikowania certyfikatu klienta do zaufanego katalogu głównego, w tym zaufanego katalogu głównego.
Gdy klient przedstawia certyfikat, który jest częścią łańcucha certyfikatów wielowymiarowych, CustomTrustStore
musi zawierać każdy certyfikat wystawiający w łańcuchu.
ValidateCertificateUse
Wartość domyślna: true
To sprawdzenie sprawdza, czy certyfikat przedstawiony przez klienta ma rozszerzone użycie klucza uwierzytelniania klienta (EKU) lub w ogóle nie ma jednostek EKU. Jak mówią specyfikacje, jeśli nie określono EKU, wszystkie EKU są uznawane za prawidłowe.
ValidateValidityPeriod
Wartość domyślna: true
Ta kontrola sprawdza, czy certyfikat znajduje się w okresie ważności. W każdym żądaniu program obsługi gwarantuje, że certyfikat, który był ważny, gdy został przedstawiony, nie wygasł podczas bieżącej sesji.
OdwołanieFlag
Wartość domyślna: X509RevocationFlag.ExcludeRoot
Flaga określająca, które certyfikaty w łańcuchu są sprawdzane pod kątem odwołania.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Odwołajmode
Wartość domyślna: X509RevocationMode.Online
Flaga określająca sposób przeprowadzania kontroli odwołania.
Określenie sprawdzania online może spowodować duże opóźnienie podczas kontaktowania się z urzędem certyfikacji.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Czy mogę skonfigurować aplikację tak, aby wymagała certyfikatu tylko w określonych ścieżkach?
Nie jest to możliwe. Pamiętaj, że wymiana certyfikatów odbywa się na początku konwersacji HTTPS, jest wykonywana przez serwer przed odebraniem pierwszego żądania na tym połączeniu, aby nie można było ograniczyć zakresu na podstawie pól żądania.
Zdarzenia programu obsługi
Procedura obsługi ma dwa zdarzenia:
OnAuthenticationFailed
: Wywoływane, jeśli podczas uwierzytelniania wystąpi wyjątek i można zareagować.OnCertificateValidated
: Wywoływany po zweryfikowaniu certyfikatu, pomyślnie zweryfikowano walidację i utworzono domyślną jednostkę zabezpieczeń. To zdarzenie umożliwia przeprowadzenie własnej weryfikacji i rozszerzenie lub zastąpienie podmiotu zabezpieczeń. Przykłady obejmują:Określanie, czy certyfikat jest znany usługom.
Konstruowanie własnego podmiotu zabezpieczeń. Rozważmy następujący przykład:
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; } }; });
Jeśli okaże się, że certyfikat przychodzący nie spełnia dodatkowej weryfikacji, wywołaj context.Fail("failure reason")
metodę z przyczyną niepowodzenia.
Aby uzyskać lepszą funkcjonalność, wywołaj usługę zarejestrowaną w iniekcji zależności, która łączy się z bazą danych lub innym typem magazynu użytkowników. Uzyskaj dostęp do usługi przy użyciu kontekstu przekazanego do delegata. Rozważmy następujący przykład:
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;
}
};
});
Koncepcyjnie weryfikacja certyfikatu jest problemem autoryzacji. Dodanie sprawdzania, na przykład wystawcy lub odcisku palca w zasadach autoryzacji, a nie wewnątrz OnCertificateValidated
elementu , jest całkowicie akceptowalne.
Konfigurowanie serwera w celu wymagania certyfikatów
Kestrel
W Program.cs
programie skonfiguruj Kestrel w następujący sposób:
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.ConfigureHttpsDefaults(options =>
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});
Uwaga
Punkty końcowe utworzone przez wywołanie przed wywołaniem ConfigureHttpsDefaults Listen nie będą miały zastosowanych wartości domyślnych.
IIS
Wykonaj następujące kroki w Menedżerze usług IIS:
- Wybierz swoją witrynę na karcie Połączenia .
- Kliknij dwukrotnie opcję Ustawienia protokołu SSL w oknie Widok funkcji.
- Zaznacz pole wyboru Wymagaj protokołu SSL i wybierz przycisk radiowy Wymagaj w sekcji Certyfikaty klienta.
Azure i niestandardowe serwery proxy sieci Web
Zapoznaj się z dokumentacją dotyczącą hosta i wdrażania, aby dowiedzieć się, jak skonfigurować oprogramowanie pośredniczące przekazujące certyfikaty.
Używanie uwierzytelniania certyfikatów w usłudze Azure Web Apps
Dla platformy Azure nie jest wymagana żadna konfiguracja przekazywania dalej. Konfiguracja przekazywania jest konfigurowana przez oprogramowanie pośredniczące przekazywania certyfikatów.
Uwaga
Oprogramowanie pośredniczące przekazywania certyfikatów jest wymagane w tym scenariuszu.
Aby uzyskać więcej informacji, zobacz Używanie certyfikatu TLS/SSL w kodzie w usłudze aplikacja systemu Azure (dokumentacja platformy Azure).
Używanie uwierzytelniania certyfikatów w niestandardowych internetowych serwerach proxy
Metoda służy do określania AddCertificateForwarding
:
- Nazwa nagłówka klienta.
- Sposób ładowania certyfikatu (przy użyciu
HeaderConverter
właściwości ).
W niestandardowych internetowych serwerach proxy certyfikat jest przekazywany jako niestandardowy nagłówek żądania, na przykład X-SSL-CERT
. Aby go użyć, skonfiguruj przekazywanie certyfikatów w programie 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;
}
};
});
Jeśli aplikacja jest odwrotnie proxied przez serwer NGINX z konfiguracją proxy_set_header ssl-client-cert $ssl_client_escaped_cert
lub wdrożona na platformie Kubernetes przy użyciu ruchu przychodzącego NGINX, certyfikat klienta jest przekazywany do aplikacji w postaci zakodowanej pod adresem URL. Aby użyć certyfikatu, zdekoduj go w następujący sposób:
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!;
};
});
Dodaj oprogramowanie pośredniczące w pliku Program.cs
. UseCertificateForwarding
parametr jest wywoływany przed wywołaniami i UseAuthentication
UseAuthorization
:
var app = builder.Build();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
Oddzielna klasa może służyć do implementowania logiki walidacji. Ponieważ w tym przykładzie jest używany ten sam certyfikat z podpisem własnym, upewnij się, że można używać tylko certyfikatu. Sprawdź, czy odciski palca certyfikatu klienta i certyfikatu serwera są zgodne, w przeciwnym razie można użyć dowolnego certyfikatu i będzie wystarczający do uwierzytelnienia. Będzie to używane wewnątrz AddCertificate
metody . Możesz również zweryfikować podmiot lub wystawcę w tym miejscu, jeśli używasz certyfikatów pośrednich lub podrzędnych.
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;
}
}
Implementowanie klienta HttpClient przy użyciu certyfikatu i IHttpClientFactory
W poniższym przykładzie certyfikat klienta jest dodawany do obiektu używającego HttpClientHandler
ClientCertificates
właściwości z programu obsługi. Ta procedura obsługi może być następnie używana w nazwanym wystąpieniu HttpClient
ConfigurePrimaryHttpMessageHandler obiektu przy użyciu metody . Jest to konfiguracja w programie 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;
});
Następnie IHttpClientFactory
można go użyć do pobrania nazwanego wystąpienia za pomocą programu obsługi i certyfikatu. Metoda CreateClient
o nazwie klienta zdefiniowanego w Program.cs
programie służy do pobierania wystąpienia. Żądanie HTTP można wysłać przy użyciu klienta zgodnie z wymaganiami:
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}");
}
}
Jeśli prawidłowy certyfikat jest wysyłany do serwera, dane są zwracane. Jeśli żaden certyfikat lub nieprawidłowy certyfikat nie zostanie wysłany, zostanie zwrócony kod stanu HTTP 403.
Tworzenie certyfikatów w programie PowerShell
Tworzenie certyfikatów jest najtrudniejszą częścią konfigurowania tego przepływu. Certyfikat główny można utworzyć przy użyciu New-SelfSignedCertificate
polecenia cmdlet programu PowerShell. Podczas tworzenia certyfikatu użyj silnego hasła. Ważne jest dodanie parametru KeyUsageProperty
i parametru KeyUsage
, jak pokazano poniżej.
Tworzenie głównego urzędu certyfikacji
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
Uwaga
Wartość parametru musi być zgodna -DnsName
z docelowym wdrożeniem aplikacji. Na przykład "localhost" na potrzeby programowania.
Instalowanie w zaufanym katalogu głównym
Certyfikat główny musi być zaufany w systemie hosta. Domyślnie zaufane są tylko certyfikaty główne utworzone przez urząd certyfikacji. Aby uzyskać informacje na temat zaufania certyfikatowi głównemu w systemie Windows, zobacz dokumentację systemu Windows lub Import-Certificate
polecenie cmdlet programu PowerShell.
Certyfikat pośredni
Certyfikat pośredniczący można teraz utworzyć na podstawie certyfikatu głównego. Nie jest to wymagane w przypadku wszystkich przypadków użycia, ale może być konieczne utworzenie wielu certyfikatów lub konieczność aktywowania lub wyłączania grup certyfikatów. Parametr TextExtension
jest wymagany do ustawienia długości ścieżki w podstawowych ograniczeniach certyfikatu.
Certyfikat pośredniczący można następnie dodać do zaufanego certyfikatu pośredniego w systemie hosta systemu Windows.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu pośredniego
Certyfikat podrzędny można utworzyć na podstawie certyfikatu pośredniego. Jest to jednostka końcowa i nie musi tworzyć większej liczby certyfikatów podrzędnych.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu głównego
Certyfikat podrzędny można również utworzyć bezpośrednio na podstawie certyfikatu głównego.
$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
Przykładowy certyfikat główny — certyfikat pośredni — certyfikat
$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
W przypadku korzystania z certyfikatów głównych, pośrednich lub podrzędnych certyfikaty można zweryfikować przy użyciu odcisku palca lub klucza publicznego zgodnie z wymaganiami:
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);
}
Buforowanie weryfikacji certyfikatu
ASP.NET Core 5.0 i nowsze wersje obsługują możliwość włączania buforowania wyników weryfikacji. Buforowanie znacznie poprawia wydajność uwierzytelniania certyfikatu, ponieważ walidacja jest kosztowną operacją.
Domyślnie uwierzytelnianie certyfikatu wyłącza buforowanie. Aby włączyć buforowanie, wywołaj metodę AddCertificateCache
w pliku Program.cs
:
builder.Services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate()
.AddCertificateCache(options =>
{
options.CacheSize = 1024;
options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
});
Domyślna implementacja buforowania przechowuje wyniki w pamięci. Możesz udostępnić własną pamięć podręczną, implementując ICertificateValidationCache
i rejestrując ją za pomocą iniekcji zależności. Na przykład services.AddSingleton<ICertificateValidationCache, YourCache>()
.
Opcjonalne certyfikaty klienta
Ta sekcja zawiera informacje dotyczące aplikacji, które muszą chronić podzbiór aplikacji przy użyciu certyfikatu. Na przykład Razor strona lub kontroler w aplikacji może wymagać certyfikatów klienta. Stanowi to wyzwania związane z certyfikatami klienta:
- To funkcja PROTOKOŁU TLS, a nie funkcja HTTP.
- Są negocjowane dla połączenia i zwykle na początku połączenia przed udostępnieniem jakichkolwiek danych HTTP.
Istnieją dwa podejścia do implementowania opcjonalnych certyfikatów klienta:
- Używanie oddzielnych nazw hostów (SNI) i przekierowywanie. Chociaż więcej pracy należy skonfigurować, jest to zalecane, ponieważ działa w większości środowisk i protokołów.
- Renegocjacja podczas żądania HTTP. Ma to kilka ograniczeń i nie jest zalecane.
Oddzielne hosty (SNI)
Na początku połączenia znana jest tylko nazwa nazwy serwera (SNI) †. Certyfikaty klienta można skonfigurować na nazwę hosta, tak aby jeden host ich wymagał, a inny nie.
- Skonfiguruj powiązanie dla domeny i poddomeny:
- Na przykład skonfiguruj powiązania w systemach
contoso.com
imyClient.contoso.com
.contoso.com
Host nie wymaga certyfikatu klienta, alemyClient.contoso.com
nie. - Aby uzyskać więcej informacji, zobacz:
- Na przykład skonfiguruj powiązania w systemach
ASP.NET Core 5 i nowszych dodaje wygodniejsze obsługę przekierowywania w celu uzyskania opcjonalnych certyfikatów klienta. Aby uzyskać więcej informacji, zobacz przykład Opcjonalne certyfikaty.
- W przypadku żądań do aplikacji internetowej, które wymagają certyfikatu klienta i których nie ma:
- Przekieruj do tej samej strony przy użyciu poddomeny chronionej certyfikatem klienta.
- Na przykład przekieruj do
myClient.contoso.com/requestedPage
. Ponieważ żądanie domyClient.contoso.com/requestedPage
jest inną nazwą hosta niżcontoso.com/requestedPage
, klient ustanawia inne połączenie, a certyfikat klienta jest dostarczany. - Aby uzyskać więcej informacji, zobacz Wprowadzenie do autoryzacji w programie ASP.NET Core.
† oznaczanie nazwy serwera (SNI) to rozszerzenie protokołu TLS do uwzględnienia domeny wirtualnej w ramach negocjacji protokołu SSL. Oznacza to, że nazwa domeny wirtualnej lub nazwa hosta może służyć do identyfikowania punktu końcowego sieci.
Renegocjacji
Renegocjacja protokołu TLS to proces, za pomocą którego klient i serwer mogą ponownie ocenić wymagania dotyczące szyfrowania dla pojedynczego połączenia, w tym żądania certyfikatu klienta, jeśli nie zostały wcześniej podane. Renegocjacja protokołu TLS jest zagrożeniem bezpieczeństwa i nie jest zalecana, ponieważ:
- W przypadku protokołu HTTP/1.1 serwer musi najpierw buforować lub używać wszelkich danych HTTP, które są w locie, takich jak treść żądania POST, aby upewnić się, że połączenie jest jasne dla renegocjacji. W przeciwnym razie renegocjacja może przestać odpowiadać lub zakończyć się niepowodzeniem.
- Protokół HTTP/2 i HTTP/3 jawnie uniemożliwiają renegocjację.
- Istnieją zagrożenia bezpieczeństwa związane z renegocjacją. Protokół TLS 1.3 usunął renegocjację całego połączenia i zastąpił go nowym rozszerzeniem żądania tylko certyfikatu klienta po rozpoczęciu połączenia. Ten mechanizm jest ujawniany za pośrednictwem tych samych interfejsów API i nadal podlega wcześniejszym ograniczeniom buforowania i wersji protokołu HTTP.
Implementacja i konfiguracja tej funkcji różni się w zależności od wersji serwera i platformy.
IIS
Usługi IIS zarządzają negocjowaniem certyfikatu klienta w Twoim imieniu. Podsekcja aplikacji może umożliwić SslRequireCert
negocjowanie certyfikatu klienta dla tych żądań. Aby uzyskać szczegółowe informacje, zobacz Konfiguracja w dokumentacji usług IIS.
Usługi IIS automatycznie buforują wszystkie dane treści żądania do skonfigurowanego limitu rozmiaru przed ponownym negocjowaniem. Żądania przekraczające limit są odrzucane z odpowiedzią 413. Ten limit jest domyślnie ustawiony na 48 KB i można go skonfigurować przez ustawienie parametru uploadReadAheadSize.
HttpSys
Protokół HttpSys ma dwa ustawienia, które kontrolują negocjacje certyfikatu klienta i oba powinny być ustawione. Pierwszy element znajduje się w netsh.exe w obszarze http add sslcert clientcertnegotiation=enable/disable
. Ta flaga wskazuje, czy certyfikat klienta powinien być negocjowany na początku połączenia i powinien być ustawiony disable
na wartość dla opcjonalnych certyfikatów klienta. Aby uzyskać szczegółowe informacje, zobacz dokumentację netsh .
Drugie ustawienie to ClientCertificateMethod. Po ustawieniu AllowRenegotation
wartości na wartość certyfikat klienta można renegocjować podczas żądania.
UWAGA Aplikacja powinna buforować lub używać danych treści żądania przed podjęciem próby renegocjacji. W przeciwnym razie żądanie może nie odpowiadać.
Aplikacja może najpierw sprawdzić ClientCertificate właściwość, aby sprawdzić, czy certyfikat jest dostępny. Jeśli nie jest dostępna, upewnij się, że treść żądania została zużyta przed wywołaniem GetClientCertificateAsync metody negocjowania. Uwaga GetClientCertificateAsync
może zwrócić certyfikat o wartości null, jeśli klient odmówi podania certyfikatu.
UWAGA Zachowanie ClientCertificate
właściwości zmieniło się na platformie .NET 6. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.
Kestrel
Kestrel kontroluje negocjacje certyfikatu klienta z opcją ClientCertificateMode .
ClientCertificateMode.DelayCertificate jest nową opcją dostępną na platformie .NET 6 lub nowszym. Po ustawieniu aplikacja może sprawdzić ClientCertificate właściwość, aby sprawdzić, czy certyfikat jest dostępny. Jeśli nie jest dostępna, upewnij się, że treść żądania została zużyta przed wywołaniem GetClientCertificateAsync metody negocjowania. Uwaga GetClientCertificateAsync
może zwrócić certyfikat o wartości null, jeśli klient odmówi podania certyfikatu.
UWAGA Aplikacja powinna buforować lub używać danych treści żądania przed podjęciem próby ponownego negocjowania. W przeciwnym razie GetClientCertificateAsync
może zostać zgłoszony komunikat InvalidOperationException: Client stream needs to be drained before renegotiation.
.
Jeśli konfigurujesz programowo ustawienia protokołu TLS dla nazwy hosta SNI, wywołaj UseHttps
przeciążenie (.NET 6 lub nowsze), które przyjmuje TlsHandshakeCallbackOptions i kontroluje renegocjację certyfikatu klienta za pośrednictwem programu TlsHandshakeCallbackContext.AllowDelayedClientCertificateNegotation.
Microsoft.AspNetCore.Authentication.Certificate
zawiera implementację podobną do uwierzytelniania certyfikatów dla ASP.NET Core. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Dokładniej jest to procedura obsługi uwierzytelniania, która weryfikuje certyfikat, a następnie daje zdarzenie, w którym można rozpoznać ten certyfikat jako ClaimsPrincipal
.
Skonfiguruj serwer pod kątem uwierzytelniania certyfikatów, niezależnie od tego, czy jest to usługa IIS, Kestrelusługa Azure Web Apps, czy cokolwiek innego, którego używasz.
Scenariusze serwera proxy i modułu równoważenia obciążenia
Uwierzytelnianie certyfikatu jest scenariuszem stanowym używanym głównie w przypadku, gdy serwer proxy lub moduł równoważenia obciążenia nie obsługuje ruchu między klientami i serwerami. Jeśli używany jest serwer proxy lub moduł równoważenia obciążenia, uwierzytelnianie certyfikatu działa tylko wtedy, gdy serwer proxy lub moduł równoważenia obciążenia:
- Obsługuje uwierzytelnianie.
- Przekazuje informacje o uwierzytelnianiu użytkownika do aplikacji (na przykład w nagłówku żądania), która działa na podstawie informacji uwierzytelniania.
Alternatywą dla uwierzytelniania certyfikatów w środowiskach, w których są używane serwery proxy i moduły równoważenia obciążenia, jest usługa Active Directory Federated Services (ADFS) z protokołem OpenID Connect (OIDC).
Rozpocznij
Uzyskaj certyfikat HTTPS, zastosuj go i skonfiguruj serwer tak, aby wymagał certyfikatów.
W aplikacji internetowej dodaj odwołanie do pakietu Microsoft.AspNetCore.Authentication.Certificate . Następnie w metodzie Startup.ConfigureServices
wywołaj metodę services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...);
z opcjami, podając pełnomocnika do OnCertificateValidated
wykonania dodatkowej weryfikacji certyfikatu klienta wysłanego z żądaniami. Zmień te informacje na ClaimsPrincipal
wartość i ustaw ją we context.Principal
właściwości .
Jeśli uwierzytelnianie zakończy się niepowodzeniem 403 (Forbidden)
, ta procedura obsługi zwróci odpowiedź raczej jako 401 (Unauthorized)
, jak można się spodziewać. Przyczyną jest to, że uwierzytelnianie powinno nastąpić podczas początkowego połączenia TLS. Do czasu dotarcia do programu obsługi jest za późno. Nie ma możliwości uaktualnienia połączenia z połączenia anonimowego do jednego z certyfikatem.
app.UseAuthentication();
Dodaj również metodę Startup.Configure
. W przeciwnym razie nie zostanie ustawiona HttpContext.User
wartość ClaimsPrincipal
utworzona na podstawie certyfikatu. Na przykład:
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
}
W poprzednim przykładzie pokazano domyślny sposób dodawania uwierzytelniania certyfikatu. Procedura obsługi konstruuje podmiot zabezpieczeń użytkownika przy użyciu typowych właściwości certyfikatu.
Konfigurowanie weryfikacji certyfikatu
Procedura CertificateAuthenticationOptions
obsługi ma pewne wbudowane weryfikacje, które są minimalnymi weryfikacjami, które należy wykonać na certyfikacie. Każde z tych ustawień jest domyślnie włączone.
AllowedCertificateTypes = Chained, SelfSigned lub All (Łańcuchowe | SelfSigned)
Wartość domyślna: CertificateTypes.Chained
To sprawdzenie sprawdza, czy dozwolony jest tylko odpowiedni typ certyfikatu. Jeśli aplikacja korzysta z certyfikatów z podpisem własnym, ta opcja musi być ustawiona na CertificateTypes.All
lub CertificateTypes.SelfSigned
.
ValidateCertificateUse
Wartość domyślna: true
To sprawdzenie sprawdza, czy certyfikat przedstawiony przez klienta ma rozszerzone użycie klucza uwierzytelniania klienta (EKU) lub w ogóle nie ma jednostek EKU. Jak mówią specyfikacje, jeśli nie określono EKU, wszystkie EKU są uznawane za prawidłowe.
ValidateValidityPeriod
Wartość domyślna: true
Ta kontrola sprawdza, czy certyfikat znajduje się w okresie ważności. W każdym żądaniu program obsługi gwarantuje, że certyfikat, który był ważny, gdy został przedstawiony, nie wygasł podczas bieżącej sesji.
OdwołanieFlag
Wartość domyślna: X509RevocationFlag.ExcludeRoot
Flaga określająca, które certyfikaty w łańcuchu są sprawdzane pod kątem odwołania.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Odwołajmode
Wartość domyślna: X509RevocationMode.Online
Flaga określająca sposób przeprowadzania kontroli odwołania.
Określenie sprawdzania online może spowodować duże opóźnienie podczas kontaktowania się z urzędem certyfikacji.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Czy mogę skonfigurować aplikację tak, aby wymagała certyfikatu tylko w określonych ścieżkach?
Nie jest to możliwe. Pamiętaj, że wymiana certyfikatów odbywa się na początku konwersacji HTTPS, jest wykonywana przez serwer przed odebraniem pierwszego żądania na tym połączeniu, aby nie można było ograniczyć zakresu na podstawie pól żądania.
Zdarzenia programu obsługi
Procedura obsługi ma dwa zdarzenia:
OnAuthenticationFailed
: Wywoływane, jeśli podczas uwierzytelniania wystąpi wyjątek i można zareagować.OnCertificateValidated
: Wywoływany po zweryfikowaniu certyfikatu, pomyślnie zweryfikowano walidację i utworzono domyślną jednostkę zabezpieczeń. To zdarzenie umożliwia przeprowadzenie własnej weryfikacji i rozszerzenie lub zastąpienie podmiotu zabezpieczeń. Przykłady obejmują:Określanie, czy certyfikat jest znany usługom.
Konstruowanie własnego podmiotu zabezpieczeń. Rozważmy następujący przykład w pliku
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; } }; });
Jeśli okaże się, że certyfikat przychodzący nie spełnia dodatkowej weryfikacji, wywołaj context.Fail("failure reason")
metodę z przyczyną niepowodzenia.
W przypadku rzeczywistych funkcji prawdopodobnie chcesz wywołać usługę zarejestrowaną we wstrzyknięciu zależności, która łączy się z bazą danych lub innym typem magazynu użytkowników. Uzyskaj dostęp do usługi przy użyciu kontekstu przekazanego do pełnomocnika. Rozważmy następujący przykład w pliku 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;
}
};
});
Koncepcyjnie weryfikacja certyfikatu jest problemem autoryzacji. Dodanie sprawdzania, na przykład wystawcy lub odcisku palca w zasadach autoryzacji, a nie wewnątrz OnCertificateValidated
elementu , jest całkowicie akceptowalne.
Konfigurowanie serwera w celu wymagania certyfikatów
Kestrel
W Program.cs
programie skonfiguruj Kestrel w następujący sposób:
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);
});
});
}
Uwaga
Punkty końcowe utworzone przez wywołanie przed wywołaniem ConfigureHttpsDefaults Listen nie będą miały zastosowanych wartości domyślnych.
IIS
Wykonaj następujące kroki w Menedżerze usług IIS:
- Wybierz swoją witrynę na karcie Połączenia .
- Kliknij dwukrotnie opcję Ustawienia protokołu SSL w oknie Widok funkcji.
- Zaznacz pole wyboru Wymagaj protokołu SSL i wybierz przycisk radiowy Wymagaj w sekcji Certyfikaty klienta.
Azure i niestandardowe serwery proxy sieci Web
Zapoznaj się z dokumentacją dotyczącą hosta i wdrażania, aby dowiedzieć się, jak skonfigurować oprogramowanie pośredniczące przekazujące certyfikaty.
Używanie uwierzytelniania certyfikatów w usłudze Azure Web Apps
Dla platformy Azure nie jest wymagana żadna konfiguracja przekazywania dalej. Konfiguracja przekazywania jest konfigurowana przez oprogramowanie pośredniczące przekazywania certyfikatów.
Uwaga
Oprogramowanie pośredniczące przekazywania certyfikatów jest wymagane w tym scenariuszu.
Aby uzyskać więcej informacji, zobacz Używanie certyfikatu TLS/SSL w kodzie w usłudze aplikacja systemu Azure (dokumentacja platformy Azure).
Używanie uwierzytelniania certyfikatów w niestandardowych internetowych serwerach proxy
Metoda służy do określania AddCertificateForwarding
:
- Nazwa nagłówka klienta.
- Sposób ładowania certyfikatu (przy użyciu
HeaderConverter
właściwości ).
W niestandardowych internetowych serwerach proxy certyfikat jest przekazywany jako niestandardowy nagłówek żądania, na przykład X-SSL-CERT
. Aby go użyć, skonfiguruj przekazywanie certyfikatów w programie 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;
}
Jeśli aplikacja jest odwrotnie proxied przez serwer NGINX z konfiguracją proxy_set_header ssl-client-cert $ssl_client_escaped_cert
lub wdrożona na platformie Kubernetes przy użyciu ruchu przychodzącego NGINX, certyfikat klienta jest przekazywany do aplikacji w postaci zakodowanej pod adresem URL. Aby użyć certyfikatu, zdekoduj go w następujący sposób:
In Startup.ConfigureServices
(Startup.cs
):
services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "ssl-client-cert";
options.HeaderConverter = (headerValue) =>
{
X509Certificate2 clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
string certPem = WebUtility.UrlDecode(headerValue);
clientCertificate = X509Certificate2.CreateFromPem(certPem);
}
return clientCertificate;
};
});
Następnie Startup.Configure
metoda dodaje oprogramowanie pośredniczące. UseCertificateForwarding
parametr jest wywoływany przed wywołaniami i UseAuthentication
UseAuthorization
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Oddzielna klasa może służyć do implementowania logiki walidacji. Ponieważ w tym przykładzie jest używany ten sam certyfikat z podpisem własnym, upewnij się, że można używać tylko certyfikatu. Sprawdź, czy odciski palca certyfikatu klienta i certyfikatu serwera są zgodne, w przeciwnym razie można użyć dowolnego certyfikatu i będzie wystarczający do uwierzytelnienia. Będzie to używane wewnątrz AddCertificate
metody . Możesz również zweryfikować podmiot lub wystawcę w tym miejscu, jeśli używasz certyfikatów pośrednich lub podrzędnych.
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;
}
}
}
Implementowanie klienta HttpClient przy użyciu certyfikatu i programu HttpClientHandler
Element HttpClientHandler
można dodać bezpośrednio w konstruktorze HttpClient
klasy . Należy zachować ostrożność podczas tworzenia wystąpień klasy HttpClient
. Następnie HttpClient
program wyśle certyfikat z każdym żądaniem.
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}");
}
Implementowanie klienta HttpClient przy użyciu certyfikatu i nazwanego obiektu HttpClient z elementu IHttpClientFactory
W poniższym przykładzie certyfikat klienta jest dodawany do obiektu używającego HttpClientHandler
ClientCertificates
właściwości z programu obsługi. Ta procedura obsługi może być następnie używana w nazwanym wystąpieniu HttpClient
ConfigurePrimaryHttpMessageHandler obiektu przy użyciu metody . Jest to konfiguracja w programie 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;
});
Następnie IHttpClientFactory
można go użyć do pobrania nazwanego wystąpienia za pomocą programu obsługi i certyfikatu. Metoda CreateClient
o nazwie klienta zdefiniowanego Startup
w klasie służy do pobierania wystąpienia. Żądanie HTTP można wysłać przy użyciu klienta zgodnie z potrzebami.
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}");
}
Jeśli prawidłowy certyfikat jest wysyłany do serwera, dane są zwracane. Jeśli żaden certyfikat lub nieprawidłowy certyfikat nie zostanie wysłany, zostanie zwrócony kod stanu HTTP 403.
Tworzenie certyfikatów w programie PowerShell
Tworzenie certyfikatów jest najtrudniejszą częścią konfigurowania tego przepływu. Certyfikat główny można utworzyć przy użyciu New-SelfSignedCertificate
polecenia cmdlet programu PowerShell. Podczas tworzenia certyfikatu użyj silnego hasła. Ważne jest dodanie parametru KeyUsageProperty
i parametru KeyUsage
, jak pokazano poniżej.
Tworzenie głównego urzędu certyfikacji
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
Uwaga
Wartość parametru musi być zgodna -DnsName
z docelowym wdrożeniem aplikacji. Na przykład "localhost" na potrzeby programowania.
Instalowanie w zaufanym katalogu głównym
Certyfikat główny musi być zaufany w systemie hosta. Certyfikat główny, który nie został utworzony przez urząd certyfikacji, nie będzie domyślnie zaufany. Aby uzyskać informacje na temat zaufania certyfikatowi głównemu w systemie Windows, zobacz to pytanie.
Certyfikat pośredni
Certyfikat pośredniczący można teraz utworzyć na podstawie certyfikatu głównego. Nie jest to wymagane w przypadku wszystkich przypadków użycia, ale może być konieczne utworzenie wielu certyfikatów lub konieczność aktywowania lub wyłączania grup certyfikatów. Parametr TextExtension
jest wymagany do ustawienia długości ścieżki w podstawowych ograniczeniach certyfikatu.
Certyfikat pośredniczący można następnie dodać do zaufanego certyfikatu pośredniego w systemie hosta systemu Windows.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu pośredniego
Certyfikat podrzędny można utworzyć na podstawie certyfikatu pośredniego. Jest to jednostka końcowa i nie musi tworzyć większej liczby certyfikatów podrzędnych.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu głównego
Certyfikat podrzędny można również utworzyć bezpośrednio na podstawie certyfikatu głównego.
$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
Przykładowy certyfikat główny — certyfikat pośredni — certyfikat
$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
W przypadku używania certyfikatów głównych, pośrednich lub podrzędnych certyfikaty można zweryfikować przy użyciu odcisku palca lub klucza publicznego zgodnie z wymaganiami.
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;
}
}
}
Buforowanie weryfikacji certyfikatu
ASP.NET Core 5.0 i nowsze wersje obsługują możliwość włączania buforowania wyników weryfikacji. Buforowanie znacznie poprawia wydajność uwierzytelniania certyfikatu, ponieważ walidacja jest kosztowną operacją.
Domyślnie uwierzytelnianie certyfikatu wyłącza buforowanie. Aby włączyć buforowanie, wywołaj metodę AddCertificateCache
w pliku Startup.ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate()
.AddCertificateCache(options =>
{
options.CacheSize = 1024;
options.CacheEntryExpiration = TimeSpan.FromMinutes(2);
});
}
Domyślna implementacja buforowania przechowuje wyniki w pamięci. Możesz udostępnić własną pamięć podręczną, implementując ICertificateValidationCache
i rejestrując ją za pomocą iniekcji zależności. Na przykład services.AddSingleton<ICertificateValidationCache, YourCache>()
.
Opcjonalne certyfikaty klienta
Ta sekcja zawiera informacje dotyczące aplikacji, które muszą chronić podzbiór aplikacji przy użyciu certyfikatu. Na przykład Razor strona lub kontroler w aplikacji może wymagać certyfikatów klienta. Stanowi to wyzwania związane z certyfikatami klienta:
- To funkcja PROTOKOŁU TLS, a nie funkcja HTTP.
- Są negocjowane dla połączenia i zwykle na początku połączenia przed udostępnieniem jakichkolwiek danych HTTP.
Istnieją dwa podejścia do implementowania opcjonalnych certyfikatów klienta:
- Używanie oddzielnych nazw hostów (SNI) i przekierowywanie. Chociaż więcej pracy należy skonfigurować, jest to zalecane, ponieważ działa w większości środowisk i protokołów.
- Renegocjacja podczas żądania HTTP. Ma to kilka ograniczeń i nie jest zalecane.
Oddzielne hosty (SNI)
Na początku połączenia znana jest tylko nazwa nazwy serwera (SNI) †. Certyfikaty klienta można skonfigurować na nazwę hosta, tak aby jeden host ich wymagał, a inny nie.
- Skonfiguruj powiązanie dla domeny i poddomeny:
- Na przykład skonfiguruj powiązania w systemach
contoso.com
imyClient.contoso.com
.contoso.com
Host nie wymaga certyfikatu klienta, alemyClient.contoso.com
nie. - Aby uzyskać więcej informacji, zobacz:
- Na przykład skonfiguruj powiązania w systemach
ASP.NET Core 5 i nowszych dodaje wygodniejsze obsługę przekierowywania w celu uzyskania opcjonalnych certyfikatów klienta. Aby uzyskać więcej informacji, zobacz przykład Opcjonalne certyfikaty.
- W przypadku żądań do aplikacji internetowej, które wymagają certyfikatu klienta i których nie ma:
- Przekieruj do tej samej strony przy użyciu poddomeny chronionej certyfikatem klienta.
- Na przykład przekieruj do
myClient.contoso.com/requestedPage
. Ponieważ żądanie domyClient.contoso.com/requestedPage
jest inną nazwą hosta niżcontoso.com/requestedPage
, klient ustanawia inne połączenie, a certyfikat klienta jest dostarczany. - Aby uzyskać więcej informacji, zobacz Wprowadzenie do autoryzacji w programie ASP.NET Core.
† oznaczanie nazwy serwera (SNI) to rozszerzenie protokołu TLS do uwzględnienia domeny wirtualnej w ramach negocjacji protokołu SSL. Oznacza to, że nazwa domeny wirtualnej lub nazwa hosta może służyć do identyfikowania punktu końcowego sieci.
Renegocjacji
Renegocjacja protokołu TLS to proces, za pomocą którego klient i serwer mogą ponownie ocenić wymagania dotyczące szyfrowania dla pojedynczego połączenia, w tym żądania certyfikatu klienta, jeśli nie zostały wcześniej podane. Renegocjacja protokołu TLS jest zagrożeniem bezpieczeństwa i nie jest zalecana, ponieważ:
- W przypadku protokołu HTTP/1.1 serwer musi najpierw buforować lub używać wszelkich danych HTTP, które są w locie, takich jak treść żądania POST, aby upewnić się, że połączenie jest jasne dla renegocjacji. W przeciwnym razie renegocjacja może przestać odpowiadać lub zakończyć się niepowodzeniem.
- Protokół HTTP/2 i HTTP/3 jawnie uniemożliwiają renegocjację.
- Istnieją zagrożenia bezpieczeństwa związane z renegocjacją. Protokół TLS 1.3 usunął renegocjację całego połączenia i zastąpił go nowym rozszerzeniem żądania tylko certyfikatu klienta po rozpoczęciu połączenia. Ten mechanizm jest ujawniany za pośrednictwem tych samych interfejsów API i nadal podlega wcześniejszym ograniczeniom buforowania i wersji protokołu HTTP.
Implementacja i konfiguracja tej funkcji różni się w zależności od wersji serwera i platformy.
IIS
Usługi IIS zarządzają negocjowaniem certyfikatu klienta w Twoim imieniu. Podsekcja aplikacji może umożliwić SslRequireCert
negocjowanie certyfikatu klienta dla tych żądań. Aby uzyskać szczegółowe informacje, zobacz Konfiguracja w dokumentacji usług IIS.
Usługi IIS automatycznie buforują wszystkie dane treści żądania do skonfigurowanego limitu rozmiaru przed ponownym negocjowaniem. Żądania przekraczające limit są odrzucane z odpowiedzią 413. Ten limit jest domyślnie ustawiony na 48 KB i można go skonfigurować przez ustawienie parametru uploadReadAheadSize.
HttpSys
Protokół HttpSys ma dwa ustawienia, które kontrolują negocjacje certyfikatu klienta i oba powinny być ustawione. Pierwszy element znajduje się w netsh.exe w obszarze http add sslcert clientcertnegotiation=enable/disable
. Ta flaga wskazuje, czy certyfikat klienta powinien być negocjowany na początku połączenia i powinien być ustawiony disable
na wartość dla opcjonalnych certyfikatów klienta. Aby uzyskać szczegółowe informacje, zobacz dokumentację netsh .
Drugie ustawienie to ClientCertificateMethod. Po ustawieniu AllowRenegotation
wartości na wartość certyfikat klienta można renegocjować podczas żądania.
UWAGA Aplikacja powinna buforować lub używać danych treści żądania przed podjęciem próby renegocjacji. W przeciwnym razie żądanie może nie odpowiadać.
Istnieje znany problem polegający na tym, że włączenie AllowRenegotation
może spowodować synchroniczną synchronizację podczas uzyskiwania ClientCertificate dostępu do właściwości. Wywołaj metodę , GetClientCertificateAsync aby tego uniknąć. Ten problem został rozwiązany na platformie .NET 6. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub. Uwaga GetClientCertificateAsync
może zwrócić certyfikat o wartości null, jeśli klient odmówi podania certyfikatu.
Kestrel
Kestrel kontroluje negocjacje certyfikatu klienta z opcją ClientCertificateMode .
W przypadku platformy .NET 5 i starszych Kestrel nie obsługuje ponownego negocjowania po rozpoczęciu połączenia w celu uzyskania certyfikatu klienta. Ta funkcja została dodana na platformie .NET 6.
Microsoft.AspNetCore.Authentication.Certificate
zawiera implementację podobną do uwierzytelniania certyfikatów dla ASP.NET Core. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Dokładniej jest to procedura obsługi uwierzytelniania, która weryfikuje certyfikat, a następnie daje zdarzenie, w którym można rozpoznać ten certyfikat jako ClaimsPrincipal
.
Skonfiguruj serwer pod kątem uwierzytelniania certyfikatów, niezależnie od tego, czy jest to usługa IIS, Kestrelusługa Azure Web Apps, czy cokolwiek innego, którego używasz.
Scenariusze serwera proxy i modułu równoważenia obciążenia
Uwierzytelnianie certyfikatu jest scenariuszem stanowym używanym głównie w przypadku, gdy serwer proxy lub moduł równoważenia obciążenia nie obsługuje ruchu między klientami i serwerami. Jeśli używany jest serwer proxy lub moduł równoważenia obciążenia, uwierzytelnianie certyfikatu działa tylko wtedy, gdy serwer proxy lub moduł równoważenia obciążenia:
- Obsługuje uwierzytelnianie.
- Przekazuje informacje o uwierzytelnianiu użytkownika do aplikacji (na przykład w nagłówku żądania), która działa na podstawie informacji uwierzytelniania.
Alternatywą dla uwierzytelniania certyfikatów w środowiskach, w których są używane serwery proxy i moduły równoważenia obciążenia, jest usługa Active Directory Federated Services (ADFS) z protokołem OpenID Connect (OIDC).
Rozpocznij
Uzyskaj certyfikat HTTPS, zastosuj go i skonfiguruj serwer tak, aby wymagał certyfikatów.
W aplikacji internetowej dodaj odwołanie do pakietu Microsoft.AspNetCore.Authentication.Certificate . Następnie w metodzie Startup.ConfigureServices
wywołaj metodę services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).AddCertificate(...);
z opcjami, podając pełnomocnika do OnCertificateValidated
wykonania dodatkowej weryfikacji certyfikatu klienta wysłanego z żądaniami. Zmień te informacje na ClaimsPrincipal
wartość i ustaw ją we context.Principal
właściwości .
Jeśli uwierzytelnianie zakończy się niepowodzeniem 403 (Forbidden)
, ta procedura obsługi zwróci odpowiedź raczej jako 401 (Unauthorized)
, jak można się spodziewać. Przyczyną jest to, że uwierzytelnianie powinno nastąpić podczas początkowego połączenia TLS. Do czasu dotarcia do programu obsługi jest za późno. Nie ma możliwości uaktualnienia połączenia z połączenia anonimowego do jednego z certyfikatem.
app.UseAuthentication();
Dodaj również metodę Startup.Configure
. W przeciwnym razie nie zostanie ustawiona HttpContext.User
wartość ClaimsPrincipal
utworzona na podstawie certyfikatu. Na przykład:
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
}
W poprzednim przykładzie pokazano domyślny sposób dodawania uwierzytelniania certyfikatu. Procedura obsługi konstruuje podmiot zabezpieczeń użytkownika przy użyciu typowych właściwości certyfikatu.
Konfigurowanie weryfikacji certyfikatu
Procedura CertificateAuthenticationOptions
obsługi ma pewne wbudowane weryfikacje, które są minimalnymi weryfikacjami, które należy wykonać na certyfikacie. Każde z tych ustawień jest domyślnie włączone.
AllowedCertificateTypes = Chained, SelfSigned lub All (Łańcuchowe | SelfSigned)
Wartość domyślna: CertificateTypes.Chained
To sprawdzenie sprawdza, czy dozwolony jest tylko odpowiedni typ certyfikatu. Jeśli aplikacja korzysta z certyfikatów z podpisem własnym, ta opcja musi być ustawiona na CertificateTypes.All
lub CertificateTypes.SelfSigned
.
ValidateCertificateUse
Wartość domyślna: true
To sprawdzenie sprawdza, czy certyfikat przedstawiony przez klienta ma rozszerzone użycie klucza uwierzytelniania klienta (EKU) lub w ogóle nie ma jednostek EKU. Jak mówią specyfikacje, jeśli nie określono EKU, wszystkie EKU są uznawane za prawidłowe.
ValidateValidityPeriod
Wartość domyślna: true
Ta kontrola sprawdza, czy certyfikat znajduje się w okresie ważności. W każdym żądaniu program obsługi gwarantuje, że certyfikat, który był ważny, gdy został przedstawiony, nie wygasł podczas bieżącej sesji.
OdwołanieFlag
Wartość domyślna: X509RevocationFlag.ExcludeRoot
Flaga określająca, które certyfikaty w łańcuchu są sprawdzane pod kątem odwołania.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Odwołajmode
Wartość domyślna: X509RevocationMode.Online
Flaga określająca sposób przeprowadzania kontroli odwołania.
Określenie sprawdzania online może spowodować duże opóźnienie podczas kontaktowania się z urzędem certyfikacji.
Kontrole odwołania są wykonywane tylko wtedy, gdy certyfikat jest w łańcuchu do certyfikatu głównego.
Czy mogę skonfigurować aplikację tak, aby wymagała certyfikatu tylko w określonych ścieżkach?
Nie jest to możliwe. Pamiętaj, że wymiana certyfikatów odbywa się na początku konwersacji HTTPS, jest wykonywana przez serwer przed odebraniem pierwszego żądania na tym połączeniu, aby nie można było ograniczyć zakresu na podstawie pól żądania.
Zdarzenia programu obsługi
Procedura obsługi ma dwa zdarzenia:
OnAuthenticationFailed
: Wywoływane, jeśli podczas uwierzytelniania wystąpi wyjątek i można zareagować.OnCertificateValidated
: Wywoływany po zweryfikowaniu certyfikatu, pomyślnie zweryfikowano walidację i utworzono domyślną jednostkę zabezpieczeń. To zdarzenie umożliwia przeprowadzenie własnej weryfikacji i rozszerzenie lub zastąpienie podmiotu zabezpieczeń. Przykłady obejmują:Określanie, czy certyfikat jest znany usługom.
Konstruowanie własnego podmiotu zabezpieczeń. Rozważmy następujący przykład w pliku
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; } }; });
Jeśli okaże się, że certyfikat przychodzący nie spełnia dodatkowej weryfikacji, wywołaj context.Fail("failure reason")
metodę z przyczyną niepowodzenia.
W przypadku rzeczywistych funkcji prawdopodobnie chcesz wywołać usługę zarejestrowaną we wstrzyknięciu zależności, która łączy się z bazą danych lub innym typem magazynu użytkowników. Uzyskaj dostęp do usługi przy użyciu kontekstu przekazanego do pełnomocnika. Rozważmy następujący przykład w pliku 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;
}
};
});
Koncepcyjnie weryfikacja certyfikatu jest problemem autoryzacji. Dodanie sprawdzania, na przykład wystawcy lub odcisku palca w zasadach autoryzacji, a nie wewnątrz OnCertificateValidated
elementu , jest całkowicie akceptowalne.
Konfigurowanie serwera w celu wymagania certyfikatów
Kestrel
W Program.cs
programie skonfiguruj Kestrel w następujący sposób:
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);
});
});
}
Uwaga
Punkty końcowe utworzone przez wywołanie przed wywołaniem ConfigureHttpsDefaults Listen nie będą miały zastosowanych wartości domyślnych.
IIS
Wykonaj następujące kroki w Menedżerze usług IIS:
- Wybierz swoją witrynę na karcie Połączenia .
- Kliknij dwukrotnie opcję Ustawienia protokołu SSL w oknie Widok funkcji.
- Zaznacz pole wyboru Wymagaj protokołu SSL i wybierz przycisk radiowy Wymagaj w sekcji Certyfikaty klienta.
Azure i niestandardowe serwery proxy sieci Web
Zapoznaj się z dokumentacją dotyczącą hosta i wdrażania, aby dowiedzieć się, jak skonfigurować oprogramowanie pośredniczące przekazujące certyfikaty.
Używanie uwierzytelniania certyfikatów w usłudze Azure Web Apps
Dla platformy Azure nie jest wymagana żadna konfiguracja przekazywania dalej. Konfiguracja przekazywania jest konfigurowana przez oprogramowanie pośredniczące przekazywania certyfikatów.
Uwaga
Oprogramowanie pośredniczące przekazywania certyfikatów jest wymagane w tym scenariuszu.
Aby uzyskać więcej informacji, zobacz Używanie certyfikatu TLS/SSL w kodzie w usłudze aplikacja systemu Azure (dokumentacja platformy Azure).
Używanie uwierzytelniania certyfikatów w niestandardowych internetowych serwerach proxy
Metoda służy do określania AddCertificateForwarding
:
- Nazwa nagłówka klienta.
- Sposób ładowania certyfikatu (przy użyciu
HeaderConverter
właściwości ).
W niestandardowych internetowych serwerach proxy certyfikat jest przekazywany jako niestandardowy nagłówek żądania, na przykład X-SSL-CERT
. Aby go użyć, skonfiguruj przekazywanie certyfikatów w programie 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;
}
Jeśli aplikacja jest odwrotnie proxied przez serwer NGINX z konfiguracją proxy_set_header ssl-client-cert $ssl_client_escaped_cert
lub wdrożona na platformie Kubernetes przy użyciu ruchu przychodzącego NGINX, certyfikat klienta jest przekazywany do aplikacji w postaci zakodowanej pod adresem URL. Aby użyć certyfikatu, zdekoduj go w następujący sposób:
Dodaj przestrzeń nazw dla elementu System.Net na początku elementu Startup.cs
:
using System.Net;
W pliku 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;
};
});
Dodaj metodę UrlEncodedPemToByteArray
:
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);
}
Następnie Startup.Configure
metoda dodaje oprogramowanie pośredniczące. UseCertificateForwarding
parametr jest wywoływany przed wywołaniami i UseAuthentication
UseAuthorization
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Oddzielna klasa może służyć do implementowania logiki walidacji. Ponieważ w tym przykładzie jest używany ten sam certyfikat z podpisem własnym, upewnij się, że można używać tylko certyfikatu. Sprawdź, czy odciski palca certyfikatu klienta i certyfikatu serwera są zgodne, w przeciwnym razie można użyć dowolnego certyfikatu i będzie wystarczający do uwierzytelnienia. Będzie to używane wewnątrz AddCertificate
metody . Możesz również zweryfikować podmiot lub wystawcę w tym miejscu, jeśli używasz certyfikatów pośrednich lub podrzędnych.
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;
}
}
}
Implementowanie klienta HttpClient przy użyciu certyfikatu i programu HttpClientHandler
Element HttpClientHandler
można dodać bezpośrednio w konstruktorze HttpClient
klasy . Należy zachować ostrożność podczas tworzenia wystąpień klasy HttpClient
. Następnie HttpClient
program wyśle certyfikat z każdym żądaniem.
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}");
}
Implementowanie klienta HttpClient przy użyciu certyfikatu i nazwanego obiektu HttpClient z elementu IHttpClientFactory
W poniższym przykładzie certyfikat klienta jest dodawany do obiektu używającego HttpClientHandler
ClientCertificates
właściwości z programu obsługi. Ta procedura obsługi może być następnie używana w nazwanym wystąpieniu HttpClient
ConfigurePrimaryHttpMessageHandler obiektu przy użyciu metody . Jest to konfiguracja w programie 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;
});
Następnie IHttpClientFactory
można go użyć do pobrania nazwanego wystąpienia za pomocą programu obsługi i certyfikatu. Metoda CreateClient
o nazwie klienta zdefiniowanego Startup
w klasie służy do pobierania wystąpienia. Żądanie HTTP można wysłać przy użyciu klienta zgodnie z potrzebami.
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}");
}
Jeśli prawidłowy certyfikat jest wysyłany do serwera, dane są zwracane. Jeśli żaden certyfikat lub nieprawidłowy certyfikat nie zostanie wysłany, zostanie zwrócony kod stanu HTTP 403.
Tworzenie certyfikatów w programie PowerShell
Tworzenie certyfikatów jest najtrudniejszą częścią konfigurowania tego przepływu. Certyfikat główny można utworzyć przy użyciu New-SelfSignedCertificate
polecenia cmdlet programu PowerShell. Podczas tworzenia certyfikatu użyj silnego hasła. Ważne jest dodanie parametru KeyUsageProperty
i parametru KeyUsage
, jak pokazano poniżej.
Tworzenie głównego urzędu certyfikacji
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
Uwaga
Wartość parametru musi być zgodna -DnsName
z docelowym wdrożeniem aplikacji. Na przykład "localhost" na potrzeby programowania.
Instalowanie w zaufanym katalogu głównym
Certyfikat główny musi być zaufany w systemie hosta. Certyfikat główny, który nie został utworzony przez urząd certyfikacji, nie będzie domyślnie zaufany. Aby uzyskać informacje na temat zaufania certyfikatowi głównemu w systemie Windows, zobacz to pytanie.
Certyfikat pośredni
Certyfikat pośredniczący można teraz utworzyć na podstawie certyfikatu głównego. Nie jest to wymagane w przypadku wszystkich przypadków użycia, ale może być konieczne utworzenie wielu certyfikatów lub konieczność aktywowania lub wyłączania grup certyfikatów. Parametr TextExtension
jest wymagany do ustawienia długości ścieżki w podstawowych ograniczeniach certyfikatu.
Certyfikat pośredniczący można następnie dodać do zaufanego certyfikatu pośredniego w systemie hosta systemu Windows.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu pośredniego
Certyfikat podrzędny można utworzyć na podstawie certyfikatu pośredniego. Jest to jednostka końcowa i nie musi tworzyć większej liczby certyfikatów podrzędnych.
$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
Tworzenie certyfikatu podrzędnego na podstawie certyfikatu głównego
Certyfikat podrzędny można również utworzyć bezpośrednio na podstawie certyfikatu głównego.
$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
Przykładowy certyfikat główny — certyfikat pośredni — certyfikat
$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
W przypadku używania certyfikatów głównych, pośrednich lub podrzędnych certyfikaty można zweryfikować przy użyciu odcisku palca lub klucza publicznego zgodnie z wymaganiami.
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;
}
}
}
Opcjonalne certyfikaty klienta
Ta sekcja zawiera informacje dotyczące aplikacji, które muszą chronić podzbiór aplikacji przy użyciu certyfikatu. Na przykład Razor strona lub kontroler w aplikacji może wymagać certyfikatów klienta. Stanowi to wyzwania związane z certyfikatami klienta:
- To funkcja PROTOKOŁU TLS, a nie funkcja HTTP.
- Są negocjowane dla połączenia i zwykle na początku połączenia przed udostępnieniem jakichkolwiek danych HTTP.
Istnieją dwa podejścia do implementowania opcjonalnych certyfikatów klienta:
- Używanie oddzielnych nazw hostów (SNI) i przekierowywanie. Chociaż więcej pracy należy skonfigurować, jest to zalecane, ponieważ działa w większości środowisk i protokołów.
- Renegocjacja podczas żądania HTTP. Ma to kilka ograniczeń i nie jest zalecane.
Oddzielne hosty (SNI)
Na początku połączenia znana jest tylko nazwa nazwy serwera (SNI) †. Certyfikaty klienta można skonfigurować na nazwę hosta, tak aby jeden host ich wymagał, a inny nie.
- Skonfiguruj powiązanie dla domeny i poddomeny:
- Na przykład skonfiguruj powiązania w systemach
contoso.com
imyClient.contoso.com
.contoso.com
Host nie wymaga certyfikatu klienta, alemyClient.contoso.com
nie. - Aby uzyskać więcej informacji, zobacz:
- Kestrel serwer internetowy w ASP.NET Core:
- ListenOptions.UseHttps
- ClientCertificateMode
- Uwaga Kestrel nie obsługuje obecnie wielu konfiguracji protokołu TLS w jednym powiązaniu. Potrzebne są dwa powiązania z unikatowymi adresami IP lub portami. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub.
- USŁUGI IIS
- HTTP.sys: Konfigurowanie systemu Windows Server
- Kestrel serwer internetowy w ASP.NET Core:
- Na przykład skonfiguruj powiązania w systemach
ASP.NET Core 5 i nowszych dodaje wygodniejsze obsługę przekierowywania w celu uzyskania opcjonalnych certyfikatów klienta. Aby uzyskać więcej informacji, zobacz przykład Opcjonalne certyfikaty.
- W przypadku żądań do aplikacji internetowej, które wymagają certyfikatu klienta i których nie ma:
- Przekieruj do tej samej strony przy użyciu poddomeny chronionej certyfikatem klienta.
- Na przykład przekieruj do
myClient.contoso.com/requestedPage
. Ponieważ żądanie domyClient.contoso.com/requestedPage
jest inną nazwą hosta niżcontoso.com/requestedPage
, klient ustanawia inne połączenie, a certyfikat klienta jest dostarczany. - Aby uzyskać więcej informacji, zobacz Wprowadzenie do autoryzacji w programie ASP.NET Core.
† oznaczanie nazwy serwera (SNI) to rozszerzenie protokołu TLS do uwzględnienia domeny wirtualnej w ramach negocjacji protokołu SSL. Oznacza to, że nazwa domeny wirtualnej lub nazwa hosta może służyć do identyfikowania punktu końcowego sieci.
Renegocjacji
Renegocjacja protokołu TLS to proces, za pomocą którego klient i serwer mogą ponownie ocenić wymagania dotyczące szyfrowania dla pojedynczego połączenia, w tym żądania certyfikatu klienta, jeśli nie zostały wcześniej podane. Renegocjacja protokołu TLS jest zagrożeniem bezpieczeństwa i nie jest zalecana, ponieważ:
- W przypadku protokołu HTTP/1.1 serwer musi najpierw buforować lub używać wszelkich danych HTTP, które są w locie, takich jak treść żądania POST, aby upewnić się, że połączenie jest jasne dla renegocjacji. W przeciwnym razie renegocjacja może przestać odpowiadać lub zakończyć się niepowodzeniem.
- Protokół HTTP/2 i HTTP/3 jawnie uniemożliwiają renegocjację.
- Istnieją zagrożenia bezpieczeństwa związane z renegocjacją. Protokół TLS 1.3 usunął renegocjację całego połączenia i zastąpił go nowym rozszerzeniem żądania tylko certyfikatu klienta po rozpoczęciu połączenia. Ten mechanizm jest ujawniany za pośrednictwem tych samych interfejsów API i nadal podlega wcześniejszym ograniczeniom buforowania i wersji protokołu HTTP.
Implementacja i konfiguracja tej funkcji różni się w zależności od wersji serwera i platformy.
IIS
Usługi IIS zarządzają negocjowaniem certyfikatu klienta w Twoim imieniu. Podsekcja aplikacji może umożliwić SslRequireCert
negocjowanie certyfikatu klienta dla tych żądań. Aby uzyskać szczegółowe informacje, zobacz Konfiguracja w dokumentacji usług IIS.
Usługi IIS automatycznie buforują wszystkie dane treści żądania do skonfigurowanego limitu rozmiaru przed ponownym negocjowaniem. Żądania przekraczające limit są odrzucane z odpowiedzią 413. Ten limit jest domyślnie ustawiony na 48 KB i można go skonfigurować przez ustawienie parametru uploadReadAheadSize.
HttpSys
Protokół HttpSys ma dwa ustawienia, które kontrolują negocjacje certyfikatu klienta i oba powinny być ustawione. Pierwszy element znajduje się w netsh.exe w obszarze http add sslcert clientcertnegotiation=enable/disable
. Ta flaga wskazuje, czy certyfikat klienta powinien być negocjowany na początku połączenia i powinien być ustawiony disable
na wartość dla opcjonalnych certyfikatów klienta. Aby uzyskać szczegółowe informacje, zobacz dokumentację netsh .
Drugie ustawienie to ClientCertificateMethod. Po ustawieniu AllowRenegotation
wartości na wartość certyfikat klienta można renegocjować podczas żądania.
UWAGA Aplikacja powinna buforować lub używać danych treści żądania przed podjęciem próby renegocjacji. W przeciwnym razie żądanie może nie odpowiadać.
Istnieje znany problem polegający na tym, że włączenie AllowRenegotation
może spowodować synchroniczną synchronizację podczas uzyskiwania ClientCertificate dostępu do właściwości. Wywołaj metodę , GetClientCertificateAsync aby tego uniknąć. Ten problem został rozwiązany na platformie .NET 6. Aby uzyskać więcej informacji, zobacz ten problem w serwisie GitHub. Uwaga GetClientCertificateAsync
może zwrócić certyfikat o wartości null, jeśli klient odmówi podania certyfikatu.
Kestrel
Kestrel kontroluje negocjacje certyfikatu klienta z opcją ClientCertificateMode .
W przypadku platformy .NET 5 i starszych Kestrel nie obsługuje ponownego negocjowania po rozpoczęciu połączenia w celu uzyskania certyfikatu klienta. Ta funkcja została dodana na platformie .NET 6.
Pozostaw pytania, komentarze i inne opinie dotyczące opcjonalnych certyfikatów klienta w tym problemie z dyskusją w usłudze GitHub.