Bewährte Methoden für TLS/SSL

TLS (Transport Layer Security) ist ein kryptografisches Protokoll, das entwickelt wurde, um die Kommunikation zwischen zwei Computern über das Internet zu schützen. Das TLS-Protokoll wird in .NET über die SslStream-Klasse verfügbar gemacht.

In diesem Artikel werden bewährte Methoden für das Einrichten der sicheren Kommunikation zwischen Client und Server beschrieben. Dabei wird davon ausgegangen, dass .NET verwendet wird. Bewährte Methoden für .NET Framework finden Sie unter Bewährte Methoden für Transport Layer Security (TLS) mit .NET Framework.

Auswählen der TLS-Version

Es ist zwar möglich, die Version des zu verwendenden TLS-Protokolls über die EnabledSslProtocols-Eigenschaft anzugeben, es wird aber empfohlen, den Wert None zu verwenden und dadurch die Einstellungen des Betriebssystems zu übernehmen (Standardeinstellung).

Wenn die Entscheidung dem Betriebssystem überlassen wird, wird automatisch die aktuellste verfügbare TLS-Version verwendet, sodass die Anwendung Änderungen nach Betriebssystemupgrades übernehmen kann. Das Betriebssystem kann auch die Verwendung von TLS-Versionen verhindern, die nicht mehr als sicher gelten.

Auswählen von Suites mit Verschlüsselungsverfahren

SslStream ermöglicht Benutzern die Angabe, welche Suites mit Verschlüsselungsverfahren vom TLS-Handshake über die CipherSuitesPolicy-Klasse ausgehandelt werden können. Wie bei TLS-Versionen wird empfohlen, dem Betriebssystem die Entscheidung zu überlassen, welche die besten Suites mit Verschlüsselungsverfahren für die Aushandlung sind. Daher wird empfohlen, die Verwendung von CipherSuitesPolicy zu vermeiden.

Hinweis

CipherSuitesPolicy wird unter Windows nicht unterstützt, und Versuche zum Instanziieren dieser Klasse lösen NotSupportedException aus.

Angeben eines Serverzertifikats

Bei der Authentifizierung als Server erfordert SslStream eine X509Certificate2-Instanz. Es wird empfohlen, immer eine X509Certificate2-Instanz zu verwenden, die auch den privaten Schlüssel enthält.

Es gibt mehrere Möglichkeiten, wie ein Serverzertifikat an SslStream übergeben werden kann:

Der empfohlene Ansatz ist die Verwendung der Eigenschaft SslServerAuthenticationOptions.ServerCertificateContext. Wenn das Zertifikat mit einer der beiden anderen Methoden abgerufen wird, wird von der SslStream-Implementierung intern eine SslStreamCertificateContext-Instanz erstellt. Bei der Erstellung von SslStreamCertificateContext wird auch ein X509Chain-Element erstellt. Dabei handelt es sich um einen CPU-intensiven Vorgang. Es ist effizienter, einmal ein SslStreamCertificateContext-Element zu erstellen und es für mehrere SslStream-Instanzen wiederzuverwenden.

Die Wiederverwendung von SslStreamCertificateContext-Instanzen ermöglicht auch zusätzliche Features wie die Wiederaufnahme der TLS-Sitzung auf Linux-Servern.

Benutzerdefinierte X509Certificate-Überprüfung

Es gibt bestimmte Szenarien, in denen das Standardverfahren zur Zertifikatüberprüfung nicht ausreichend ist und eine benutzerdefinierte Überprüfungslogik erforderlich ist. Teile der Überprüfungslogik können durch Angabe von SslClientAuthenticationOptions.CertificateChainPolicy oder SslServerAuthenticationOptions.CertificateChainPolicy angepasst werden. Alternativ kann eine vollständig benutzerdefinierte Logik über die Eigenschaft <System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback> bereitgestellt werden. Weitere Informationen finden Sie unter Benutzerdefinierte Zertifikatvertrauensstellung.

Benutzerdefinierte Zertifikatvertrauensstellung

Wenn ein Zertifikat vorhanden ist, das nicht von einer der Zertifizierungsstellen ausgestellt wurde, die der Computer als vertrauenswürdig einstuft (einschließlich selbstsignierter Zertifikate), schlägt das Standardverfahren zur Zertifikatüberprüfung fehl. Eine Möglichkeit, dieses Problem zu beheben, besteht darin, dem vertrauenswürdigen Speicher des Computers die erforderlichen Ausstellerzertifikate hinzuzufügen. Dies kann sich jedoch auf andere Anwendungen im System auswirken und ist nicht immer möglich.

Die alternative Lösung besteht darin, benutzerdefinierte vertrauenswürdige Stammzertifikate über X509ChainPolicy anzugeben. Wenn Sie eine Liste mit benutzerdefinierten Vertrauensstellungen angeben möchten, die während der Überprüfung anstelle der Systemliste mit Vertrauensstellungen verwendet werden soll, sehen Sie sich das folgende Beispiel an:

SslClientAuthenticationOptions clientOptions = new();

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

Clients, die mit der vorherigen Richtlinie konfiguriert wurden, akzeptieren nur Zertifikate, die von customIssuerCert als vertrauenswürdig eingestuft werden.

Ignorieren bestimmter Überprüfungsfehler

Stellen Sie sich ein IoT-Gerät ohne persistente Uhr vor. Nach dem Einschalten wird die Uhr des Geräts mit einem Datum gestartet, das viele Jahre in der Vergangenheit liegt. Daher werden alle Zertifikate als „noch nicht gültig“ betrachtet. Sehen Sie sich den folgenden Code an, der die Implementierung eines Überprüfungsrückrufs zeigt, die Verstöße gegen den Gültigkeitszeitraum ignoriert.

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

Anheften von Zertifikaten

Eine andere Situation, in der eine benutzerdefinierte Zertifikatüberprüfung erforderlich ist, entsteht, wenn Clients erwarten, dass Server ein bestimmtes Zertifikat oder ein Zertifikat aus einer kleinen Gruppe bekannter Zertifikate verwenden. Dies wird als Anheften von Zertifikaten bezeichnet. Der folgende Codeausschnitt zeigt einen Überprüfungsrückruf, der überprüft, ob der Server ein Zertifikat mit einem bestimmten bekannten öffentlichen Schlüssel präsentiert.

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

Überlegungen zur Überprüfung von Clientzertifikaten

Serveranwendungen müssen beim Anfordern und Überprüfen von Clientzertifikaten vorsichtig sein. Zertifikate können die Erweiterung AIA (Authority Information Access, Zugriff auf Stelleninformationen) enthalten, die angibt, wo das Ausstellerzertifikat heruntergeladen werden kann. Der Server versucht daher möglicherweise, das Ausstellerzertifikat vom externen Server herunterzuladen, wenn er das X509Chain-Element für das Clientzertifikat erstellt. Ebenso müssen Server möglicherweise externe Server kontaktieren, um sicherzustellen, dass das Clientzertifikat nicht widerrufen wurde.

Die Notwendigkeit, beim Erstellen und Überprüfen von X509Chain externe Server zu kontaktieren, kann die Anwendung für Denial-of-Service-Angriffe verfügbar machen, wenn die externen Server langsam reagieren. Daher müssen Serveranwendungen das Verhalten für die X509Chain-Erstellung mithilfe von CertificateChainPolicy konfigurieren.