Osvědčené postupy protokolu TLS/SSL

TLS (Transport Layer Security) je kryptografický protokol navržený k zabezpečení komunikace mezi dvěma počítači přes internet. Protokol TLS je vystavený v .NET prostřednictvím SslStream třídy.

Tento článek představuje osvědčené postupy pro nastavení zabezpečené komunikace mezi klientem a serverem a předpokládá použití rozhraní .NET. Osvědčené postupy pro rozhraní .NET Framework najdete v osvědčených postupech tls (Transport Layer Security) s rozhraním .NET Framework.

Vyberte verzi protokolu TLS.

I když je možné zadat verzi protokolu TLS, která se má použít prostřednictvím EnabledSslProtocols vlastnosti, doporučujeme odložit nastavení operačního systému pomocí None hodnoty (výchozí nastavení).

Ponechání rozhodnutí na operačním systému automaticky zajistí použití nejnovější dostupné verze protokolu TLS a umožní aplikaci zohlednit změny po upgradu operačního systému. Operační systém může také zabránit použití verzí protokolu TLS, které se už nepovažují za bezpečné.

Výběr šifrovacích sad

SslStream umožňuje uživatelům určit, které šifrovací sady mohou být vyjednány metodou handshake protokolu TLS prostřednictvím CipherSuitesPolicy třídy. Stejně jako u verzí TLS se doporučuje nechat operační systém rozhodnout, které jsou nejlepší šifrovací sady pro vyjednávání, a proto se doporučuje vyhnout se použití CipherSuitesPolicy.

Poznámka:

CipherSuitesPolicy není podporováno v systému Windows a pokud se o to někdo pokusí, dojde k vyvolání NotSupportedException.

Zadání místního certifikátu

Při autentizaci v roli serveru SslStream vždy vyžaduje certifikát. Při ověřování totožnosti jako klient také předložíte certifikát, pokud si jej server vyžádá pro vzájemné TLS (mTLS). V obou rolích musí být certifikát instancí X509Certificate2, která obsahuje soukromý klíč.

Nedávné .NET verze zpracovávají serverovou a klientskou stranu symetricky, takže následující pokyny platí, jestli se aplikace ověřuje jako server, klient nebo obojí.

Certifikát SslStream můžete poskytnout několika způsoby.

Když se ověřujete jako server:

Když se ověřujete jako klient:

Poznámka:

Vlastnost ClientCertificateContext je k dispozici od platformy .NET 8.

Pokud chcete dosáhnout lepšího výkonu, použijte vlastnost kontextu certifikátu (ServerCertificateContext nebo ClientCertificateContext). Když certifikát zadáte jedním z jiných způsobů, SslStream interně vytvoří SslStreamCertificateContext. Vytvoření kontextu vytvoří operaci náročnou X509Chainna procesor, takže je efektivnější vytvořit kontext jednou a znovu ho použít napříč několika SslStream instancemi.

SslStreamCertificateContext Opětovné použití instance také umožňuje další funkce, jako je obnovení relace TLS na serverech s Linuxem.

Odeslání zprostředkujících certifikátů partnerskému uzlu

Pokud lokální certifikát vydá zprostředkující certifikační autorita, protistrana nemusí být schopna sestavit úplný řetězec certifikátů, pokud během navázání spojení nejsou zahrnuty zprostředkující certifikáty. Chcete-li tyto zprostředkující certifikáty odeslat, vytvořte objekt SslStreamCertificateContext pomocí metody Create a předejte zprostředkující certifikáty v parametru additionalCertificates:

X509Certificate2 leafCertificate = GetLeafCertificate();
X509Certificate2Collection intermediates = GetIntermediateCertificates();

SslStreamCertificateContext certificateContext =
    SslStreamCertificateContext.Create(leafCertificate, intermediates);

// When you authenticate as a server.
serverOptions.ServerCertificateContext = certificateContext;

// When you authenticate as a client for mutual TLS.
clientOptions.ClientCertificateContext = certificateContext;

Kontext funguje stejně pro obě role. Na straně klienta je doporučeným způsobem odesílání mezilehlých certifikátů použití kontextu certifikátu, protože alternativa – přidání mezilehlých certifikátů do úložiště certifikátů počítače nebo uživatele – ovlivňuje každou aplikaci v celém systému.

Vlastní X509Certificate ověřování

Existují určité scénáře, kdy výchozí postup ověření certifikátu není adekvátní a vyžaduje se nějaká vlastní logika ověření. Části ověřovací logiky lze přizpůsobit zadáním SslClientAuthenticationOptions.CertificateChainPolicy nebo SslServerAuthenticationOptions.CertificateChainPolicy. Alternativně lze prostřednictvím vlastnosti System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback< poskytnout >zcela vlastní logiku. Další informace naleznete v tématu Vlastní důvěryhodnost certifikátu.

Důvěra vlastního certifikátu

