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 zum Beispiel eine HTTP-Anfrage wie folgt durchgeführt wurde?
%Vor%Ich benötige diese Informationen nur für Protokollierungs- / Debugging-Zwecke. Daher ist es nicht wichtig, dass ich diese Informationen habe, bevor ich in den Anfragestream schreibe oder die Antwort erhalte. Ich möchte net Tracing-Protokolle für diese Informationen nicht analysieren, und ich möchte auch keine zweite Verbindung (mit SslStream oder ähnliches) erstellen.
UPDATE : Ich habe einen anderen möglichen Weg gefunden, das Protokoll zu verwenden in diesen Verbindungen, aber es ist wahrscheinlich als Dokumentationsquelle nützlicher als als ein praktikabler Ersatz Siehe das Update unten.
Sie können Reflection verwenden, um zum TlsStream->SslState->SslProtocol
-Eigenschaftswert zu gelangen.
Diese Informationen können aus dem Stream extrahiert werden, der von HttpWebRequest.GetRequestStream()
und HttpWebRequest.GetResponseStream()
zurückgegeben wurde.
Die Validierung erfolgt in TlsValidationCallback
, die aufgerufen wird, wenn die Anfrage mit request.GetRequestStream()
Das RemoteCertificateValidationCallback
enthält nützliche Informationen zu den verwendeten Sicherheitsprotokollen. (Siehe: TLS-Parameter (Transport Layer Security) und RFC 5246 ).
Die Arten der verwendeten Sicherheitsprotokolle können informativ genug sein, da jede Protokollversion eine Teilmenge von Hashing- und Verschlüsselungsalgorithmen unterstützt
Tls 1.2, führt HMAC-SHA256
ein und veraltet die Codes IDEA
und DES
(alle Varianten sind in den verknüpften Dokumenten aufgelistet).
Hier habe ich ein OIDExtractor
eingefügt, das die verwendeten Algorithmen auflistet.
Beachten Sie, dass sowohl TcpClient () als auch WebRequest () hierher kommen.
UPDATE 2:
secur32.dll
- & gt; QueryContextAttributesW()
method, ermöglicht die Abfrage des Connection Security Context eines initialisierten Streams. using System.Net.Security;
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
//(...)
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 |
SecurityProtocolType.Tls |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += TlsValidationCallback;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(decodedUri);
if (requestPayload.Length > 0)
{
using (Stream requestStream = request.GetRequestStream())
{
//Here the request stream is already validated
SslProtocols SslProtocol = ExtractSslProtocol(requestStream);
requestStream.Write(requestPayload, 0, requestPayload.Length);
}
}
//(...)
private SslProtocols ExtractSslProtocol(Stream stream)
{
BindingFlags bindingFlags = BindingFlags .Instance | BindingFlags .Public |
BindingFlags .NonPublic | BindingFlags.Static;
var _objConnection = stream.GetType().GetField("m_Connection", bindingFlags).GetValue(stream);
var _objTlsStream = _objConnection.GetType().GetProperty("NetworkStream", bindingFlags).GetValue(_objConnection);
var _objSslState = _objTlsStream.GetType().GetField("m_Worker", bindingFlags).GetValue(_objTlsStream);
return (SslProtocols)_objSslState.GetType().GetProperty("SslProtocol", bindingFlags).GetValue(_objSslState);
}
Wie Sie der Dokumentation entnehmen können, gibt diese Methode eine void* buffer
zurück, die auf eine SecPkgContext_ConnectionInfo
-Struktur verweist:
Das Mitglied SchProtocols dwProtocol
ist das SslProtocol.
Was ist der Haken?
Das TlsStream.Context.m_SecurityContext._handle
, das auf den Connection Context-Handle verweist, ist nicht öffentlich.
Sie erhalten es also wiederum nur durch Reflexion oder durch die System.Net.Security.AuthenticatedStream
abgeleiteten Klassen ( System.Net.Security.SslStream
und System.Net.Security.NegotiateStream
), die von TcpClient.GetStream()
zurückgegeben werden.
Leider kann der von WebRequest / WebResponse zurückgegebene Stream nicht in diese Klassen umgewandelt werden. Die Verbindungen und Streams-Typen werden nur durch nicht öffentliche Eigenschaften und Felder referenziert.
Ich veröffentliche die zusammengefügte Dokumentation, es hilft Ihnen vielleicht, einen anderen Pfad zu finden, um zu diesem Kontext-Handle zu gelangen.
Die Deklarationen, Strukturen, Aufzählungslisten befinden sich in QueryContextAttributesW (PASTEBIN) .
Microsoft TechNet
Authentifizierungsstrukturen
MSDN
Erstellen einer sicheren Verbindung mit Schannel p>
Informationen über Schannel-Verbindungen erhalten
Abfragen der Attribute eines Schannel-Kontexts
QueryContextAttributes (Schannel)
Codebasis (teilweise)
interne Struktur SSPIHandle {}
interne enum ContextAttribute {}
UPDATE 1:
Ich habe in Ihrem Kommentar zu einer anderen Antwort gesehen, dass die Lösung verwendet
TcpClient()
ist für Sie nicht akzeptabel. Ich verlasse es hier sowieso Die Kommentare von Ben Voigt in diesem Artikel werden für alle anderen Interessierten nützlich sein. Außerdem sind 3 mögliche Lösungen besser als 2.
Einige Implementierungsdetails zum TcpClient ( ) SslStream Verwendung im angegebenen Kontext.
Wenn Protokollinformationen vor dem Initialisieren einer WebRequest erforderlich sind, kann eine TcpClient () - Verbindung im selben Kontext mit denselben Tools hergestellt werden, die für eine TLS-Verbindung erforderlich sind. Nämlich die ServicePointManager.SecurityProtocol
Definieren der unterstützten Protokolle und der ServicePointManager.ServerCertificateValidationCallback
um das Serverzertifikat zu validieren.
Sowohl TcpClient () als auch WebRequest können diese Einstellungen verwenden:
- Aktivieren Sie alle Protokolle und lassen Sie den Tls Handshake bestimmen, welcher verwendet wird.
- Definieren Sie einen RemoteCertificateValidationCallback()
-Delegaten, um die X509Certificates
zu validieren, die der Server in einem X509Chain
übergibt.
In der Praxis ist der Tls-Handshake der gleiche, wenn ein TcpClient oder eine WebRequest-Verbindung hergestellt wird.
Dieser Ansatz lässt Sie wissen, welches Tls-Protokoll Ihre HttpWebRequest mit demselben Server aushandeln wird.
Richten Sie ein TcpClient()
ein, um das SslStream
zu empfangen und auszuwerten.
Das Flag checkCertificateRevocation
wird auf false
gesetzt, sodass der Prozess keine Zeit mit dem Aufrufen der Sperrliste verschwendet.
Der Callback für die Zertifikatsüberprüfung ist derselbe wie in ServicePointManager
Die TlsInfo
-Klasse sammelt einige Informationen über die hergestellte sichere Verbindung:
- Tls-Protokollversion
- Verschlüsselungs- und Hash-Algorithmen
- Das Serverzertifikat, das im SSL Handshake verwendet wird.
Die folgende Lösung ist sicherlich ein "Hack", da sie Reflektion verwendet, aber sie deckt derzeit die meisten Situationen ab, in denen Sie sich mit einer HttpWebRequest befinden könnten. Es wird null zurückgegeben, wenn die Tls-Version nicht ermittelt werden konnte. Es überprüft auch die Tls-Version in derselben Anfrage, bevor Sie etwas in den Anfrage-Stream geschrieben haben. Wenn der Stream Tls Handshake beim Aufruf der Methode noch nicht aufgetreten ist, wird er ausgelöst.
Ihre Beispielverwendung würde wie folgt aussehen:
%Vor%Und die Methode:
%Vor% Der einzige Weg, den ich herausfinden kann, ist die Verwendung von SslStream
, um eine Testverbindung herzustellen, dann überprüfe SslProtocol
Eigenschaft.
Ich habe überprüft, dass sslStream.SslProtocl
immer gleich ist wie TlsStream.m_worker.SslProtocol
, das von HttpWebRequest
's Connection
verwendet wurde.