Sdílet prostřednictvím


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í).

Odložení rozhodnutí na operační systém automaticky používá nejnovější dostupnou verzi protokolu TLS a umožní aplikaci po upgradu operačního systému vyzvednout změny. 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í PROTOKOLU TLS doporučujeme, aby operační systém rozhodl, se kterými nejlepšími šifrovacími sadami se vyjednávají, a proto se doporučuje vyhnout použití CipherSuitesPolicy.

Poznámka:

CipherSuitesPolicy v systému Windows se nepodporuje a pokusí se vytvořit instanci, která způsobí NotSupportedException vyvolání.

Zadání certifikátu serveru

Při ověřování jako serveru vyžaduje SslStreamX509Certificate2 instanci. Doporučujeme vždy použít X509Certificate2 instanci, která obsahuje také privátní klíč.

Certifikát serveru lze předat SslStreamněkolika způsoby:

Doporučeným přístupem je použití SslServerAuthenticationOptions.ServerCertificateContext vlastnosti. Když certifikát získá jeden z dalších dvou způsobů, SslStreamCertificateContext instance se vytvoří interně implementací SslStream . SslStreamCertificateContext Vytvoření zahrnuje vytvoření X509Chain operace náročné na procesor. Je efektivnější vytvořit SslStreamCertificateContext jednou a znovu ho použít pro více SslStream instancí.

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

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.

Vztah důvěryhodnosti vlastních certifikátů

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 v minulosti mnoho let, a proto by všechny certifikáty byly 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 do odepření služeb, pokud jsou externí servery pomalé reagovat. Serverové aplikace by proto měly konfigurovat X509Chain chování sestavení pomocí nástroje CertificateChainPolicy.