【问题标题】:Detecting TLS Version used for HttpClient POST or GET calls检测用于 HttpClient POST 或 GET 调用的 TLS 版本
【发布时间】:2019-01-07 19:43:01
【问题描述】:

我正在尝试检索 TLS 版本信息。我下面的代码使用 HttpClient 成功进行了 HTTP GET 调用。我错过了什么?从哪里获取 HttpClient 的 TLS 版本信息?

我正在做与Which TLS version was negotiated? 中建议的相同的事情,但这是特定于 WebRequest 的,它与 HttpClient 不同。

static async Task MainAsync()
{
    Uri baseURI = new Uri("https://jsonplaceholder.typicode.com/posts/1");
    string apiPath = "";
    using (var client = new HttpClient())
    {
        client.BaseAddress = baseURI;
        HttpResponseMessage response = await client.GetAsync(apiPath);
        Console.WriteLine("HTTP status code: " + response.StatusCode.ToString());
        GetSSLConnectionInfo(response, client.BaseAddress.ToString(), apiPath);
    }
    Console.ReadKey();
}

static async Task GetSSLConnectionInfo(HttpResponseMessage response, string baseURI, string apiPath)
{
    using (Stream stream = await response.RequestMessage.Content.ReadAsStreamAsync())
    {
        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);
        SslProtocols b = (SslProtocols)objSslState.GetType().GetProperty("SslProtocol", bindingFlags).GetValue(objSslState);
        Console.WriteLine("SSL Protocol Used for " + baseURI + apiPath + System.Environment.NewLine + "The TLS version used is " + b);
    }
}

我期待 TLS 连接信息,但出现异常。

【问题讨论】:

  • 我的团队试图做同样的事情,但基本上得出的结论是,这是不可能的。
  • 你遇到了什么异常?
  • 永远不要使用async void
  • @abatishchev 抱歉,它只是测试代码。我得到的异常是 System.NullReferenceException: 'Object reference not set to an instance of an object。当他们开始阅读 objbaseStream 时就会发生这种情况。
  • 您的实现依赖于反射,反射本质上和可以理解的片状。调试并找出导致 NullReferenceException 的行意味着您要查找的内容不存在/命名不同。

标签: c# ssl


【解决方案1】:

在后台HttpClient 使用内部TlsStream 类(如您的WebRequest 示例)。我们只需要在另一个位置找到它。这是一个例子:

static void Main(string[] args)
{
    using (var client = new HttpClient())
    {
        using (var response = client.GetAsync("https://example.com/").Result)
        {
            if (response.Content is StreamContent)
            {
                var webExceptionWrapperStream = GetPrivateField(response.Content, "content");
                var connectStream = GetBasePrivateField(webExceptionWrapperStream, "innerStream");
                var connection = GetPrivateProperty(connectStream, "Connection");
                var tlsStream = GetPrivateProperty(connection, "NetworkStream");
                var state = GetPrivateField(tlsStream, "m_Worker");
                var protocol = (SslProtocols)GetPrivateProperty(state, "SslProtocol");
                Console.WriteLine(protocol);
            }
            else
            {
                // not sure if this is possible
            }
        }
    }
}

private static object GetPrivateProperty(object obj, string property)
{
    return obj.GetType().GetProperty(property, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
}

private static object GetPrivateField(object obj, string field)
{
    return obj.GetType().GetField(field, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
}

private static object GetBasePrivateField(object obj, string field)
{
    return obj.GetType().BaseType.GetField(field, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj);
}

【讨论】:

  • 我收到异常“无法访问已处置的对象。对象名称:'SslStream'。”在行 var 协议 = (SslProtocols)GetPrivateProperty(state, "SslProtocol");在 .net 框架 4.6.1 中。 @Zergatul
  • @HassanQayyum 你是用了同样的代码,还是做了一些修改?
  • 我的函数类型不是“static void main()”,但它是一个返回类型的子函数,我也从这个函数中调用了一些其他的东西。但是我已经按原样粘贴了您的代码并进行了尝试。当我在调试模式下运行它时,我注意到在“状态”变量中也有其他属性。还有我可以尝试的其他方法吗? @Zergatul
  • 在做了一些研究之后,我得出的结论是,一些对象被释放是因为它们是非托管的,而 .net 默认情况下会释放非托管对象。有没有办法停止处理它们? @Zergatul
  • @HassanQayyum 您的服务器是否返回空响应?我认为这可能是问题所在。
【解决方案2】:

您可以使用ServerCertificateCustomValidationCallback轻松提取证书

Uri baseURI = new Uri("https://jsonplaceholder.typicode.com/posts/1");
string apiPath = "";
using (var client = new HttpClient(new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) =>
    {
        Console.WriteLine(certificate2.GetNameInfo(X509NameType.SimpleName, false));
        return true;
    }
}))
{
    client.BaseAddress = baseURI;
    HttpResponseMessage response = await client.GetAsync(apiPath);
    Console.WriteLine("HTTP status code: " + response.StatusCode.ToString());
}

【讨论】:

  • 问题不在于如何获得证书,这可以通过多种更简单的方式实现。另外,从回调中返回 true 并首先使用回调并不是一个好主意。是吗?
  • 我不想要 ssl 证书信息我想要用于建立连接的 TLS 版本。
  • @abatishchev 你能告诉我获取该信息的其他方法吗,因为这篇文章中的方法对我不起作用?
  • @HassanQayyum:你在寻找什么样的信息?客户端或服务器证书?
  • @abatishchev 我找到了解决方案。 stackoverflow.com/questions/62610835/…
猜你喜欢
  • 1970-01-01
  • 2020-11-03
  • 1970-01-01
  • 2019-05-30
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多