Ungültiges SSL-Zertifikat im .net-Kern umgehen


93

Ich arbeite an einem Projekt, das eine Verbindung zu einer https-Site herstellen muss. Jedes Mal, wenn ich eine Verbindung herstelle, löst mein Code eine Ausnahme aus, da das Zertifikat dieser Site von einer nicht vertrauenswürdigen Site stammt. Gibt es eine Möglichkeit, die Zertifikatsprüfung in .net core http zu umgehen?

Ich habe diesen Code aus einer früheren Version von .NET gesehen. Ich brauche wohl so etwas.

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

Antworten:


26

ServicePointManager.ServerCertificateValidationCallback wird in .Net Core nicht unterstützt.

Derzeit handelt es sich um eine neue ServerCertificateCustomValidationCallback- Methode für den kommenden 4.1. * System.Net.Http-Vertrag (HttpClient). Das .NET Core-Team schließt jetzt den 4.1-Vertrag ab. Sie können darüber hier auf Github lesen

Sie können die Vorabversion von System.Net.Http 4.1 ausprobieren, indem Sie die Quellen direkt hier in CoreFx oder im MYGET-Feed verwenden: https://dotnet.myget.org/gallery/dotnet-core

Aktuelle WinHttpHandler.ServerCertificateCustomValidationCallback- Definition auf Github


8
Dies funktioniert nur unter Windows. Haben Sie eine Lösung für Linux? Vielen Dank.
Vladimir

140

Aktualisieren:

Wie unten erwähnt, unterstützen nicht alle Implementierungen diesen Rückruf (dh Plattformen wie iOS). In diesem Fall können Sie, wie in den Dokumenten angegeben , den Validator explizit festlegen:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

Dies funktioniert auch für .NET Core 2.2, 3.0 und 3.1

Alte Antwort , mit mehr Kontrolle, kann aber werfen PlatformNotSupportedException:

Sie können die SSL-Zertifikatsprüfung bei einem HTTP-Aufruf mit einer solchen anonymen Rückruffunktion überschreiben

using (var httpClientHandler = new HttpClientHandler())
{
   httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
   using (var client = new HttpClient(httpClientHandler))
   {
       // Make your request...
   }
}

Außerdem empfehle ich, ein Factory-Muster zu verwenden, HttpClientda es sich um ein freigegebenes Objekt handelt, das möglicherweise nicht sofort entsorgt werden kann und daher die Verbindungen offen bleiben .


2
Ich verwende .Net Core 1.0 und das hat bei mir funktioniert. Als Heads-up sieht es so aus, als hätte .Net Core 2.0 eine HttpClientEigenschaft namens aufgerufen DangerousAcceptAnyServerCertificateValidator, mit der dies unter MacOSX funktioniert. Weitere Infos hier - github.com/dotnet/corefx/pull/19908
Troy Witthoeft

Durch die Verwendung von AWS Lambda in .NET Core 1.0 wurde korrigiert, was mich daran hinderte, mit einem benutzerdefinierten Stammzertifizierungsstellenzertifikat eine Verbindung zu einem internen HTTPS herzustellen.
QuickNull

Irgendwelche factory patternfür HttpClient?
Kiquenet

@Kiquenet Erstellen Sie einfach eine Factory, in der die GetHttpClientMethode die konfigurierte zurückgibt, HttpClientund verwenden Sie sie innerhalb eines usingBlocks.
LuckyLikey

Dies sollte die akzeptierte Antwort sein, insbesondere da sie auf einen einzelnen Client beschränkt sein kann.
BinaryPatrick

32

Ich löse damit:

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient("HttpClientWithSSLUntrusted").ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            ClientCertificateOptions = ClientCertificateOption.Manual,
            ServerCertificateCustomValidationCallback =
            (httpRequestMessage, cert, cetChain, policyErrors) =>
            {
                return true;
            }
        });

YourService.cs

public UserService(IHttpClientFactory clientFactory, IOptions<AppSettings> appSettings)
    {
        _appSettings = appSettings.Value;
        _clientFactory = clientFactory;
    }

