协商了哪个TLS版本?

我的应用程序在.NET 4.7中运行。默认情况下,它将尝试使用TLS1.2。例如,执行以下HTTP请求时,是否可以知道协商了哪个TLS版本?

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(decodedUri);

if (requestPayload.Length > 0)

{

using (Stream requestStream = request.GetRequestStream())

{

requestStream.Write(requestPayload, 0, requestPayload.Length);

}

}

我仅出于日志记录/调试目的需要此信息,因此在写入请求流或接收响应之前获取此信息并不重要。我不希望为这些信息解析网络跟踪日志,也不想创建第二个连接(使用SslStream或类似的连接)。

回答:

您可以使用反射来获取TlsStream->SslState->SslProtocol属性值。

这些信息可以从双方返回的流中提取HttpWebRequest.GetRequestStream()HttpWebRequest.GetResponseStream()

ExtractSslProtocol()还处理压缩GzipStream或者DeflateStream当被返回WebRequest

AutomaticDecompression被激活。

验证将在中进行ServerCertificateValidationCallback,当使用初始化请求时会调用。request.GetRequestStream()

: 包含在.Net Framework 4.8+和.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并弃用IDEADES加密(所有变体在链接的文档中列出)。

在这里,我插入了OIDExtractor,其中列出了正在使用的算法。

请注意,TcpClient()和WebRequest()都将到达此处。

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;

}


secur32.dll- > QueryContextAttributesW()方法,允许查询一个初始化流的连接安全性上下文。

[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* buffer引用SecPkgContext_ConnectionInfo结构的:

//[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引用连接上下文句柄是不公开的。

因此,您只能再次通过反射或通过返回的System.Net.Security.AuthenticatedStream派生类(System.Net.Security.SslStreamSystem.Net.Security.NegotiateStream)来获取它TcpClient.GetStream()

不幸的是,WebRequest / WebResponse返回的Stream不能转换为这些类。仅通过非公共属性和字段引用连接和流类型。

我正在发布汇编的文档,它可能会帮助您找到获取该Context Handle的另一条路径。

声明,结构,枚举器列表位于QueryContextAttributesW(PASTEBIN)中。

身份验证结构

使用Schannel创建安全连接

获取有关Schannel连接的信息

查询Schannel上下文的属性

QueryContextAttributes(Schannel)

Internals.cs

内部结构SSPIHandle

{}

内部枚举ContextAttribute

{}


我在您对另一个答案的评论中看到,您TcpClient()不接受使用该解决方案

。我还是把它留在这里,所以本·沃伊特(Ben

Voigt)在这本书中的评论

将对其他感兴趣的人有用。另外,3种可能的解决方案要优于2种。

在提供的上下文中,有关TcpClient()

SslStream用法的一些实现细节。

如果在初始化WebRequest之前需要协议信息,则可以使用TLS连接所需的相同工具在相同的上下文中建立TcpClient()连接。即,ServicePointManager.SecurityProtocol定义支持的协议和ServicePointManager.ServerCertificateValidationCallback验证服务器证书。

TcpClient()和WebRequest都可以使用以下设置:

-启用所有协议,并让Tls握手确定将使用哪个协议。

-定义一个RemoteCertificateValidationCallback()委托来验证X509Certificates服务器传递的X509Chain

实际上,建立TcpClient或WebRequest连接时,Tls握手是相同的。

这种方法使您知道HttpWebRequest 与同一服务器协商哪些Tls协议。

设置一个TcpClient()以接收和评估SslStream

checkCertificateRevocation标志设置为false,因此该过程不会浪费时间来查找吊销列表。

证书验证回调与在中指定的相同ServicePointManager

TlsInfo 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级收集建立安全连接的一些信息:

-

-密码和Hash算法

- 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; }

}

以上是 协商了哪个TLS版本? 的全部内容, 来源链接: utcz.com/qa/420506.html

回到顶部