it-swarm.com.de

Zulassen nicht vertrauenswürdiger SSL-Zertifikate mit HttpClient

Ich habe Schwierigkeiten, meine Windows 8-Anwendung dazu zu bringen, mit meiner Test-Web-API über SSL zu kommunizieren.

Es scheint, dass HttpClient/HttpClientHandler nicht die Möglichkeit bietet, nicht vertrauenswürdige Zertifikate zu ignorieren, wie WebRequest dies ermöglicht (wenn auch mit ServerCertificateValidationCallback "hacky").

Jede Hilfe wäre sehr dankbar!

79
Jamie

Mit Windows 8.1 können Sie nun ungültigen SSL-Zertifikaten vertrauen. Sie müssen entweder den Windows.Web.HttpClient verwenden, oder wenn Sie den System.Net.Http.HttpClient verwenden möchten, können Sie den Message-Handler-Adapter verwenden, den ich geschrieben habe: http://www.nuget.org/packages/WinRtHttpClientHandler

Dokumente befinden sich im GitHub: https://github.com/onovotny/WinRtHttpClientHandler

9
Oren Novotny

Eine schnelle und schmutzige Lösung ist die Verwendung des ServicePointManager.ServerCertificateValidationCallback - Delegaten. Auf diese Weise können Sie Ihre eigene Zertifikatvalidierung bereitstellen. Die Validierung wird global in der gesamten App-Domäne angewendet.

ServicePointManager.ServerCertificateValidationCallback +=
    (sender, cert, chain, sslPolicyErrors) => true;

Ich verwende dies hauptsächlich für Unit-Tests in Situationen, in denen ich gegen einen Endpunkt laufen möchte, den ich gerade hoste, und versuche, ihn mit einem WCF-Client zu treffen oder mit der HttpClient .

Für den Produktionscode möchten Sie möglicherweise eine feinere Kontrolle, und Sie sollten die Option WebRequestHandler und ihre ServerCertificateValidationCallback delegate-Eigenschaft verwenden (Siehe dtb's Antwort unten ). Oder ctacke answer mit der HttpClientHandler . Ich ziehe jetzt bei meinen Integrationstests den beiden lieber vor als früher, wenn ich keinen anderen Hook finde.

116
Bronumski

Schauen Sie sich die WebRequestHandler-Klasse und ihre ServerCertificateValidationCallback-Eigenschaft an:

using (var handler = new WebRequestHandler())
{
    handler.ServerCertificateValidationCallback = ...

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
69
dtb

Wenn Sie versuchen, dies in einer .NET-Standardbibliothek auszuführen, haben wir hier eine einfache Lösung mit allen Risiken, nur true in Ihrem Handler zurückzugeben. Ich überlasse Ihnen die Sicherheit.

var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateCustomValidationCallback = 
    (httpRequestMessage, cert, cetChain, policyErrors) =>
{
    return true;
};

var client = new HttpClient(handler);
21
ctacke

Oder Sie können für den HttpClient im Windows.Web.Http-Namespace verwenden:

var filter = new HttpBaseProtocolFilter();
#if DEBUG
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Expired);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
    filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
#endif
using (var httpClient = new HttpClient(filter)) {
    ...
}
21
dschüsä

Wenn dies für eine Windows-Laufzeitanwendung gilt, müssen Sie das selbstsignierte Zertifikat zum Projekt hinzufügen und im appxmanifest darauf verweisen.

Die Dokumente sind hier: http://msdn.Microsoft.com/en-us/library/windows/apps/hh465031.aspx

Das Gleiche gilt, wenn es sich um eine nicht vertrauenswürdige Zertifizierungsstelle handelt (wie eine private Zertifizierungsstelle, der die Maschine selbst nicht vertraut) - Sie müssen das öffentliche Zertifizierungsstellenzertifikat der Zertifizierungsstelle erwerben, es als Inhalt zur App hinzufügen und dann zum Manifest hinzufügen.

Sobald dies erledigt ist, wird die App es als korrekt signiertes Zertifikat sehen.

6
Oren Novotny

Ich habe keine Antwort, aber ich habe eine Alternative. 

Wenn Sie Fiddler2 verwenden, um den Datenverkehr zu überwachen UND HTTPS-Entschlüsselung zu aktivieren, wird Ihre Entwicklungsumgebung keine Beschwerden verursachen. Dies funktioniert nicht auf WinRT-Geräten wie Microsoft Surface, da auf diesen keine Standard-Apps installiert werden können. Aber Ihr Win8-Entwicklungscomputer wird in Ordnung sein.

Um die HTTPS-Verschlüsselung in Fiddler2 zu aktivieren, wählen Sie Tools> Fiddler-Optionen> HTTPS (Registerkarte)> "Entschlüsseln von HTTPS-Datenverkehr".

Ich werde diesen Faden im Auge behalten und hoffen, dass jemand eine elegante Lösung hat. 

2
Laith

Ich habe online ein Beispiel gefunden, das gut zu funktionieren scheint:

Zuerst erstellen Sie eine neue ICertificatePolicy

using System.Security.Cryptography.X509Certificates;
using System.Net;

public class MyPolicy : ICertificatePolicy
{
  public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, 
int certificateProblem)
  {
    //Return True to force the certificate to be accepted.
    return true;
  }
}

