Bewährte Methoden für TLS/SSL

TLS (Transport Layer Security) ist ein kryptografisches Protokoll zur Sicherung der Kommunikation zwischen zwei Computern über das Internet. Das TLS-Protokoll wird in .NET über die SslStream Klasse verfügbar gemacht.

In diesem Artikel werden bewährte Methoden zum Einrichten der sicheren Kommunikation zwischen Client und Server beschrieben und die Verwendung von .NET vorausgesetzt. Bewährte Methoden mit .NET Framework finden Sie unter " Transport Layer Security (TLS)"- bewährte Methoden mit .NET Framework.

TLS-Version auswählen

Obwohl es möglich ist, die Version des TLS-Protokolls über die EnabledSslProtocols-Eigenschaft anzugeben, wird empfohlen, sich auf die Einstellungen des Betriebssystems zu verlassen, indem der None Wert verwendet wird (dies ist die Standardeinstellung).

Durch das Zurückstellen der Entscheidung auf das Betriebssystem wird automatisch die neueste Version von TLS verwendet, und die Anwendung kann Änderungen nach Betriebssystemupgrades übernehmen. Das Betriebssystem kann auch die Verwendung von TLS-Versionen verhindern, die nicht mehr als sicher eingestuft werden.

Cipher-Suites auswählen

SslStream ermöglicht es Benutzern, festzulegen, welche Cipher-Suites 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. Der Versuch, es zu instanziieren, führt dazu, dass NotSupportedException ausgelöst wird.

Angeben eines lokalen Zertifikats

Bei der Authentifizierung als Server SslStream ist immer ein Zertifikat erforderlich. Beim Authentifizieren als Client stellen Sie auch ein Zertifikat bereit, wenn der Server ein Zertifikat für gegenseitiges TLS (mTLS) anfordert. In beiden Rollen muss das Zertifikat eine X509Certificate2 Instanz sein, die den privaten Schlüssel enthält.

Aktuelle .NET-Versionen behandeln Server- und Clientseite gleichermaßen, sodass die folgenden Hinweise gelten, unabhängig davon, ob sich die Anwendung als Server, als Client oder in beiden Rollen authentifiziert.

Sie können das Zertifikat SslStream auf verschiedene Arten bereitstellen.

Wenn Sie sich als Server authentifizieren:

Wenn Sie sich als Client authentifizieren:

Hinweis

Die ClientCertificateContext Eigenschaft ist ab .NET 8 verfügbar.

Verwenden Sie für eine bessere Leistung die Zertifikatkontexteigenschaft (ServerCertificateContext oder ClientCertificateContext). Wenn Sie das Zertifikat auf eine andere Weise bereitstellen, erstellt SslStream intern ein SslStreamCertificateContext. Das Erstellen des Kontexts erstellt einen X509ChainCPU-intensiven Vorgang, sodass es effizienter ist, den Kontext einmal zu erstellen und ihn in mehreren SslStream Instanzen wiederzuverwenden.

Durch die erneute Wiederverwendung einer SslStreamCertificateContext Instanz werden auch zusätzliche Features wie die Wiederaufnahme der TLS-Sitzung auf Linux-Servern ermöglicht.

Senden von Zwischenzertifikaten an den Peer

Wenn eine Zwischenzertifizierungsstelle das lokale Zertifikat ausgibt, kann der Peer möglicherweise nicht die vollständige Zertifikatkette erstellen, es sei denn, der Handshake enthält die Zwischenzertifikate. Um diese Zwischenstufen zu senden, erstellen Sie eine SslStreamCertificateContext mit der Create Methode, und übergeben Sie die Zwischenzertifikate im additionalCertificates Parameter:

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;

Der Kontext funktioniert für beide Rollen auf die gleiche Weise. Auf clientseitiger Seite ist der Zertifikatkontext die empfohlene Methode zum Senden von Zwischenstufen, da sich die Alternative – das Hinzufügen der Zwischenstufen zum Computer- oder Benutzerzertifikatspeicher – auf jede Anwendung im System auswirkt.

Benutzerdefinierte X509Certificate-Überprüfung

Es gibt bestimmte Szenarien, in denen das Standardmäßige Zertifikatüberprüfungsverfahren nicht ausreichend ist und einige benutzerdefinierte Validierungslogik erforderlich ist. Teile der Validierungslogik 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

Beim Auftreten eines Zertifikats, das von keiner der vom Computer vertrauenswürdigen Zertifizierungsstellen (einschließlich selbstsignierter Zertifikate) ausgestellt wurde, schlägt die Standardmäßige Zertifikatüberprüfung fehl. Eine mögliche Möglichkeit, dies zu beheben, ist das Hinzufügen der erforderlichen Ausstellerzertifikate zum vertrauenswürdigen Speicher des Computers. Dies kann sich jedoch auf andere Anwendungen auf dem System auswirken und ist nicht immer möglich.

Die alternative Lösung besteht darin, benutzerdefinierte vertrauenswürdige Stammzertifikate über X509ChainPolicy anzugeben. Wenn Sie eine benutzerdefinierte Vertrauensliste angeben möchten, die während der Überprüfung anstelle der Systemvertrauensliste verwendet wird, ziehen Sie das folgende Beispiel in Betracht:

SslClientAuthenticationOptions clientOptions = new();

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

Clients, die mit der vorherigen Richtlinie konfiguriert sind, akzeptieren nur Zertifikate, die vertrauenswürdig sind.customIssuerCert

Bestimmte Überprüfungsfehler ignorieren

Erwägen Sie ein IoT-Gerät ohne dauerhafte Uhr. Nach dem Einschalten würde sich die Uhr des Geräts viele Jahre in die Vergangenheit zurücksetzen und daher würden alle Zertifikate als "noch nicht gültig" betrachtet. Beachten Sie den folgenden Code, der eine Überprüfungsrückrufimplementierung anzeigt, die Gültigkeitsdauerverletzungen 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 weitere Situation, in der eine benutzerdefinierte Zertifikatüberprüfung erforderlich ist, besteht darin, dass Clients erwarten, dass Server ein bestimmtes Zertifikat oder ein Zertifikat aus einer kleinen Gruppe von bekannten Zertifikaten verwenden. Diese Vorgehensweise wird als Zertifikatheftung bezeichnet. Der folgende Codeausschnitt zeigt einen Validierungsrückruf, der überprüft, ob der Server ein Zertifikat mit einem bestimmten bekannten öffentlichen Schlüssel vorlegt.

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 Clientzertifikatüberprüfung

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

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

Hinweis

Ab .NET 11 deaktiviert SslStream standardmäßig AIA-Zertifikatdownloads, wenn Clientzertifikate als Server überprüft werden. Wenn kein benutzerdefiniertes CertificateChainPolicy zur Verfügung gestellt wird, wird der Server nicht versuchen, fehlende Zwischenzertifikate über AIA abzurufen. Weitere Informationen finden Sie unter Standardmäßig deaktivierte serverseitige AIA-Zertifikatdownloads in SslStream.