【问题标题】:UWP HttpRequestMessage.TransportInformation missingUWP HttpRequestMessage.TransportInformation 丢失
【发布时间】:2016-11-16 13:16:31
【问题描述】:

我的 UWP 应用连接到 http(s) 服务器以执行一些数据传输。 在建立 TLS 连接时,如果由于握手失败而发生故障,HttpRequestMessage.TransportInformation 会提供有关特定错误和服务器证书的信息,我们使用这些信息向最终用户显示消息。

在我将开发机器升级到 Windows 10 build 1607 并将应用程序重新定位到“Windows 10 Anniversary Edition (10.0; Build 14393)”之前,上述所有方法都运行良好。早些时候,这是“Windows 10 (10.0; Build 10586)”。

更改后,HttpRequestMessage.TransportInformation 中的所有字段都为空。但是,异常和相应的 HRESULT 清楚地表明了 SSL 错误(在这种情况下是不受信任的服务器证书)。

我尝试使用 StreamSocket,果然出现 SSL 握手错误,但 StreamSocket.Information 属性已正确填写所有字段(服务器证书、错误等),因此可以检查它们。

作为记录,服务器证书是自签名的,并使用 SHA1 指纹/签名算法。

在下面的代码 sn-ps 中,ConnectToServerHttpAsync 中的 req.TransprtInformation 从不提供服务器证书,而 streamSock.Information 在 ConnectToServerAsync 中提供服务器证书详细信息。

问题:这是较新的 SDK 中的一个错误,还是我必须对 build 14393 上的 HttpClient 执行不同的操作才能获取传输信息?没有在 MSDN 或 SO 上找到任何关于此行为的内容,因此发布。

private async Task ConnectToServerHttpAsync(Uri connectUri)
{
    HttpRequestMessage req = null;
    try
    {
        using (HttpBaseProtocolFilter bpf = new HttpBaseProtocolFilter())
        {
            bpf.AllowUI = false;
            using (HttpClient httpClient = new HttpClient(bpf))
            {
                req = new HttpRequestMessage(HttpMethod.Get, connectUri);
                using (HttpResponseMessage res = await httpClient.SendRequestAsync(req))
                {
                    Status = ((int)(res.StatusCode)) + " " + res.ReasonPhrase;
                }
            }
        }
    }
    catch (Exception ex)
    {
        SocketErrorStatus eSocketErrorStatus = SocketError.GetStatus(ex.HResult);
        Status = eSocketErrorStatus.ToString();
        Status = req?.TransportInformation?.ServerCertificate?.ToString() ?? "No server certificate.";
    }
    req?.Dispose();
}

private async Task ConnectToServerAsync(Uri uriToConnect)
{
    StreamSocket streamSock = new StreamSocket();
    HostName hostName = new HostName(uriToConnect.Host);

    try
    {
        await streamSock.ConnectAsync(hostName, uriToConnect.Port.ToString(), SocketProtectionLevel.Tls12);
        Status = "Connected.";
        streamSock.Dispose();
    }
    catch (Exception ex)
    {
        SocketErrorStatus eSocketErrorStatus = SocketError.GetStatus(ex.HResult);
        Status = eSocketErrorStatus.ToString();
        Status = "Certificate details:";
        Status = "Friendly name: " + streamSock.Information.ServerCertificate.FriendlyName;
        Status = "Issuer: " + streamSock.Information.ServerCertificate.Issuer;
        Status = "SignatureAlgorithmName: " + streamSock.Information.ServerCertificate.SignatureAlgorithmName;
        Status = "SignatureHashAlgorithmName: " + streamSock.Information.ServerCertificate.SignatureHashAlgorithmName;
        Status = "Subject: " + streamSock.Information.ServerCertificate.Subject;
        Status = "ValidFrom: " + streamSock.Information.ServerCertificate.ValidFrom.ToString();
        Status = "ValidTo: " + streamSock.Information.ServerCertificate.ValidTo.ToString();
        ServerCert = streamSock.Information.ServerCertificate;
    }
}

【问题讨论】:

    标签: c# ssl uwp


    【解决方案1】:

    看来我可能找到了一种解决方法(由于 MS 似乎没有很好地记录 API 行为更改,因此需要)。显然,如果证书的操作系统默认验证失败,则 HttpRequestMessage::TransportInformation 属性中不再提供服务器证书详细信息。要获取服务器证书详细信息,需要添加(内部版本 13493 中的新)ServerCustomValidationRequested 事件的处理程序。对于我的特定情况(自签名、不受信任的证书),我还必须将 ChainValidationResult.Untrusted 添加到 HttpBaseProtocolFilter 中的 IgnorableServerCertificateErrors 属性中。在此之后,ServerCustomValidationRequested 被触发,我能够获取服务器证书详细信息。修改后的功能如下。

    顺便说一句,我还注意到,一旦使用 HttpServerCustomValidationRequestedEventArgs::Reject 处理了 ServerCustomValidationRequested 并拒绝了证书,就会填充 HttpRequestMessage.TransportInformation。

    如果 MS 更好地记录了这种行为变化,我本可以避免很多时间浪费和悲伤。希望这对其他人有帮助。

            private async Task ConnectToServerHttpAsync(Uri connectUri)
            {
                HttpRequestMessage req = null;
                try
                {
                    using (HttpBaseProtocolFilter bpf = new HttpBaseProtocolFilter())
                    {
                        bpf.AllowUI = false;
                        bpf.ServerCustomValidationRequested += ServerCustomValidationRequested;
                        bpf.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
                        using (HttpClient httpClient = new HttpClient(bpf))
                        {
                            req = new HttpRequestMessage(HttpMethod.Get, connectUri);
                            using (HttpResponseMessage res = await httpClient.SendRequestAsync(req))
                            {
                                Status = ((int)(res.StatusCode)) + " " + res.ReasonPhrase;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    SocketErrorStatus eSocketErrorStatus = SocketError.GetStatus(ex.HResult);
                    Status = eSocketErrorStatus.ToString();
                    Status = req?.TransportInformation?.ServerCertificate?.ToString() ?? "No server certificate.";
                }
                req?.Dispose();
            }
    
            private void ServerCustomValidationRequested(HttpBaseProtocolFilter sender, HttpServerCustomValidationRequestedEventArgs customValidationArgs)
            {
                Status = "-----ServerCustomValidationRequested-----";
                Status = "Certificate details:";
                Status = "Friendly name: " + customValidationArgs.ServerCertificate.FriendlyName;
                Status = "Issuer: " + customValidationArgs.ServerCertificate.Issuer;
                Status = "SignatureAlgorithmName: " + customValidationArgs.ServerCertificate.SignatureAlgorithmName;
                Status = "SignatureHashAlgorithmName: " + customValidationArgs.ServerCertificate.SignatureHashAlgorithmName;
                Status = "Subject: " + customValidationArgs.ServerCertificate.Subject;
                Status = "ValidFrom: " + customValidationArgs.ServerCertificate.ValidFrom.ToString();
                Status = "ValidTo: " + customValidationArgs.ServerCertificate.ValidTo.ToString();
    
                ServerCert = customValidationArgs.ServerCertificate;
    // Validate the server certificate as required.
    //            customValidationArgs.Reject();
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多