Dela via


Metodtips för TLS/SSL

TLS (Transport Layer Security) är ett kryptografiskt protokoll som är utformat för att skydda kommunikationen mellan två datorer via Internet. TLS-protokollet exponeras i .NET via SslStream klassen.

Den här artikeln innehåller metodtips för att konfigurera säker kommunikation mellan klient och server och förutsätter användning av .NET. Metodtips för .NET Framework finns i Metodtips för Transport Layer Security (TLS) med .NET Framework.

Välj TLS-version

Även om det är möjligt att ange vilken version av TLS-protokollet som ska användas via EnabledSslProtocols egenskapen rekommenderar vi att du skjuter upp inställningarna för operativsystemet med hjälp None av värdet (detta är standardvärdet).

Om du skjuter upp beslutet till operativsystemet används automatiskt den senaste versionen av TLS som är tillgänglig och låter programmet hämta ändringar efter OS-uppgraderingar. Operativsystemet kan också förhindra användning av TLS-versioner som inte längre anses vara säkra.

Välj chiffersviter

SslStream tillåter användare att ange vilka chiffersviter som kan förhandlas med TLS-handskakningen CipherSuitesPolicy via klassen. Precis som med TLS-versioner rekommenderar vi att operativsystemet bestämmer vilka som är de bästa chiffersviterna att förhandla med, och därför rekommenderar vi att du undviker att använda CipherSuitesPolicy.

Kommentar

CipherSuitesPolicy stöds inte i Windows och försök att instansiera det leder NotSupportedException till att det utlöses.

Ange ett servercertifikat

När du autentiserar som en server SslStream krävs en X509Certificate2 instans. Vi rekommenderar att du alltid använder en X509Certificate2 instans som även innehåller den privata nyckeln.

Det finns flera sätt som ett servercertifikat kan skickas till SslStream:

Den rekommenderade metoden är att använda egenskapen SslServerAuthenticationOptions.ServerCertificateContext . När certifikatet hämtas på något av de andra två sätten skapas en SslStreamCertificateContext instans internt av implementeringen SslStream . Att skapa en SslStreamCertificateContext innebär att skapa en X509Chain som är en processorintensiv åtgärd. Det är mer effektivt att skapa en gång SslStreamCertificateContext och återanvända den för flera SslStream instanser.

Återanvändning av SslStreamCertificateContext instanser möjliggör även ytterligare funktioner, till exempel återupptagande av TLS-sessioner på Linux-servrar.

Anpassad X509Certificate validering

Det finns vissa scenarier där standardproceduren för certifikatverifiering inte är tillräcklig och viss anpassad valideringslogik krävs. Delar av valideringslogik kan anpassas genom att SslClientAuthenticationOptions.CertificateChainPolicy ange eller SslServerAuthenticationOptions.CertificateChainPolicy. Du kan också ange helt anpassad logik via <egenskapen System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback> . Mer information finns i Anpassat certifikatförtroende.

Anpassat certifikatförtroende

När du stöter på ett certifikat som inte har utfärdats av någon av de certifikatutfärdare som är betrodda av datorn (inklusive självsignerade certifikat) misslyckas standardproceduren för certifikatverifiering. Ett möjligt sätt att lösa detta är att lägga till nödvändiga utfärdarcertifikat i datorns betrodda arkiv. Det kan dock påverka andra program i systemet och är inte alltid möjligt.

Den alternativa lösningen är att ange anpassade betrodda rotcertifikat via en X509ChainPolicy. Om du vill ange en anpassad förtroendelista som ska användas i stället för listan över systemförtroenden under valideringen bör du överväga följande exempel:

SslClientAuthenticationOptions clientOptions = new();

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

Klienter som konfigurerats med föregående princip accepterar endast certifikat som är betrodda av customIssuerCert.

Ignorera specifika verifieringsfel

Överväg en IoT-enhet utan en beständig klocka. När enheten har startats startar enheten många år tidigare och därför anses alla certifikat vara "ogiltiga". Tänk på följande kod som visar en implementering av valideringsåteranrop som ignorerar överträdelser av giltighetsperioden.

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

Fäst certifikat

En annan situation där anpassad certifikatverifiering krävs är när klienter förväntar sig att servrar ska använda ett specifikt certifikat eller ett certifikat från en liten uppsättning kända certifikat. Den här metoden kallas för att fästa certifikat. Följande kodfragment visar ett verifieringsåteranrop som kontrollerar att servern visar ett certifikat med en specifik känd offentlig nyckel.

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

Överväganden för validering av klientcertifikat

Serverprogram måste vara försiktiga när de kräver och validerar klientcertifikat. Certifikat kan innehålla tillägget AIA (Authority Information Access) som anger var utfärdarcertifikatet kan laddas ned. Servern kan därför försöka ladda ned utfärdarcertifikatet från den externa servern när du skapar X509Chain för klientcertifikatet. På samma sätt kan servrar behöva kontakta externa servrar för att säkerställa att klientcertifikatet inte har återkallats.

Behovet av att kontakta externa servrar när du skapar och verifierar X509Chain kan göra programmet utsatt för överbelastningsattacker om de externa servrarna svarar långsamt. Därför bör serverprogram konfigurera X509Chain byggnadsbeteendet med hjälp av CertificateChainPolicy.