Zulassen nicht vertrauenswürdiger SSL-Zertifikate mit HttpClient

114

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

Es scheint, dass HttpClient / HttpClientHandler keine Option zum Ignorieren nicht vertrauenswürdiger Zertifikate bietet, wie es WebRequest Ihnen ermöglicht (wenn auch auf "hackige" Weise mit ServerCertificateValidationCallback).

Jede Hilfe wäre sehr dankbar!

Jamie
quelle
5
Wenn Sie .NET Core verwenden, könnte Sie diese Antwort interessieren .
Kdaveid

Antworten:

13

Unter Windows 8.1 können Sie jetzt 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, den von mir geschriebenen Message-Handler-Adapter verwenden: http://www.nuget.org/packages/WinRtHttpClientHandler

Die Dokumente befinden sich auf dem GitHub: https://github.com/onovotny/WinRtHttpClientHandler

Claire Novotny
quelle
12
Gibt es eine Möglichkeit, dies zu tun, ohne Ihren gesamten Code zu verwenden? Mit anderen Worten, was ist der Kern Ihrer Lösung?
Wensveen
Der Kern der Lösung besteht darin, den Windows-HTTP-Client-Handler zu verpacken und als Implementierung des HttpClient zu verwenden. Es ist alles in dieser Datei: github.com/onovotny/WinRtHttpClientHandler/blob/master/… Sie können es gerne kopieren, sind sich aber nicht sicher, warum Sie nicht einfach das Paket verwenden.
Claire Novotny
@OrenNovotny, damit Ihre Lösung nicht an die Windows-Version gebunden ist?
Chester89
Ich habe dies in meiner UWP-App implementiert und das folgende Filterbeispiel verwendet. Ich bekomme immer noch den gleichen Fehler.
Christian Findlay
158

Eine schnelle und schmutzige Lösung besteht darin, den ServicePointManager.ServerCertificateValidationCallbackDelegaten zu verwenden. Auf diese Weise können Sie Ihre eigene Zertifikatvalidierung bereitstellen. Die Validierung wird global auf die gesamte 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 oder dem zu treffen HttpClient.

Für Produktionscode möchten Sie möglicherweise eine feinkörnigere Steuerung und sollten die Eigenschaft WebRequestHandlerund ihre ServerCertificateValidationCallbackDelegate-Eigenschaft verwenden (siehe die Antwort von dtb unten ). Oder antworten Sie mit dem HttpClientHandler. Ich bevorzuge jetzt beide bei meinen Integrationstests gegenüber meiner bisherigen Vorgehensweise, es sei denn, ich kann keinen anderen Haken finden.

Bronumski
quelle
31
Nicht der Downvoter, aber eines der größten Probleme mit dem ServerCertificateValidationCallback ist, dass es für Ihre AppDomain grundsätzlich global ist. Wenn Sie also eine Bibliothek schreiben, die eine Site mit einem nicht vertrauenswürdigen Zertifikat aufrufen und diese Problemumgehung anwenden muss, ändern Sie das Verhalten der gesamten Anwendung, nicht nur Ihrer Bibliothek. Außerdem sollten die Leute immer vorsichtig sein mit dem Ansatz, blind wahr zurückzugeben. Dies hat schwerwiegende Auswirkungen auf die Sicherheit. Es sollte / * die angegebenen Parameter lesen und dann sorgfältig entscheiden, ob * / true zurückgegeben werden soll;
Scottt732
@ scottt732 Sicherlich ein gültiger Punkt und erwähnenswert, aber es ist immer noch eine gültige Lösung. Vielleicht war ihm nach dem erneuten Lesen der ursprünglichen Frage durch das OP der ServerCertificateValidationCallback-Handler
Bronumski
2
Ich würde empfehlen, zumindest nach einigen Kriterien wie Absender zu filtern
Boas Enkler
2
Ich muss die WebRequestHandler-Option wirklich gegenüber dem globalen ServicePointManager empfehlen.
Thomas S. Trias
3
Sie müssen ServicePointManager nicht global verwenden, sondern können ServicePointManager.GetServicePoint(Uri) (siehe Dokumente ) verwenden, um den Servicepunkt abzurufen, der nur für Aufrufe dieser URI gilt. Sie können dann Eigenschaften festlegen und Ereignisse basierend auf dieser Teilmenge verarbeiten.
Oatsoda
87

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

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
dtb
quelle
5
Vielen Dank für die Antwort, aber ich habe mich bereits damit befasst - es ist in .NET für Windows 8 Store-Apps nicht vorhanden.
Jamie
Das Erstellen einer eigenen von HttpClientHandler oder HttpMessageHandler abgeleiteten Klasse ist jedoch einfach genug, insbesondere angesichts der Tatsache, dass die WebRequestHandler-Quelle leicht verfügbar ist.
Thomas S. Trias
@ ThomasS.Trias irgendein Beispiel darüber? derived class?
Kiquenet
2
Verwenden HttpClientHandler?
Kiquenet
1
@Kiquenet diese Antwort ist aus dem Jahr 2012, es ist bereits 6 Jahre alt, und ja - viele Leute waren sich diesmal sicher, dass dies der richtige Weg ist, um httpclient zu verwenden :)
justmara
63

