2016-11-16 83 views
0

我的UWP應用連接到http(s)服務器以執行一些數據傳輸。 在建立TLS連接時,如果由於握手失敗而發生故障,HttpRequestMessage.TransportInformation會提供有關特定錯誤和服務器證書的信息,我們正在使用該信息將消息顯示給最終用戶。UWP 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指紋/簽名算法。

在下面的代碼片段中,ConnectToServerHttpAsync中的req.TransprtInformation從不給服務器證書,而streamSock.Information在ConnectToServerAsync中提供服務器證書詳細信息。

問題:這是一個新的SDK中的錯誤,還是我必須做一些不同的與構建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; 
    } 
} 

回答

0

看來我可能已經找到一種解決方法(需要因這似乎是由MS一個不是很好的記錄API行爲的改變)。顯然,如果操作系統默認的證書驗證失敗,服務器證書詳細信息將不再在HttpRequestMessage :: TransportInformation屬性中可用。要獲得服務器證書詳細信息,需要添加處理程序(新建版本13493)ServerCustomValidationRequested事件。對於我的具體情況(自簽名,不受信任的證書),我還必須將ChainValidationResult.Untrusted添加到HttpBaseProtocolFilter中的IgnorableServerCertificateErrors屬性。在此之後,ServerCustomValidationRequested被激發,我能夠獲得服務器證書的詳細信息。修改後的功能如下。

順便說一句,我也注意到,一旦ServerCustomValidationRequested處理和證書被拒絕,使用HttpServerCustomValidationRequestedEventArgs ::拒絕,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(); 
     }