webentwicklung-frage-antwort-db.com.de

Welche TLS-Version wurde ausgehandelt?

Ich habe meine App in .NET 4.7 ausgeführt. Standardmäßig wird versucht, TLS1.2 .. zu verwenden. Ist es möglich, zu wissen, welche TLS-Version ausgehandelt wurde, wenn Sie beispielsweise eine HTTP-Anforderung wie unten beschrieben ausführen?

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(decodedUri);
if (requestPayload.Length > 0)
{
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(requestPayload, 0, requestPayload.Length);
    }
}

Ich benötige diese Informationen nur zu Protokollierungs-/Debugging-Zwecken. Daher ist es nicht wichtig, dass mir diese Informationen vorliegen, bevor in den Anforderungsstream geschrieben oder die Antwort empfangen wird. Ich möchte keine Net Tracing-Protokolle für diese Informationen analysieren und ich möchte auch keine zweite Verbindung erstellen (mithilfe von SslStream oder ähnlichem).

19
Frederic

Die nachstehende Lösung ist sicherlich ein "Hack", da sie Reflexion verwendet. Derzeit werden jedoch die meisten Situationen abgedeckt, in denen Sie möglicherweise ein HttpWebRequest haben. Es wird null zurückgegeben, wenn die Tls-Version nicht ermittelt werden konnte. Außerdem wird die Tls-Version in derselben Anfrage überprüft, bevor Sie etwas in den Anforderungsstream geschrieben haben. Wenn der Stream Tls-Handshake beim Aufruf der Methode noch nicht aufgetreten ist, wird er ausgelöst.

Ihre Beispielnutzung würde folgendermaßen aussehen:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("...");
request.Method = "POST";
if (requestPayload.Length > 0)
{
    using (Stream requestStream = request.GetRequestStream())
    {
        SslProtocols? protocol = GetSslProtocol(requestStream);
        requestStream.Write(requestPayload, 0, requestPayload.Length);
    }
}

Und die Methode:

public static SslProtocols? GetSslProtocol(Stream stream)
{
    if (stream == null)
        return null;

    if (typeof(SslStream).IsAssignableFrom(stream.GetType()))
    {
        var ssl = stream as SslStream;
        return ssl.SslProtocol;
    }

    var flags = BindingFlags.NonPublic | BindingFlags.Instance;

    if (stream.GetType().FullName == "System.Net.ConnectStream")
    {
        var connection = stream.GetType().GetProperty("Connection", flags).GetValue(stream);
        var netStream = connection.GetType().GetProperty("NetworkStream", flags).GetValue(connection) as Stream;
        return GetSslProtocol(netStream);
    }

    if (stream.GetType().FullName == "System.Net.TlsStream")
    {
        // type SslState
        var ssl = stream.GetType().GetField("m_Worker", flags).GetValue(stream);

        if (ssl.GetType().GetProperty("IsAuthenticated", flags).GetValue(ssl) as bool? != true)
        {
            // we're not authenticated yet. see: https://referencesource.Microsoft.com/#System/net/System/Net/_TLSstream.cs,115
            var processAuthMethod = stream.GetType().GetMethod("ProcessAuthentication", flags);
            processAuthMethod.Invoke(stream, new object[] { null });
        }

        var protocol = ssl.GetType().GetProperty("SslProtocol", flags).GetValue(ssl) as SslProtocols?;
        return protocol;
    }

    return null;
}
2
caesay

Ich habe hier und da ein paar Ideen zusammengestellt. Ich habe eine einfache Methode zum Testen der verfügbaren Protokolle verwendet, wobei jeweils eine bestimmte Verbindungsart erzwungen wurde. Am Ende bekomme ich eine Liste mit den Ergebnissen, die ich je nach Bedarf verwenden kann.

Ps: Der Test ist nur gültig, wenn Sie wissen, dass die Website online ist. Sie können einen vorherigen Test durchführen, um dies zu überprüfen.

    public static IEnumerable<T> GetValues<T>()
    {
        return Enum.GetValues(typeof(T)).Cast<T>();
    }

    private Dictionary<SecurityProtocolType, bool> ProcessProtocols(string address)
    {   
        var protocolResultList = new Dictionary<SecurityProtocolType, bool>();
        var defaultProtocol = ServicePointManager.SecurityProtocol;

        ServicePointManager.Expect100Continue = true;
        foreach (var protocol in GetValues<SecurityProtocolType>())
        {
            try
            {
                ServicePointManager.SecurityProtocol = protocol;

                var request = WebRequest.Create(address);
                var response = request.GetResponse();

                protocolResultList.Add(protocol, true);
            }
            catch
            {
                protocolResultList.Add(protocol, false);
            }
        }

        ServicePointManager.SecurityProtocol = defaultProtocol;

        return protocolResultList;
    }

Hoffe das wird hilfreich sein

0
Eduardo Gadotti

Die einzige Möglichkeit, die ich herausfinden kann, ist SslStream, um eine Testverbindung herzustellen, und dann die SslProtocol-Eigenschaft überprüfen.

TcpClient client = new TcpClient(decodedUri.DnsSafeHost, 443);
SslStream sslStream = new SslStream(client.GetStream());

// use this overload to ensure SslStream has the same scope of enabled protocol as HttpWebRequest
sslStream.AuthenticateAsClient(decodedUri.Host, null,
    (SslProtocols)ServicePointManager.SecurityProtocol, true);

// Check sslStream.SslProtocol here

client.Close();
sslStream.Close();

Ich habe überprüft, dass sslStream.SslProtocl immer derselbe ist wie der TlsStream.m_worker.SslProtocol, der von HttpWebRequests Connection verwendet wird.

0
Alex.Wei