谈判了哪个TLS版本?
我的应用程序在.NET 4.7中运行。 默认情况下,它将尝试使用TLS1.2。 是否可以知道在执行时协商了哪个TLS版本,例如,如下所示的HTTP请求?
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(decodedUri); if (requestPayload.Length > 0) { using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(requestPayload, 0, requestPayload.Length); } }
我只需要这些信息用于记录/调试目的,因此在写入请求流或接收响应之前获取此信息并不重要。 我不希望解析此信息的网络跟踪日志,我也不想创建第二个连接(使用SslStream或类似)。
您可以使用Reflection来获取TlsStream->SslState->SslProtocol
属性值。
可以从HttpWebRequest.GetRequestStream()
和HttpWebRequest.GetResponseStream()
返回的Stream中提取此信息。
更新 :
ExtractSslProtocol()
方法现在检测激活WebRequest
AutomaticDecompression时可能返回的压缩GzipStream
或DeflateStream
。
validation将在TlsValidationCallback
,当使用request.GetRequestStream()
初始化请求时调用TlsValidationCallback
using System.Net; 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.NonPublic; Stream CompressedStream = null; if (stream.GetType().BaseType == typeof(GZipStream)) CompressedStream = (GZipStream)stream; else if (stream.GetType().BaseType == typeof(DeflateStream)) CompressedStream = (DeflateStream)stream; var objbaseStream = CompressedStream?.GetType().GetProperty("BaseStream").GetValue(stream); if (objbaseStream == null) objbaseStream = stream; var objConnection = objbaseStream.GetType().GetField("m_Connection", bindingFlags).GetValue(objbaseStream); 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); }
RemoteCertificateValidationCallback
包含有关所用安全协议的一些有用信息。 (请参阅: 传输层安全性(TLS)参数(IANA)和RFC 5246 )。
所使用的安全协议类型可以提供足够的信息,因为每个协议版本都支持哈希和加密算法的子集。
Tls 1.2,介绍了HMAC-SHA256
并弃用了IDEA
和DES
密码(所有变体都列在链接文档中)。
在这里,我插入了一个OIDExtractor
,它列出了正在使用的算法。
请注意,TcpClient()和WebRequest()都将在此处。
private bool TlsValidationCallback(object sender, X509Certificate CACert, X509Chain CAChain, SslPolicyErrors sslPolicyErrors) { List _OIDExtractor = CAChain .ChainElements .Cast() .Select(x509 => new Oid(x509.Certificate.SignatureAlgorithm.Value)) .ToList(); if (sslPolicyErrors == SslPolicyErrors.None) return true; X509Certificate2 _Certificate = new X509Certificate2(CACert); //If you needed/have to pass a certificate, add it here. //X509Certificate2 _CACert = new X509Certificate2(@"[localstorage]/ca.cert"); //CAChain.ChainPolicy.ExtraStore.Add(_CACert); CAChain.Build(_Certificate); foreach (X509ChainStatus CACStatus in CAChain.ChainStatus) { if ((CACStatus.Status != X509ChainStatusFlags.NoError) & (CACStatus.Status != X509ChainStatusFlags.UntrustedRoot)) return false; } return true; }
更新2:
secur32.dll
– > QueryContextAttributesW()
方法允许查询已初始化的Stream的连接安全上下文。
[DllImport("secur32.dll", CharSet = CharSet.Auto, ExactSpelling=true, SetLastError=false)] private static extern int QueryContextAttributesW(SSPIHandle contextHandle, [In] ContextAttribute attribute, [In] [Out] ref SecPkgContext_ConnectionInfo ConnectionInfo);
从文档中可以看出,此方法返回一个引用SecPkgContext_ConnectionInfo
结构的void* buffer
:
//[SuppressUnmanagedCodeSecurity] private struct SecPkgContext_ConnectionInfo { public SchProtocols dwProtocol; public ALG_ID aiCipher; public int dwCipherStrength; public ALG_ID aiHash; public int dwHashStrength; public ALG_ID aiExch; public int dwExchStrength; }
SchProtocols dwProtocol
成员是SslProtocol。
有什么问题。
引用连接上下文句柄的TlsStream.Context.m_SecurityContext._handle
不是公共的。
因此,您只能通过reflection或通过TcpClient.GetStream()
返回的System.Net.Security.AuthenticatedStream
派生类( System.Net.Security.SslStream
和System.Net.Security.NegotiateStream
)来获取TcpClient.GetStream()
。
遗憾的是,WebRequest / WebResponse返回的Stream无法强制转换为这些类。 Connections和Streams Types仅通过非公共属性和字段引用。
我正在发布已组装的文档,它可能会帮助您找到另一条获取Context Handle的途径。
声明,结构,枚举器列表位于QueryContextAttributesW(PASTEBIN)中 。
Microsoft TechNet
身份validation结构
MSDN
使用Schannel创建安全连接
获取有关Schannel连接的信息
查询Schannel上下文的属性
QueryContextAttributes(Schannel)
代码库(部分)
.NET参考源
Internals.cs
内部结构SSPIHandle {}
内部枚举ContextAttribute {}
更新1:
我在你的评论中看到另一个答案,使用
TcpClient()
的解决方案对你来说是不可接受的。 无论如何我都会把它留在这里,所以Ben Voigt在这篇文章中的评论对任何有兴趣的人都有用。 此外,3种可能的解决方案优于2种。
提供的上下文中有关TcpClient() SslStream用法的一些实现细节。
如果在初始化WebRequest之前需要协议信息,则可以使用TLS连接所需的相同工具在同一上下文中建立TcpClient()连接。 即, ServicePointManager.SecurityProtocol
用于定义支持的协议, ServicePointManager.ServerCertificateValidationCallback
用于validation服务器证书。
TcpClient()和WebRequest都可以使用以下设置:
– 启用所有协议,让Tls Handshake确定将使用哪个协议。
– 定义RemoteCertificateValidationCallback()
委托以validation服务器在X509Chain
传递的X509Certificates
。
实际上,在建立TcpClient或WebRequest连接时,Tls Handshake是相同的。
这种方法可以让您知道您的HttpWebRequest 将与同一服务器协商的Tls协议。
设置TcpClient()
以接收和评估SslStream
。
checkCertificateRevocation
标志设置为false
,因此该过程不会浪费时间查找吊销列表。
证书validationCallback与ServicePointManager
指定的相同
TlsInfo TLSInfo; IPHostEntry DnsHost = await Dns.GetHostEntryAsync(HostURI.Host); using (TcpClient client = new TcpClient(DnsHost.HostName, 443)) { using (SslStream sslstream = new SslStream(client.GetStream(), false, TlsValidationCallback, null)) { sslstream.AuthenticateAsClient(DnsHost.HostName, null, (SslProtocols)ServicePointManager.SecurityProtocol, false); TLSInfo = new TlsInfo(sslstream); } } //The HttpWebRequest goes on from here. HttpWebRequest httpRequest = WebRequest.CreateHttp(HostURI); //(...)
TlsInfo
类收集有关已建立的安全连接的一些信息:
– Tls协议版本
– 密码和散列算法
– Ssl握手中使用的服务器证书
public class TlsInfo { public TlsInfo(SslStream SecureStream) { this.ProtocolVersion = SecureStream.SslProtocol; this.CipherAlgorithm = SecureStream.CipherAlgorithm; this.HashAlgorithm = SecureStream.HashAlgorithm; this.RemoteCertificate = SecureStream.RemoteCertificate; } public SslProtocols ProtocolVersion { get; set; } public CipherAlgorithmType CipherAlgorithm { get; set; } public HashAlgorithmType HashAlgorithm { get; set; } public X509Certificate RemoteCertificate { get; set; } }
下面的解决方案肯定是“黑客”,因为它确实使用了reflection,但它目前涵盖了你可能使用HttpWebRequest的大多数情况。 如果无法确定Tls版本,它将返回null。 在您向请求流写入任何内容之前,它还会在同一请求中validationTls版本。 如果在调用方法时尚未发生流Tls握手,则会触发它。
您的示例用法如下所示:
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); } }
方法:
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; }
我能弄明白的唯一方法是使用SslStream
建立测试连接,然后检查SslProtocol
属性。
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();
我已经检查过sslStream.SslProtocl
将始终与HttpWebRequest
的Connection
使用的TlsStream.m_worker.SslProtocol
相同。
上述就是C#学习教程:谈判了哪个TLS版本?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/983365.html