var request = new HttpRequestMessage(...

var client = _clientFactory.CreateClient("HttpClientWithSSLUntrusted");

HttpResponseMessage response = await client.SendAsync(request);

30

Kam hier auf der Suche nach einer Antwort auf das gleiche Problem, aber ich verwende WCF für NET Core. Wenn Sie im selben Boot sitzen, verwenden Sie:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };

Global für alle Zertifikate und AppDomain?
Kiquenet

@ Kiquenet: Ich glaube schon. Suchen Sie an anderer Stelle nach einer aktualisierten Antwort. Möglicherweise gibt es jetzt eine bessere Lösung. Es ist ein Jahr her. Ich denke, Sie könnten den Authentifikator unterordnen, wenn sonst nichts. Und nein, es gibt keine native Fabrik für HttpClient, die ich kenne. Wenn Sie weitere Funktionen benötigen, schauen Sie sich RestClient an.
Troels Larsen

In HttpClient (.NET Core 3.1) gibt es keine ClientCredentials-Eigenschaft.
23авле

@ Павле: Ich habe dieses Projekt noch nicht auf 3.1 aktualisiert, aber es sollte eine solche Eigenschaft geben: docs.microsoft.com/en-us/dotnet/api/… .
Troels Larsen

12

In .NetCore können Sie das folgende Code-Snippet bei der Services Configure-Methode hinzufügen. Ich habe eine Überprüfung hinzugefügt, um sicherzustellen, dass nur das SSL-Zertifikat nur in der Entwicklungsumgebung übergeben wird

services.AddHttpClient("HttpClientName", client => {
// code to configure headers etc..
}).ConfigurePrimaryHttpMessageHandler(() => {
                  var handler = new HttpClientHandler();
                  if (hostingEnvironment.IsDevelopment())
                  {
                      handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
                  }
                  return handler;
              });

Warum das -ve, dies implementiert genau das, was andere im mvc.net-Code vorgeschlagen haben und sie haben Punkte darauf erzielt. Ich illustriere nur die gleiche Implementierung im .netCore-Code
Sameh

wahrscheinlich. weil es keinerlei Erklärung gibt. Warum sollte dieser Ansatz einen anderen übernehmen? Welcher Code sollte im aufrufenden Abschnitt (z. B. mycontroller.cs) geschrieben werden, der Teil der Erklärung sein könnte. jegliche offizielle Dokumentation / Zitierung.
Bhanu Chhabra

Wie ich schon sagte, wenn Sie andere Kommentare oben im Thread überprüft haben, gibt es keinen großen Unterschied und dennoch erzielten sie 18 und 81 Punkte
Sameh

1
Da sie Text hinzugefügt haben, der ihre Antworten unterstützt, lesen Sie bitte die Richtlinien noch einmal. Könnte Ihnen helfen, @moderators kann meiner Meinung nach auf die genauen Probleme hinweisen.
Bhanu Chhabra

7

Ich hatte das gleiche Problem, als ich mit selbstsignierten Zertifikaten und Client-Zertifikatsauthentifizierung auf .NET Core 2.2- und Docker Linux-Containern arbeitete. Auf meinem Windows-Entwickler hat alles gut funktioniert, aber in Docker habe ich folgende Fehlermeldung erhalten:

System.Security.Authentication.AuthenticationException: Das Remote-Zertifikat ist gemäß dem Validierungsverfahren ungültig

Glücklicherweise wurde das Zertifikat mithilfe einer Kette generiert. Natürlich können Sie diese Lösung jederzeit ignorieren und die oben genannten Lösungen verwenden.

Also hier ist meine Lösung:

  1. Ich habe das Zertifikat mit Chrome auf meinem Computer im P7B- Format gespeichert.

  2. Konvertieren Sie das Zertifikat mit diesem Befehl in das PEM-Format:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. Öffnen Sie die Datei ca_bundle.crt, löschen Sie alle Betreff-Aufzeichnungen und hinterlassen Sie eine saubere Datei. Beispiel unten:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. Fügen Sie diese Zeilen in die Docker-Datei ein (in den letzten Schritten):
    # Update system and install curl and ca-certificates
    RUN apt-get update && apt-get install -y curl && apt-get install -y ca-certificates
    # Copy your bundle file to the system trusted storage
    COPY ./ca_bundle.crt /usr/local/share/ca-certificates/ca_bundle.crt
    # During docker build, after this line you will get such output: 1 added, 0 removed; done.
    RUN update-ca-certificates
  1. In der App:
    var address = new EndpointAddress("https://serviceUrl");                
    var binding = new BasicHttpsBinding
    {
        CloseTimeout = new TimeSpan(0, 1, 0),
        OpenTimeout = new TimeSpan(0, 1, 0),
        ReceiveTimeout = new TimeSpan(0, 1, 0),
        SendTimeout = new TimeSpan(0, 1, 0),
        MaxBufferPoolSize = 524288,
        MaxBufferSize = 65536,
        MaxReceivedMessageSize = 65536,
        TextEncoding = Encoding.UTF8,
        TransferMode = TransferMode.Buffered,
        UseDefaultWebProxy = true,
        AllowCookies = false,
        BypassProxyOnLocal = false,
        ReaderQuotas = XmlDictionaryReaderQuotas.Max,
        Security =
        {
            Mode = BasicHttpsSecurityMode.Transport,
            Transport = new HttpTransportSecurity
            {
                ClientCredentialType = HttpClientCredentialType.Certificate,
                ProxyCredentialType = HttpProxyCredentialType.None
            }
        }
    };
    var client = new MyWSClient(binding, address);
    client.ClientCredentials.ClientCertificate.Certificate = GetClientCertificate("clientCert.pfx", "passwordForClientCert");
    // Client certs must be installed
    client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication
    {
        CertificateValidationMode = X509CertificateValidationMode.ChainTrust,
        TrustedStoreLocation = StoreLocation.LocalMachine,
        RevocationMode = X509RevocationMode.NoCheck
    };

GetClientCertificate-Methode:

private static X509Certificate2 GetClientCertificate(string clientCertName, string password)
{
    //Create X509Certificate2 object from .pfx file
    byte[] rawData = null;
    using (var f = new FileStream(Path.Combine(AppContext.BaseDirectory, clientCertName), FileMode.Open, FileAccess.Read))
    {
        var size = (int)f.Length;
        var rawData = new byte[size];
        f.Read(rawData, 0, size);
        f.Close();
    }
    return new X509Certificate2(rawData, password);
}

3

Erstens, verwenden Sie es nicht in der Produktion

Wenn Sie die AddHttpClient-Middleware verwenden, ist dies hilfreich. Ich denke, es wird für Entwicklungszwecke benötigt, nicht für die Produktion. Bis Sie ein gültiges Zertifikat erstellen, können Sie diese Funktion verwenden.

Func<HttpMessageHandler> configureHandler = () =>
        {
            var bypassCertValidation = Configuration.GetValue<bool>("BypassRemoteCertificateValidation");
            var handler = new HttpClientHandler();
            //!DO NOT DO IT IN PRODUCTION!! GO AND CREATE VALID CERTIFICATE!
            if (bypassCertValidation)
            {
                handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, x509Certificate2, x509Chain, sslPolicyErrors) =>
                {
                    return true;
                };
            }
            return handler;
        };

und wende es gerne an

services.AddHttpClient<IMyClient, MyClient>(x => { x.BaseAddress = new Uri("https://localhost:5005"); })
        .ConfigurePrimaryHttpMessageHandler(configureHandler);

1

Das Zulassen aller Zertifikate ist sehr leistungsfähig, kann aber auch gefährlich sein. Wenn Sie nur gültige Zertifikate und einige bestimmte Zertifikate zulassen möchten, können Sie dies folgendermaßen tun.

using (var httpClientHandler = new HttpClientHandler())
{
    httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => {
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            return true;   //Is valid
        }

        if (cert.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7")
        {
            return true;
        }
        return false;
    };
    using (var httpClient = new HttpClient(httpClientHandler))
    {
        var httpResponse = httpClient.GetAsync("https://example.com").Result;
    }
}

Originalquelle:

https://stackoverflow.com/a/44140506/3850405

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.