Pokud narazíte na certifikát, který nevystavil žádný z certifikačních autorit důvěryhodných počítačem (včetně certifikátů podepsaných svým držitelem), výchozí postup ověření certifikátu selže. Jedním z možných způsobů, jak to vyřešit, je přidat potřebné certifikáty vystavitele do důvěryhodného úložiště počítače. To však může mít vliv na jiné aplikace v systému a není vždy možné.

Alternativním řešením je zadat vlastní důvěryhodné kořenové certifikáty prostřednictvím příkazu X509ChainPolicy. Pokud chcete zadat vlastní seznam důvěryhodnosti, který se použije místo seznamu důvěryhodnosti systému během ověřování, zvažte následující příklad:

SslClientAuthenticationOptions clientOptions = new();

clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
    TrustMode = X509ChainTrustMode.CustomRootTrust,
    CustomTrustStore =
    {
        customIssuerCert
    }
};

Klienti nakonfigurovaní s předchozí zásadou přijímají pouze certifikáty, kterým důvěřuje customIssuerCert.

Ignorovat konkrétní chyby ověřování

Zvažte zařízení IoT bez trvalých hodin. Po zapnutí by hodiny zařízení začaly ukazovat datum o mnoho let v minulosti, a proto by byly všechny certifikáty považovány za "ještě neplatné". Vezměte v úvahu následující kód, který ukazuje implementaci zpětného volání ověřování, která ignoruje porušení doby platnosti.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // Anything that would have been accepted by default is OK
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }

    // If there is something wrong other than a chain processing error, don't trust it.
    if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
    {
        return false;
    }

    Debug.Assert(chain is not null);

    // If the reason for RemoteCertificateChainError is that the chain built empty, don't trust it.
    if (chain.ChainStatus.Length == 0)
    {
        return false;
    }

    foreach (X509ChainStatus status in chain.ChainStatus)
    {
        // If an error other than `NotTimeValid` (or `NoError`) is present, don't trust it.
        if ((status.Status & ~X509ChainStatusFlags.NotTimeValid) != X509ChainStatusFlags.NoError)
        {
            return false;
        }
    }

    return true;
}

Připnutí certifikátu

Další situace, kdy je nutné provést vlastní ověření certifikátu, je situace, kdy klienti očekávají, že servery budou používat konkrétní certifikát, nebo certifikát z malé sady známých certifikátů. Tento postup se označuje jako připnutí certifikátu. Následující fragment kódu ukazuje zpětné volání ověření, které kontroluje, že server prezentuje certifikát s konkrétním známým veřejným klíčem.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // If there is something wrong other than a chain processing error, don't trust it.
    if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
    {
        return false;
    }

    Debug.Assert(certificate is not null);

    const string ExpectedPublicKey =
        "3082010A0282010100C204ECF88CEE04C2B3D850D57058CC9318EB5C" +
        "A86849B022B5F9959EB12B2C763E6CC04B604C4CEAB2B4C00F80B6B0" +
        "F972C98602F95C415D132B7F71C44BBCE9942E5037A6671C618CF641" +
        "42C546D31687279F74EB0A9D11522621736C844C7955E4D16BE8063D" +
        "481552ADB328DBAAFF6EFF60954A776B39F124D131B6DD4DC0C4FC53" +
        "B96D42ADB57CFEAEF515D23348E72271C7C2147A6C28EA374ADFEA6C" +
        "B572B47E5AA216DC69B15744DB0A12ABDEC30F47745C4122E19AF91B" +
        "93E6AD2206292EB1BA491C0C279EA3FB8BF7407200AC9208D98C5784" +
        "538105CBE6FE6B5498402785C710BB7370EF6918410745557CF9643F" +
        "3D2CC3A97CEB931A4C86D1CA850203010001";

    return certificate.GetPublicKeyString().Equals(ExpectedPublicKey);
}

Důležité informace o ověřování klientských certifikátů

Serverové aplikace musí být při vyžadování a ověřování klientských certifikátů opatrní. Certifikáty můžou obsahovat rozšíření AIA (Authority Information Access), které určuje, kde se dá stáhnout certifikát vystavitele. Server se proto může pokusit stáhnout certifikát vystavitele z externího serveru při sestavování klientského X509Chain certifikátu. Podobně mohou servery potřebovat kontaktovat externí servery, aby se zajistilo, že certifikát klienta nebyl odvolán.

Potřeba kontaktovat externí servery při sestavování a ověřování X509Chain může aplikaci vystavit útokům typu odmítnutí služby, pokud jsou externí servery pomalé v reagování. Serverové aplikace by proto měly konfigurovat X509Chain chování sestavení pomocí nástroje CertificateChainPolicy.

Poznámka:

Ve výchozím nastavení, od .NET 11, SslStream zakazuje stahování certifikátů AIA při ověřování klientských certifikátů v roli serveru. Pokud není zadaný žádný vlastní CertificateChainPolicy, server se nepokusí prostřednictvím AIA načíst chybějící zprostředkující certifikáty. Další informace naleznete v tématu Stažení AIA certifikátu na straně serveru je ve výchozí konfiguraci zakázáno.