本文介绍了协商了哪个TLS版本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我的应用程序在.NET 4.7中运行。默认情况下,它将尝试使用TLS1.2。 执行下面的HTTP请求时是否可能知道协商了哪个TLS版本?I have my app running in .NET 4.7. By default, it will try to use TLS1.2.Is it possible to know which TLS version was negotiated when performing, for example, an HTTP Request as below?HttpWebRequest request = (HttpWebRequest)WebRequest.Create(decodedUri);if (requestPayload.Length > 0){ using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(requestPayload, 0, requestPayload.Length); }}我仅需要此信息用于记录/调试目的,因此,在写入请求流或接收响应之前获取此信息并不重要。我不希望为这些信息解析网络跟踪日志,也不想创建第二个连接(使用SslStream或类似的连接)。I only need this information for logging / debugging purposes, so it's not important that I have this information before writing to the request stream or receiving the response. I do not wish to parse net tracing logs for this information, and I also don't want to create a second connection (using SslStream or similar).推荐答案您可以使用Reflection来获取 TlsStream-> SslState-> SslProtocol 属性值。 此信息可以从 HttpWebRequest.GetRequestStream()和 HttpWebRequest.GetResponseStream()返回的Stream中提取。You can use Reflection to get to the TlsStream->SslState->SslProtocol property value.This information can be extracted from the Stream returned by both HttpWebRequest.GetRequestStream() and HttpWebRequest.GetResponseStream(). ExtractSslProtocol()还可以处理压缩后的 GzipStream 或在 WebRequest DeflateStream 我们/dotnet/api/system.net.httpwebrequest.automaticdecompression?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(系统。 Net.HttpWebRequest.AutomaticDecompression); k(TargetFrameworkMoniker-.NETFramework& view = netframework-4.7.2 rel = nofollow否Referrer>自动解压缩已激活。The ExtractSslProtocol() also handles the compressed GzipStream or DeflateStream that are returned when the WebRequest AutomaticDecompression is activated.验证将在 ServerCertificateValidationCallback 中进行,当使用 request.GetRequestStream()The validation will occur in the ServerCertificateValidationCallback, which is called when the request is initialized with request.GetRequestStream() 注意初始化请求时调用: SecurityProtocolType.Tls13 包含在.Net Framework 4.8 + 和.Net Core 3.0 + 。Note: SecurityProtocolType.Tls13 is include in .Net Framework 4.8+ and .Net Core 3.0+.using System.IO.Compression;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 | SecurityProtocolType.Tls13; ServicePointManager.ServerCertificateValidationCallback += TlsValidationCallback; Uri requestUri = new Uri("https://somesite.com"); var request = WebRequest.CreateHttp(requestUri); request.Method = WebRequestMethods.Http.Post; request.ServicePoint.Expect100Continue = false; request.AllowAutoRedirect = true; request.CookieContainer = new CookieContainer(); request.ContentType = "application/x-www-form-urlencoded"; var postdata = Encoding.UTF8.GetBytes("Some postdata here"); request.ContentLength = postdata.Length; request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident / 7.0; rv: 11.0) like Gecko"; request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate;q=0.8"); request.Headers.Add(HttpRequestHeader.CacheControl, "no-cache"); using (var requestStream = request.GetRequestStream()) { //Here the request stream is already validated SslProtocols sslProtocol = ExtractSslProtocol(requestStream); if (sslProtocol < SslProtocols.Tls12) { // Refuse/close the connection } } //(...)private SslProtocols ExtractSslProtocol(Stream stream){ if (stream is null) return SslProtocols.None; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; Stream metaStream = stream; if (stream.GetType().BaseType == typeof(GZipStream)) { metaStream = (stream as GZipStream).BaseStream; } else if (stream.GetType().BaseType == typeof(DeflateStream)) { metaStream = (stream as DeflateStream).BaseStream; } var connection = metaStream.GetType().GetProperty("Connection", bindingFlags).GetValue(metaStream); if (!(bool)connection.GetType().GetProperty("UsingSecureStream", bindingFlags).GetValue(connection)) { // Not a Https connection return SslProtocols.None; } var tlsStream = connection.GetType().GetProperty("NetworkStream", bindingFlags).GetValue(connection); var tlsState = tlsStream.GetType().GetField("m_Worker", bindingFlags).GetValue(tlsStream); return (SslProtocols)tlsState.GetType().GetProperty("SslProtocol", bindingFlags).GetValue(tlsState);} RemoteCertificateValidationCallback 有有关使用的安全协议的一些有用信息。 (请参阅:传输层安全性(TLS)参数(IANA)和 RFC 5246 )。 使用的安全协议类型可以足够丰富,因为每个协议版本都支持哈希和加密算法的子集。 Tls 1.2,引入 HMAC-SHA256 并弃用 IDEA 和 DES 密码(所有变体在链接的文档中列出)。The RemoteCertificateValidationCallback has some useful information on the security protocols used. (see: Transport Layer Security (TLS) Parameters (IANA) and RFC 5246).The types of security protocols used can be informative enough, since each protocol version supports a subset of Hashing and Encryption algorithms.Tls 1.2, introduces HMAC-SHA256 and deprecates IDEA and DES ciphers (all variants are listed in the linked documents).在这里,我插入了 OIDExtractor ,其中列出了正在使用的算法。 请注意,TcpClient()和WebRequest都()将到达此处。Here, I inserted an OIDExtractor, which lists the algorithms in use.Note that both TcpClient() and WebRequest() will get here.private bool TlsValidationCallback(object sender, X509Certificate CACert, X509Chain CAChain, SslPolicyErrors sslPolicyErrors){ List<Oid> oidExtractor = CAChain .ChainElements .Cast<X509ChainElement>() .Select(x509 => new Oid(x509.Certificate.SignatureAlgorithm.Value)) .ToList(); // Inspect the oidExtractor list if (sslPolicyErrors == SslPolicyErrors.None) return true; X509Certificate2 certificate = new X509Certificate2(CACert); //If you needed/have to pass a certificate, add it here. //X509Certificate2 cert = new X509Certificate2(@"[localstorage]/[ca.cert]"); //CAChain.ChainPolicy.ExtraStore.Add(cert); 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()方法,允许查询连接UPDATE 2:The secur32.dll -> QueryContextAttributesW() method, allows to query the Connection Security Context of an initialized 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);从文档中可以看到,此方法返回 void *缓冲区引用 SecPkgContext_ConnectionInfo 结构的:As you can see from the documentation, this method returns a void* buffer that references a SecPkgContext_ConnectionInfo structure://[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。The SchProtocols dwProtocol member is the SslProtocol.有什么收获。 TlsStream.Context.m_SecurityContext._handle 不是公开的。 因此,您只能再次通过反射或通过 System.Net.Security.AuthenticatedStream获得它。 派生类( System.Net.Security.SslStream 和 System.Net.Security.NegotiateStream )由 TcpClient.GetStream()返回。What's the catch.The TlsStream.Context.m_SecurityContext._handle that references the Connection Context Handle is not public.Thus, you can get it, again, only through reflection or through the System.Net.Security.AuthenticatedStream derived classes (System.Net.Security.SslStream and System.Net.Security.NegotiateStream) returned by TcpClient.GetStream().不幸的是,WebRequest / WebResponse返回的Stream无法转换为这些类。连接和流类型仅通过非公共属性和字段进行引用。Unfortunately, the Stream returned by WebRequest/WebResponse cannot be cast to these classes. The Connections and Streams Types are only referenced through non-public properties and fields. 我正在发布汇编文档,也许可以帮助您找到声明,结构和枚举器列表位于 QueryContextAttributesW(PASTEBIN)。The declarations, structures, enumerator lists are in QueryContextAttributesW (PASTEBIN). Microsoft TechNet 身份验证结构 MSDN 使用Schannel创建安全连接 获取有关Schannel连接的信息 查询Schannel上下文的属性 QueryContextAttributes(Schannel) 代码库(部分) .NET参考源.NET Reference Source Internals.cs 内部结构SSPIHandle {} 内部枚举ContextAttribute {} 更新1: 我在您的评论中看到该解决方案使用的另一个答案 TcpClient()不可接受。无论如何,我还是把它留在这里,因此在本项中的 Ben Voigt 中的注释对于任何其他感兴趣的人。另外,有3种可能的解决方案优于2。 I saw in your comment to another answer that the solution using TcpClient() is not acceptable for you. I'm leaving it here anyway so the comments of Ben Voigt in this one will be useful to anyone else interested. Also, 3 possible solutions are better than 2. TcpClient() SslStream 的用法。Some implementation details on the TcpClient() SslStream usage in the context provided.如果在初始化WebRequest之前需要协议信息,则可以使用TLS连接所需的相同工具在同一上下文中建立TcpClient()连接。 。即, ServicePointManager.SecurityProtocol 定义受支持的协议和 ServicePointManager.ServerCertificateValidationCallback 来验证服务器证书。 >If protocol informations are required before initializing a WebRequest, a TcpClient() connection can be established in the same context using the same tools required for a TLS connection. Namely, the ServicePointManager.SecurityProtocol to define the supported protocols and the ServicePointManager.ServerCertificateValidationCallback to validate the server certificate. TcpClient()和WebRequest都可以使用以下设置: -启用所有协议,并让Tls握手确定将使用哪个协议。 -定义一个 RemoteCertificateValidationCallback()委托以验证服务器在 X509Certificates 中传递的 X509Chain 。Both TcpClient() and WebRequest can use these settings:- enable all protocols and let the Tls Handshake determine which one will be used.- define a RemoteCertificateValidationCallback() delegate to validate the X509Certificates the Server passes in a X509Chain.实际上,在建立TcpClient或WebRequest连接时,Tls握手是相同的tion。 这种方法使您知道HttpWebRequest 将与同一服务器协商的Tls协议。In practice, the Tls Handshake is the same when establishing a TcpClient or a WebRequest connection.This approach lets you know what Tls Protocol your HttpWebRequest will negotiate with the same server.设置 TcpClient()来接收和评估 SslStream 。 checkCertificateRevocation 标志设置为 false ,因此该过程不会浪费时间查找撤消列表。 证书验证回调与 ServicePointManagerSetup a TcpClient() to receive and evaluate the SslStream.The checkCertificateRevocation flag is set to false, so the process won't waste time looking up the revocation list.The certificate validation Callback is the same specified in ServicePointManagerTlsInfo tlsInfo = null;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握手中使用的服务器证书The TlsInfo Class collects some information on the established secure connection:- Tls protocol version- Cipher and Hash Algorithms- The Server certificate used in the Ssl Handshakepublic 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; }} 这篇关于协商了哪个TLS版本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-13 19:26