обойти недействительный сертификат SSL в ядре .net

104

Я работаю над проектом, которому необходимо подключиться к сайту https. Каждый раз, когда я подключаюсь, мой код выдает исключение, потому что сертификат этого сайта поступает с ненадежного сайта. Есть ли способ обойти проверку сертификата в .net core http?

Я видел этот код из предыдущей версии .NET. Думаю, мне просто нужно что-то вроде этого.

 ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Рэмппи Дампи
источник

Ответы:

28

ServicePointManager.ServerCertificateValidationCallback не поддерживается в .Net Core.

Текущая ситуация такова, что это будет новый метод ServerCertificateCustomValidationCallback для предстоящего контракта 4.1. * System.Net.Http (HttpClient). Команда .NET Core сейчас завершает работу над контрактом на версию 4.1. Вы можете прочитать об этом здесь, на github

Вы можете опробовать предварительную версию System.Net.Http 4.1, используя источники прямо здесь, в CoreFx или в ленте MYGET: https://dotnet.myget.org/gallery/dotnet-core

Текущее определение WinHttpHandler.ServerCertificateCustomValidationCallback на Github

Задавать
источник
8
Это работает только в Windows. У вас есть решение для Linux? Спасибо.
Владимир
146

Обновить:

Как указано ниже, не все реализации поддерживают этот обратный вызов (например, платформы, такие как iOS). В этом случае, как говорится в документации , вы можете явно установить валидатор:

handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

Это также работает для .NET Core 2.2, 3.0 и 3.1.

Старый ответ , с большим контролем, но может бросить PlatformNotSupportedException:

Вы можете переопределить проверку сертификата SSL при HTTP-вызове с помощью анонимной функции обратного вызова, подобной этой

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

Кроме того, я предлагаю использовать фабричный шаблон, HttpClientпотому что это общий объект, который нельзя удалить немедленно, и поэтому соединения останутся открытыми .

kdaveid
источник
3
Я использую .Net Core 1.0, и это сработало для меня. Напомним, что в .Net Core 2.0 добавлено HttpClientсвойство с именем, DangerousAcceptAnyServerCertificateValidatorкоторое позволяет сделать эту работу на MacOSX. Подробнее здесь - github.com/dotnet/corefx/pull/19908
Трой Виттхофт,
1
Используя это с AWS Lambda, .NET Core 1.0 исправил то, что мешало мне подключиться к внутреннему HTTPS с настраиваемым корневым сертификатом CA.
QuickNull
Любой factory patternдля HttpClient?
Kiquenet 05
@Kiquenet Просто создайте фабрику, где GetHttpClientметод возвращает сконфигурированное HttpClientи используйте его в using-блоке.
LuckyLikey
Это должен быть принятый ответ, тем более, что его можно ограничить одним клиентом.
BinaryPatrick
37

Решаю с этим:

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);
О. Мачадо
источник
32

Пришел сюда в поисках ответа на ту же проблему, но я использую WCF для NET Core. Если вы находитесь в одной лодке, используйте:

client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication = 
    new X509ServiceCertificateAuthentication()
    {
        CertificateValidationMode = X509CertificateValidationMode.None,
        RevocationMode = X509RevocationMode.NoCheck
    };
Троэльс Ларсен
источник
Глобальный для всех сертификатов и AppDomain?
Kiquenet 05
@Kiquenet: Думаю, да. Проверьте обновленный ответ в другом месте, возможно, сейчас есть лучшее решение. Прошел год. Я думаю, вы могли бы подклассифицировать аутентификатор, если не что иное. И нет, я не знаю родной фабрики для HttpClient. Если вам нужна дополнительная функциональность, посмотрите RestClient.
Troels Larsen
В HttpClient (.NET Core 3.1) нет свойства ClientCredentials.
Павле
@ Павле: Я еще не обновил этот проект до версии 3.1, но должно быть такое свойство: docs.microsoft.com/en-us/dotnet/api/… .
Troels Larsen,
@ Павле: Этот ответ не о HttpClient, а о клиенте, созданном службой WCF. Также работал с моим ASMX SoapClient, большое спасибо!
Ян Заградник,
14

В .NetCore вы можете добавить следующий фрагмент кода в методе настройки служб, я добавил проверку, чтобы убедиться, что мы передаем сертификат SSL только в среде разработки.

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;
              });
Самех
источник
1
Почему -ve, это точно реализует то, что другие предлагали в коде mvc.net, и они набрали на нем баллы, я просто иллюстрирую ту же реализацию в коде .netCore
Самех
наверное. потому что ему нет никакого объяснения. почему этот подход должен быть заменен любым другим, какой код должен быть написан в вызывающей секции (скажем, mycontroller.cs), что может быть частью объяснения. любая официальная документация / цитата.
Бхану Чхабра
Как я уже сказал, если вы просмотрели другие комментарии в верхней части
Самех
1
поскольку они добавили текст, подтверждающий свои ответы, пожалуйста, прочтите руководство еще раз. Может вам помочь, @moderators могут указать на точные проблемы ИМХО.
Бхану Чхабра
8

Я столкнулся с той же проблемой при работе с самозаверяющими сертификатами и аутентификацией сертификатов клиента в контейнерах .NET Core 2.2 и Docker Linux. На моем компьютере с Windows все работало нормально, но в Docker я получил такую ​​ошибку:

System.Security.Authentication.AuthenticationException: удаленный сертификат недействителен в соответствии с процедурой проверки

К счастью, сертификат был создан с помощью цепочки. Конечно, вы всегда можете проигнорировать это решение и использовать вышеуказанные решения.

Итак, вот мое решение:

  1. Я сохранил сертификат с помощью Chrome на моем компьютере в формате P7B .

  2. Преобразуйте сертификат в формат PEM с помощью этой команды:
    openssl pkcs7 -inform DER -outform PEM -in <cert>.p7b -print_certs > ca_bundle.crt

  3. Откройте файл ca_bundle.crt и удалите все записи темы, оставив чистый файл. Пример ниже:

    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    _BASE64 DATA_
    -----END CERTIFICATE-----
  1. Поместите эти строки в Dockerfile (на последних шагах):
    # 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. В приложении:
    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 метод:

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
источник
4

Во-первых, НЕ ИСПОЛЬЗУЙТЕ ЕГО В ПРОИЗВОДСТВЕ

Если вы используете промежуточное ПО AddHttpClient, это будет полезно. Я думаю, что это нужно для разработки, а не для производства. Пока вы не создадите действующий сертификат, вы можете использовать этот Func.

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

и примените это как

services.AddHttpClient<IMyClient, MyClient>(x => { x.BaseAddress = new Uri("https://localhost:5005"); })
        .ConfigurePrimaryHttpMessageHandler(configureHandler);
Озан ЭРТЮРК
источник
3

Разрешение всех сертификатов очень важно, но также может быть опасно. Если вы хотите разрешить только действительные сертификаты плюс некоторые определенные сертификаты, это можно сделать следующим образом.

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

Первоисточник:

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

Огглас
источник