2016-11-16 5 views
0

Meine UWP-App stellt eine Verbindung zu einem http (s) -Server her, um Daten zu übertragen. Beim Erstellen einer TLS-Verbindung bietet die HttpRequestMessage.TransportInformation Informationen über die spezifischen Fehler und das Serverzertifikat, mit denen wir eine Nachricht für den Endbenutzer anzeigen, wenn ein Fehler aufgrund eines Handshake-Fehlers auftritt.UWP HttpRequestMessage.TransportInformation fehlt

Alle oben genannten funktionierten einwandfrei, bis ich meinen dev-Computer auf Windows 10 Build 1607 aktualisierte und die App auf "Windows 10 Anniversary Edition (10.0; Build 14393)" umstellte. Früher war dies "Windows 10 (10.0; Build 10586)".

Nach dieser Änderung sind alle Felder in der HttpRequestMessage.TransportInformation null. Die Ausnahme und das entsprechende HRESULT weisen jedoch eindeutig auf den SSL-Fehler hin (in diesem Fall nicht vertrauenswürdiges Serverzertifikat).

Ich habe versucht, StreamSocket zu verwenden, und sicher genug ein SSL-Handshake-Fehler, aber die StreamSocket.Information -Eigenschaft hatte alle Felder (Server-Zertifikat, Fehler usw.) ordnungsgemäß ausgefüllt, so dass sie untersucht werden konnten.

Für das Protokoll ist das Serverzertifikat selbst signiert und verwendet SHA1 Daumen/Signatur-Algorithmus.

In den folgenden Codebeispielen gibt req.TransprtInformation in ConnectToServerHttpAsync niemals das Serverzertifikat zurück, während streamSock.Information Serverzertifikatdetails in ConnectToServerAsync bereitstellt.

Frage: Ist das ein Fehler im neueren SDK oder muss ich etwas anderes mit dem HttpClient auf Build 14393 tun, um Transportinformationen zu erhalten? Habe bei MSDN oder SO nichts über dieses Verhalten gefunden und daher nichts gepostet.

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

Antwort

0

Es scheint, ich könnte eine Abhilfe gefunden haben (erforderlich aufgrund was scheint, ein nicht sehr gut dokumentierte API Verhaltensänderung von MS zu sein). Offensichtlich sind die Serverzertifikatdetails in der HttpRequestMessage :: TransportInformation-Eigenschaft nicht mehr verfügbar, wenn die BS-Standardüberprüfung des Zertifikats fehlschlägt. Zum Abrufen der Serverzertifikatdetails muss ein Handler für das Ereignis (neu in Build 13493) ServerCustomValidationRequested hinzugefügt werden. Für meinen speziellen Fall (selbstsigniertes, nicht vertrauenswürdiges Zertifikat) musste ich ChainValidationResult.Notrusted der IgnorableServerCertificateErrors-Eigenschaft in HttpBaseProtocolFilter hinzufügen. Danach wird die ServerCustomValidationRequested ausgelöst und ich kann die Serverzertifikatdetails abrufen. Die modifizierte Funktion ist wie folgt.

Übrigens bemerkte ich auch, sobald die ServerCustomValidationRequested behandelt und das Zertifikat abgelehnt wird, mit HttpServerCustomValidationRequestedEventArgs :: Reject, die HttpRequestMessage.TransportInformation wird aufgefüllt.

Hatte MS dokumentiert dieses Verhalten besser zu ändern, hätte ich viel Zeitverschwendung und Kummer vermeiden können. Ich hoffe, dies ist hilfreich für andere.

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