Dann nutzen Sie einfach diese vor dem Senden Ihrer http-Anfrage wie folgt:

System.Net.ServicePointManager.CertificatePolicy = new MyPolicy();

http://www.terminally-incoherent.com/blog/2008/05/05/send-a-https-post-request-with-c/

1
TombMedia

Ich habe in diesem Kubernetes-Client ein Beispiel gefunden, in dem X509VerificationFlags.AllowUnknownCertificateAuthority verwendet wurde, um selbstsignierten selbstsignierten Stammzertifikaten zu vertrauen. Ich habe ihr Beispiel leicht überarbeitet, um mit unseren eigenen PEM-codierten Stammzertifikaten zu arbeiten. Hoffentlich hilft das jemandem.

namespace Utils
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net.Security;
  using System.Security.Cryptography.X509Certificates;

  /// <summary>
  /// Verifies that specific self signed root certificates are trusted.
  /// </summary>
  public class HttpClientHandler : System.Net.Http.HttpClientHandler
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="HttpClientHandler"/> class.
    /// </summary>
    /// <param name="pemRootCerts">The PEM encoded root certificates to trust.</param>
    public HttpClientHandler(IEnumerable<string> pemRootCerts)
    {
      foreach (var pemRootCert in pemRootCerts)
      {
        var text = pemRootCert.Trim();
        text = text.Replace("-----BEGIN CERTIFICATE-----", string.Empty);
        text = text.Replace("-----END CERTIFICATE-----", string.Empty);
        this.rootCerts.Add(new X509Certificate2(Convert.FromBase64String(text)));
      }

      this.ServerCertificateCustomValidationCallback = this.VerifyServerCertificate;
    }

    private bool VerifyServerCertificate(
      object sender,
      X509Certificate certificate,
      X509Chain chain,
      SslPolicyErrors sslPolicyErrors)
    {
      // If the certificate is a valid, signed certificate, return true.
      if (sslPolicyErrors == SslPolicyErrors.None)
      {
        return true;
      }

      // If there are errors in the certificate chain, look at each error to determine the cause.
      if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
      {
        chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

        // add all your extra certificate chain
        foreach (var rootCert in this.rootCerts)
        {
          chain.ChainPolicy.ExtraStore.Add(rootCert);
        }

        chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
        var isValid = chain.Build((X509Certificate2)certificate);

        var rootCertActual = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
        var rootCertExpected = this.rootCerts[this.rootCerts.Count - 1];
        isValid = isValid && rootCertActual.RawData.SequenceEqual(rootCertExpected.RawData);

        return isValid;
      }

      // In all other cases, return false.
      return false;
    }

    private readonly IList<X509Certificate2> rootCerts = new List<X509Certificate2>();
  }
}
0
PaulB

Die meisten Antworten hier schlagen vor, das typische Muster zu verwenden:

using (var httpClient = new HttpClient())
{
 // do something
}

wegen der IDisposable-Schnittstelle. Bitte nicht!

Microsoft sagt Ihnen warum:

Und hier finden Sie eine detaillierte Analyse, was hinter den Kulissen vor sich geht: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Bezüglich Ihrer SSL-Frage und basierend auf https://docs.Microsoft.com/en-us/Azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Hier ist dein Muster:

class HttpInterface
{
 // https://docs.Microsoft.com/en-us/Azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem
 // https://docs.Microsoft.com/en-us/dotnet/api/system.net.http.httpclient#remarks
 private static readonly HttpClient client;

 // static initialize
 static HttpInterface()
 {
  // choose one of these depending on your framework

  // HttpClientHandler is an HttpMessageHandler with a common set of properties
  var handler = new HttpClientHandler();
  {
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };
  // derives from HttpClientHandler but adds properties that generally only are available on full .NET
  var handler = new WebRequestHandler()
  {
      ServerCertificateValidationCallback = delegate { return true; },
      ServerCertificateCustomValidationCallback = delegate { return true; },
  };

  client = new HttpClient(handler);
 }

 .....

 // in your code use the static client to do your stuff
 var jsonEncoded = new StringContent(someJsonString, Encoding.UTF8, "application/json");

 // here in sync
 using (HttpResponseMessage resultMsg = client.PostAsync(someRequestUrl, jsonEncoded).Result)
 {
  using (HttpContent respContent = resultMsg.Content)
  {
   return respContent.ReadAsStringAsync().Result;
  }
 }
}
0
Bernhard