HTTPS, TLS и сертификаты в .NET: как работает и как настроить?
Что даёт TLS
TLS 1.3 handshake
- Асимметричная криптография (ECDHE, подпись сертификата) — только для установки соединения: она медленная.
- Симметричная (AES-GCM) — для данных: быстрая, аппаратно ускоренная.
- Forward secrecy: ключи сессии эфемерные — даже если приватный ключ сервера украдут позже, записанный трафик не расшифровать. В TLS 1.3 обязательна.
- TLS 1.2 требует 2 round-trip, TLS 1.3 — 1 (и 0-RTT для повторных соединений, с оговорками про replay).
Цепочка доверия
Сервер должен отдавать полную цепочку (свой + промежуточные). Частая ошибка — не отдан промежуточный сертификат: браузеры иногда достраивают цепочку сами, а HttpClient в Linux-контейнере — нет, и запросы падают.
ASP.NET Core: базовая настройка
var app = builder.Build();
if (!app.Environment.IsDevelopment())
app.UseHsts(); // Strict-Transport-Security: браузер будет ходить только по HTTPS
app.UseHttpsRedirection(); // 307/308 с http на httpsbuilder.Services.AddHsts(o =>
{
o.MaxAge = TimeSpan.FromDays(365);
o.IncludeSubDomains = true;
o.Preload = true; // для включения в preload-список браузеров
});Kestrel с сертификатом
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://0.0.0.0:8443",
"Certificate": { "Path": "/certs/tls.crt", "KeyPath": "/certs/tls.key" }
}
}
}
}builder.WebHost.ConfigureKestrel(k =>
{
k.ConfigureHttpsDefaults(h =>
{
h.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
h.ServerCertificateSelector = (ctx, name) => certStore.GetCurrent(name); // горячая замена при продлении
});
});Разработка: dotnet dev-certs https --trust создаёт и добавляет в доверенные локальный сертификат для localhost.
TLS-терминация на ingress
Приложение видит HTTP-запрос от прокси, поэтому без настройки ломаются Request.Scheme, редиректы, генерация ссылок и OIDC redirect_uri:
builder.Services.Configure<ForwardedHeadersOptions>(o =>
{
o.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
o.KnownNetworks.Clear(); // доверять только известным прокси
o.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
});
app.UseForwardedHeaders(); // самым первым в конвейереHttpClient: не отключайте проверку
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
// шифрование есть, а аутентификации сервера нет — MITM подменит сертификат незаметноПравильные решения для «своего» CA (внутренние сервисы): добавить корневой сертификат компании в хранилище доверия образа (update-ca-certificates) или проверять конкретный CA/отпечаток (certificate pinning) в callback.
mTLS: взаимная аутентификация
builder.WebHost.ConfigureKestrel(k => k.ConfigureHttpsDefaults(h =>
h.ClientCertificateMode = ClientCertificateMode.RequireCertificate));
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(o =>
{
o.AllowedCertificateTypes = CertificateTypes.Chained;
o.RevocationMode = X509RevocationMode.Online;
});var handler = new SocketsHttpHandler();
handler.SslOptions.ClientCertificates = [X509Certificate2.CreateFromPemFile("client.crt", "client.key")];В Kubernetes mTLS между сервисами обычно берёт на себя service mesh (Istio, Linkerd): sidecar-прокси выпускают и ротируют сертификаты автоматически, приложение ничего не знает.
Вопросы на засыпку
Почему не шифровать всё асимметрично?
Асимметричные операции в сотни и тысячи раз медленнее симметричных и ограничены по размеру данных. Поэтому асимметрия используется только для обмена ключами и аутентификации, а поток данных шифруется симметричным ключом сессии.
Что видно наблюдателю при HTTPS?
IP-адреса, порт, объём и время трафика, а также доменное имя в SNI (пока не используется Encrypted Client Hello) и DNS-запросы (если не DoH/DoT). Путь URL, query string, заголовки и тело — зашифрованы.
Сертификат истёк ночью. Как не допустить?
Автоматизировать: ACME (Let's Encrypt) с cert-manager или встроенным продлением облака, сроки 90 дней и меньше (отрасль движется к 47 дням), мониторинг срока действия (алерт за 14–30 дней, blackbox exporter), горячая перезагрузка сертификата без рестарта.
Чем самоподписанный сертификат отличается от выданного CA?
Криптографически — ничем. Разница в доверии: самоподписанный никто не подписал, клиенту нечем проверить, что ключ принадлежит серверу. Для внутренних систем используют свой корпоративный CA, чей корень добавлен в доверенные на всех клиентах.
Итог
TLS защищает конфиденциальность, целостность и подлинность сервера: асимметрия для рукопожатия, симметрия для данных, сертификат с цепочкой доверия для идентификации. В ASP.NET Core — HTTPS-редирект и HSTS, TLS 1.2+, сертификаты от автоматизированного CA; при терминации на ingress — Forwarded Headers; никогда не отключайте проверку сертификатов в HttpClient, а для сервис-сервис рассматривайте mTLS или service mesh.