2016-07-08 4 views
4

Ich schreibe eine UWP-App in C#, die schließlich für IoT bestimmt ist, aber im Moment habe ich nur lokal debuggen. Ich verwende Windows.Web.Http.HttpClient, um eine Verbindung zu einem selbst gehosteten WCF-REST-Webdienst herzustellen, den ich auch geschrieben habe und der zum Testen als Konsolenanwendung auf demselben Computer ausgeführt wird. Der Dienst erfordert eine gegenseitige Authentifizierung mit Zertifikaten, also habe ich ein CA-Zertifikat, ein Service-Zertifikat und ein Client-Zertifikat.UWP App HttpClient HTTPS-Client-Zertifikat Probleme

Mein UWP Code funktioniert wie folgt:

  1. prüfen App cert Speicher für Client-Zertifikat und CA-Zertifikat installiert.
  2. Wenn nicht, installieren Sie von PFX-Datei bzw. CER-Datei.
  3. Bringen Sie die Certificate zum HttpBaseProtocolFilter und den Filter auf die die HttpClient.PostAsync

Nachdem ich PostAsyncHttpClient

  • Anruf hinzufügen nenne ich die folgende Fehlermeldung erhalten: An Error Occurred in the Secure Channel Support. Nach vielen Online-Suchen und mit gesundem Menschenverstand bin ich mir ziemlich sicher, dass HttpClient wegen eines Problems, das die gegenseitig authentifizierte SSL-Verbindung herstellt, barfing ist. Aber aufgrund meiner Fehlersuche kann ich nicht nachvollziehen, warum.

    Um Troubleshoot weiter, habe ich eine einfache alte Konsole app mit System.Net.Http.HttpClient geschrieben, fügte das Client-Zertifikat an die Anfrage und alles funktioniert super. Leider wird System.Net nicht vollständig von UWP unterstützt. Ich habe auch versucht, das Zertifikat nicht an die UWP HttpClient anzuhängen und die App fordert mich mit einer UI auf, ein installiertes Zertifikat auszuwählen. Ich wähle das korrekte Zertifikat aus und bekomme immer noch die selbe Ausnahme (dadurch weiß ich zumindest, dass das Zertifikat korrekt installiert wurde und mit der Zertifizierungsstelle aus Sicht der App korrekt validiert wird). Außerdem habe ich den GET im Web-Service von einem Browser aus getroffen, das Client-Zertifikat bei Aufforderung ausgewählt und kann eine Datei herunterladen.

    Ich habe versucht mit Fiddler und, ich nehme an, wegen der Art, wie es Verkehr Verkehr, scheint es ein wenig weiter zu arbeiten, außer mein Web-Service lehnt die Anfrage als Forbidden ab (vermutlich weil Fiddler nicht den richtigen Client enthält) Zertifikat in der Anfrage). Ich habe Wireshark noch nicht gefunden, weil es ein Problem ist, Wireshark mit localhost unter Windows zu arbeiten.

    Mein nächster Schritt besteht darin, den Webdienst zu ändern, um keine Clientauthentifizierung zu erfordern und zu sehen, ob das das Problem ist.

    Zwei Fragen: Warum funktioniert Windows.Web.Http.HttClient in diesem Fall nicht? Und, weniger wichtig, irgendwelche Empfehlungen zu guten HTTP-Überwachungstools, die mir helfen, das weiter zu debuggen?

  • +0

    diese selbst signierte Zertifikate sind? –

    +0

    Ich habe 'makecert' verwendet, um ein CA-Zertifikat zu erstellen und aus diesem CA-Zertifikat den Client und den Service mit' makecert' ausgegeben. – koopaking3

    +0

    Möglicherweise relevant: https://code.msdn.microsoft.com/windowsapps/How-to-ignore-Self-Signed-e50b89b6 –

    Antwort

    3

    Dieser MSDN-Beitrag erwies sich als die Antwort. Scheint wie ein Versehen bei MS, das vorher einen separaten, bedeutungslosen Aufruf der API erfordert. Naja.

    http://blogs.msdn.com/b/wsdevsol/archive/2015/03/26/how-to-use-a-shared-user-certificate-for-https-authentication-in-an-enterprise-application.aspx

    Auszug aus dem Artikel:

    Jedoch erfordert das Sicherheitssubsystem Bestätigung durch den Benutzer, bevor sie in dem gemeinsam genutzten Benutzerzertifikatspeicher gespeichert Zugriff auf einen Zertifikate privaten Schlüssel eines Zertifikats ermöglicht. Wenn ein Client-Zertifikat im Code angegeben wird, gehen die untergeordneten Netzwerkfunktionen davon aus, dass die Anwendung dies bereits erledigt hat und den Benutzer nicht zur Bestätigung auffordert.

    Wenn Sie sich die Windows-Runtime-Klassen im Zusammenhang mit Zertifikaten ansehen, finden Sie keine Methode, um explizit Zugriff auf den privaten Schlüssel des Zertifikats anzufordern. Was muss der App-Entwickler tun?

    Die Lösung besteht darin, das ausgewählte Zertifikat zu verwenden, um einige kleine Daten zu signieren. Wenn eine Anwendung CryptographicEngine.SignAsync aufruft, fordert der zugrunde liegende Code den Zugriff auf den privaten Schlüssel an, um die Signierung durchzuführen. Zu diesem Zeitpunkt wird der Benutzer gefragt, ob er der Anwendung den Zugriff auf den privaten Schlüssel des Zertifikats erlauben möchte. Beachten Sie, dass Sie die Async-Version dieser Funktion aufrufen müssen, da die synchrone Version der Funktion: Signieren eine Option verwendet, die die Anzeige des Bestätigungsdialogs blockiert.

    Zum Beispiel:

    public static async Task<bool> VerifyCertificateKeyAccess(Certificate selectedCertificate) 
    { 
        bool VerifyResult = false; // default to access failure 
        CryptographicKey keyPair = await PersistedKeyProvider.OpenKeyPairFromCertificateAsync(
                 selectedCertificate, HashAlgorithmNames.Sha1, 
                 CryptographicPadding.RsaPkcs1V15); 
        String buffer = "Data to sign"; 
        IBuffer Data = CryptographicBuffer.ConvertStringToBinary(buffer, BinaryStringEncoding.Utf16BE); 
    
        try 
        { 
         //sign the data by using the key 
         IBuffer Signed = await CryptographicEngine.SignAsync(keyPair, Data); 
         VerifyResult = CryptographicEngine.VerifySignature(keyPair, Data, Signed); 
        } 
        catch (Exception exp) 
        { 
         System.Diagnostics.Debug.WriteLine("Verification Failed. Exception Occurred : {0}", exp.Message); 
         // default result is false so drop through to exit. 
        } 
    
        return VerifyResult; 
    } 
    

    Sie können dann den früheren Codebeispiel ändern, diese Funktion aufzurufen, bevor das Client-Zertifikat, um mit der Anwendung hat Zugriff auf das Zertifikat privaten Schlüssel zu gewährleisten.

    0
    1. Fügen Sie das Zertifikat Project
    2. Fügen Sie das Zertifikat aus der manifestierten Datei Datei (Dateipfad in der Anlage geben)
    3. die Frist Dienst von Call in Ur-Projekt Verwendung der Zertifikatsüberprüfung zu ignorieren folgenden Code ist am meisten Geeignet für Login-Funktion.

    versuchen {

      var filter = new HttpBaseProtocolFilter(); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.WrongUsage); 
          filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain); 
    
          Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(filter); 
          TimeSpan span = new TimeSpan(0, 0, 60); 
          var cts = new CancellationTokenSource(); 
          cts.CancelAfter(span); 
          var request = new Windows.Web.Http.HttpRequestMessage() 
          { 
           RequestUri = new Uri(App.URL + "/oauth/token"), 
           Method = Windows.Web.Http.HttpMethod.Post, 
          }; 
          //request.Properties. = span; 
          string encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(Server_Username + ":" + Server_Password)); 
          var values = new Dictionary<string, string> 
           { { "grant_type", "password" },{ "username", Uname}, { "password", Pwd }}; 
          var content = new HttpFormUrlEncodedContent(values); 
          request.Headers.Add("Authorization", "Basic " + encoded); 
          request.Content = content; 
          User root = new User(); 
          using (Windows.Web.Http.HttpResponseMessage response = await client.SendRequestAsync(request).AsTask(cts.Token)) 
          { 
           HttpStatusCode = (int)response.StatusCode; 
           if (HttpStatusCode == (int)HttpCode.OK) 
           { 
            using (IHttpContent content1 = response.Content) 
            { 
             var jsonString = await content1.ReadAsStringAsync(); 
             root = JsonConvert.DeserializeObject<User>(jsonString); 
             App.localSettings.Values["access_token"] = root.Access_token; 
             App.localSettings.Values["refresh_token"] = root.Refresh_token; 
             App.localSettings.Values["expires_in"] = root.Expires_in; 
             var json = JsonConvert.SerializeObject(root.Locations); 
             App.localSettings.Values["LocationList"] = json; 
             App.localSettings.Values["LoginUser"] = Uname; 
            } 
           } 
          } 
         } 
         catch (Exception ex) 
         { 
          ex.ToString(); 
         }