обхід недійсного сертифіката SSL у .net ядрі


103

Я працюю над проектом, який повинен підключитися до 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? Дякую.
Володимир

145

Оновлення:

Як зазначалося нижче, не всі реалізації підтримують цей зворотний виклик (тобто такі платформи, як 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оскільки це спільний об'єкт, який може не бути утилізований негайно, і тому з'єднання залишаться відкритими .


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

@Kiquenet Просто створіть фабрику, де GetHttpClientМетод повертає налаштоване HttpClientі використовує його в межах using-block.
LuckyLikey

Це має бути прийнятою відповіддю, тим більше, що її можна застосувати до одного клієнта.
BinaryPatrick

36

Я вирішую з цим:

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

@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
Sameh

ймовірно. тому що йому не вистачає жодного пояснення. чому цей підхід слід застосовувати до будь-якого іншого, Який код слід писати у розділі виклику (скажімо mycontroller.cs), що може бути частиною пояснення. будь-яка офіційна документація / цитата.
Bhanu Chhabra

Як я вже говорив, якщо ви переглядаєте інші коментарі у верхній частині ланцюжка, особливої ​​різниці немає, і все ж вони набрали 18 і 81 бал,
Самех

1
оскільки вони додали текст, що підтверджує їх відповіді, ще раз прочитайте інструкції. Можливо, вам допоможе, @moderators може вказати на точні проблеми IMHO.
Бхану

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. Помістіть ці рядки в файл Docker (на останніх кроках):
    # 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);
}

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.