Wenn Sie versuchen, dies in einer .NET Standard-Bibliothek zu tun, finden Sie hier eine einfache Lösung mit allen Risiken einer Rückkehr truein Ihren Handler. Ich überlasse die Sicherheit Ihnen.

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

var client = new HttpClient(handler);
ctacke
quelle
1
Dies führt dazu, dass die Plattform nicht unterstützt wird, wenn auf uwp verwiesen wird
Ivan
arbeitete für mich in UWP. Vielleicht ist die Support-Deckung in 14 Monaten gereift. Hinweis: Dieser Hack sollte nur in test / dev env erforderlich sein (wo selbstsignierte Zertifikate verwendet werden)
Ben McIntyre
Ich habe diesen Code ausgeführt und immer die System.NotImplementedException erfüllt. Ich weiß nicht warum.
Liu Feng
25

Oder Sie können für den HttpClient im Windows.Web.HttpNamespace 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)) {
    ...
}
dschüsä
quelle
Kann ich System.Net.Http, System.Webund das Windows.Web.Httpzusammen?
Kiquenet
2
HttpClient scheint diese Überschreibung in .NET Standard oder UWP nicht zu haben.
Christian Findlay
Verwenden Sie nicht das "using" -Muster: docs.microsoft.com/en-us/azure/architecture/antipatterns/…
Bernhard
15

Wenn Sie verwenden, System.Net.Http.HttpClientglaube ich, ist das richtige Muster

var handler = new HttpClientHandler() 
{ 
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};

var http = new HttpClient(handler);
var res = http.GetAsync(url);
Jakub Šturc
quelle
8

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 erklärt Ihnen warum:

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

In Bezug auf Ihre 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;
  }
 }
}
Bernhard
quelle
Die Frage bezieht sich auf die Vertrauensbeziehung des selbstsignierten Zertifikats. Bei Ihrer gesamten Antwort geht es darum, den HttpClient-Thread sicher zu machen.
Adarsha
1
Es geht nicht nur um "Thread-Sicherheit". Ich wollte die "richtige" Art der Verwendung des httpclient mit "deaktivierter" SSL-Überprüfung zeigen. Andere Antworten "global" ändern den Zertifikatmanager.
Bernhard
7

Wenn dies für eine Windows Runtime-Anwendung gilt, müssen Sie dem Projekt das selbstsignierte Zertifikat hinzufügen und im Appxmanifest darauf verweisen.

Die Dokumente finden Sie hier: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Dasselbe gilt, wenn es sich um eine nicht vertrauenswürdige Zertifizierungsstelle handelt (wie eine private Zertifizierungsstelle, der der Computer selbst nicht vertraut). Sie müssen das öffentliche Zertifikat der Zertifizierungsstelle abrufen, es als Inhalt zur App hinzufügen und dann dem Manifest hinzufügen.

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

Claire Novotny
quelle
2

Ich habe keine Antwort, aber ich habe eine Alternative.

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

Um die HTTPS-Verschlüsselung in Fiddler2 zu aktivieren, gehen Sie zu Extras > Fiddler-Optionen> HTTPS (Registerkarte)> Aktivieren Sie "HTTPS-Verkehr entschlüsseln" .

Ich werde diesen Thread im Auge behalten und hoffe, dass jemand eine elegante Lösung findet.

Laith
quelle
2

Ich fand ein Beispiel in diesem Kubernetes Client , wo sie wurden mit X509VerificationFlags.AllowUnknownCertificateAuthority zu vertrauen selbstsignierten selbstsignierten Stammzertifikate. 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>();
  }
}
PaulB
quelle
1

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

Verwenden Sie dies dann einfach, bevor Sie Ihre http-Anfrage wie folgt senden:

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

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

TombMedia
quelle
1
ServicePointManager.CertificatePolicyist veraltet: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop