ominąć nieprawidłowy certyfikat SSL w .net core

110

Pracuję nad projektem, który musi łączyć się z witryną https. Za każdym razem, gdy się łączę, mój kod generuje wyjątek, ponieważ certyfikat tej witryny pochodzi z niezaufanej witryny. Czy istnieje sposób na obejście sprawdzania certyfikatu w .net core http?

Widziałem ten kod z poprzedniej wersji .NET. Chyba po prostu potrzebuję czegoś takiego.

 ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Ramppy Dumppy
źródło

Odpowiedzi:

29

Usługa ServicePointManager.ServerCertificateValidationCallback nie jest obsługiwana w .Net Core.

Obecna sytuacja jest taka, że ​​będzie to nowa metoda ServerCertificateCustomValidationCallback dla przyszłego kontraktu 4.1. * System.Net.Http (HttpClient). Zespół .NET Core finalizuje teraz kontrakt 4.1. Możesz o tym przeczytać tutaj na githubie

Możesz wypróbować przedpremierową wersję System.Net.Http 4.1, korzystając ze źródeł bezpośrednio tutaj w CoreFx lub w kanale MYGET: https://dotnet.myget.org/gallery/dotnet-core

Bieżąca definicja WinHttpHandler.ServerCertificateCustomValidationCallback w serwisie Github

Zestaw
źródło
8
Działa to tylko w systemie Windows. Czy masz rozwiązanie dla systemu Linux? Dzięki.
Vladimir
154

Aktualizacja:

Jak wspomniano poniżej, nie wszystkie implementacje obsługują to wywołanie zwrotne (np. Platformy takie jak iOS). W tym przypadku, jak mówią dokumenty , możesz jawnie ustawić walidator:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

Działa to również w przypadku .NET Core 2.2, 3.0 i 3.1

Stara odpowiedź , z większą kontrolą, ale może rzucić PlatformNotSupportedException:

Możesz zastąpić sprawdzanie certyfikatu SSL w wywołaniu HTTP za pomocą funkcji anonimowego wywołania zwrotnego, takiej jak ta

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

Dodatkowo sugeruję użycie wzorca fabrycznego, HttpClientponieważ jest to obiekt współdzielony, który może nie zostać natychmiast usunięty, a zatem połączenia pozostaną otwarte .

kdaveid
źródło
3
Używam .Net Core 1.0 i to zadziałało. Na pierwszy rzut oka wygląda na to, że .Net Core 2.0 dodał HttpClientwłaściwość o nazwie, DangerousAcceptAnyServerCertificateValidatorktóra umożliwia wykonanie tego zadania w systemie MacOSX. Więcej informacji tutaj - github.com/dotnet/corefx/pull/19908
Troy Witthoeft
1
Używając tego z AWS Lambda, .NET Core 1.0 poprawił to, co uniemożliwiało mi połączenie się z wewnętrznym HTTPS z niestandardowym certyfikatem głównego urzędu certyfikacji.
QuickNull
Każdy factory patterndla HttpClient?
Kiquenet
@Kiquenet Po prostu stwórz fabrykę, w której GetHttpClientMetoda zwraca skonfigurowane HttpClienti użyj jej w using-blocku.
LuckyLikey
Powinna to być akceptowana odpowiedź, zwłaszcza, że ​​można ją zastosować tylko dla jednego klienta.
BinaryPatrick
40

Rozwiązuję tym:

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);
O.Machado
źródło
35

Przyszedłem tutaj, szukając odpowiedzi na ten sam problem, ale używam WCF dla NET Core. Jeśli jesteś na tej samej łodzi, użyj:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };
Troels Larsen
źródło
Globalny dla wszystkich certyfikatów i domeny AppDomain?
Kiquenet
@Kiquenet: Myślę, że tak. Poszukaj zaktualizowanej odpowiedzi w innym miejscu, teraz może być lepsze rozwiązanie. To był rok. Wydaje mi się, że możesz podklasować autentykator, jeśli nic innego. I nie, nie ma natywnej fabryki dla HttpClient, którą znam. Jeśli potrzebujesz większej funkcjonalności, spójrz na RestClient.
Troels Larsen
W HttpClient (.NET Core 3.1) nie ma właściwości ClientCredentials.
Павле
@ Павле: Nie zaktualizowałem jeszcze tego projektu do wersji 3.1, ale powinna istnieć taka właściwość: docs.microsoft.com/en-us/dotnet/api/… .
Troels Larsen
@ Павле: Ta odpowiedź nie dotyczy HttpClient, ale klienta wygenerowanego przez usługę WCF. Pracowałem też dla mojego ASMX SoapClient, wielkie dzięki!
Jan Zahradník
15

W .NetCore możesz dodać następujący fragment kodu w metodzie konfiguracji usług, dodałem sprawdzenie, aby upewnić się tylko, że przekazujemy certyfikat SSL tylko w środowisku programistycznym

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;
              });
Sameh
źródło
1
Dlaczego -ve, to dokładnie implementuje to, co inni sugerowali w kodzie mvc.net i zdobyli w tym punkty, po prostu ilustruję tę samą implementację w kodzie .netCore
Sameh
prawdopodobnie. bo brakuje mu jakiegokolwiek wyjaśnienia. dlaczego to podejście powinno być zastąpione jakimkolwiek innym, jaki kod należy napisać w sekcji wywołania (powiedzmy mycontroller.cs), co może być częścią wyjaśnienia. wszelkie oficjalne dokumenty / cytaty.
Bhanu Chhabra
Jak powiedziałem, jeśli przejrzeliście inne komentarze na górze wątku, nie ma dużej różnicy, a mimo to zdobyli 18 i 81 punktów,
Sameh
1
ponieważ dodali tekst wspierający ich odpowiedzi, prosimy o ponowne przeczytanie wskazówek. Może ci pomóc, @moderatorzy mogą wskazać dokładne problemy IMHO.
Bhanu Chhabra
9

Napotkałem ten sam problem podczas pracy z samopodpisanymi certyfikatami i uwierzytelnianiem certyfikatów klienta w kontenerach .NET Core 2.2 i Docker Linux. Wszystko działało dobrze na moim deweloperskim komputerze z Windowsem, ale w Dockerze dostałem taki błąd:

System.Security.Authentication.AuthenticationException: zdalny certyfikat jest nieprawidłowy zgodnie z procedurą sprawdzania poprawności

Na szczęście certyfikat został wygenerowany przy użyciu łańcucha. Oczywiście zawsze możesz zignorować to rozwiązanie i skorzystać z powyższych rozwiązań.

Oto moje rozwiązanie:

  1. Certyfikat zapisałem za pomocą przeglądarki Chrome na moim komputerze w formacie P7B .

  2. Konwertuj certyfikat do formatu PEM za pomocą tego polecenia:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. Otwórz plik ca_bundle.crt i usuń wszystkie nagrania podmiotu, pozostawiając czysty plik. Przykład poniżej:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. Umieść te wiersze w pliku Dockerfile (w ostatnich krokach):
    # 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. W aplikacji:
    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
    };

Metoda GetClientCertificate:

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);
}
DerSkythe
źródło
5

Po pierwsze, NIE UŻYWAJ GO W PRODUKCJI

Jeśli używasz oprogramowania pośredniczącego AddHttpClient, będzie to przydatne. Myślę, że jest to potrzebne w celu rozwoju, a nie produkcji. Dopóki nie utworzysz ważnego certyfikatu, możesz używać tej funkcji.

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

i zastosuj jak

services.AddHttpClient<IMyClient, MyClient>(x => { x.BaseAddress = new Uri("https://localhost:5005"); })
        .ConfigurePrimaryHttpMessageHandler(configureHandler);
Ozan ERTÜRK
źródło
3

Zezwalanie na wszystkie certyfikaty jest bardzo potężne, ale może być również niebezpieczne. Jeśli chcesz zezwolić tylko na ważne certyfikaty oraz niektóre określone certyfikaty, możesz to zrobić w ten sposób.

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

Pierwotnym źródłem:

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

Ogglas
źródło