Разрешение ненадежных сертификатов SSL с помощью HttpClient

115

Я изо всех сил пытаюсь заставить мое приложение Windows 8 взаимодействовать с моим тестовым веб-API через SSL.

Похоже, что HttpClient / HttpClientHandler не предоставляет и возможность игнорировать ненадежные сертификаты, такие как WebRequest, позволяет вам (хотя и «хакерским» способом ServerCertificateValidationCallback).

Любая помощь приветствуется!

Джейми
источник
5
Если вы используете .NET Core, вам может быть интересен этот ответ.
kdaveid

Ответы:

13

В Windows 8.1 теперь вы можете доверять недействительным сертификатам SSL. Вам нужно либо использовать Windows.Web.HttpClient, либо, если вы хотите использовать System.Net.Http.HttpClient, вы можете использовать адаптер обработчика сообщений, который я написал: http://www.nuget.org/packages/WinRtHttpClientHandler

Документы находятся на GitHub: https://github.com/onovotny/WinRtHttpClientHandler.

Клэр Новотны
источник
12
Есть ли способ сделать это без использования всего вашего кода? Другими словами, в чем суть вашего решения?
wensveen
Суть решения состоит в том, чтобы обернуть обработчик клиента http в Windows и использовать его как реализацию HttpClient. Все это находится в этом файле: github.com/onovotny/WinRtHttpClientHandler/blob/master/… не стесняйтесь копировать его, но не знаете, почему бы просто не использовать пакет.
Клэр Новотны
@OrenNovotny, значит, ваше решение не привязано к версии для Windows?
chester89
Я реализовал это в своем приложении UWP и использовал приведенный ниже пример фильтров. Я по-прежнему получаю ту же ошибку.
Кристиан Финдли
158

Быстрое и грязное решение - использовать ServicePointManager.ServerCertificateValidationCallbackделегат. Это позволяет вам обеспечить собственную проверку сертификата. Проверка применяется глобально во всем домене приложения.

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

Я использую это в основном для модульного тестирования в ситуациях, когда я хочу работать с конечной точкой, которую я размещаю в процессе, и пытаюсь поразить ее с помощью клиента WCF или HttpClient.

Для производственного кода вам может потребоваться более мелкозернистый элемент управления, и было бы лучше использовать свойство WebRequestHandlerи его ServerCertificateValidationCallbackделегат (см . Ответ dtb ниже ). Или ctacke ответьте, используя HttpClientHandler. Я предпочитаю любой из этих двух сейчас, даже с моими интеграционными тестами, чем то, как я это делал раньше, если я не могу найти другой крючок.

Bronumski
источник
32
Не тот, кто проголосовал против, но одна из самых больших проблем с ServerCertificateValidationCallback заключается в том, что он в основном глобален для вашего AppDomain. Поэтому, если вы пишете библиотеку, которая должна обращаться к сайту с ненадежным сертификатом, и используете этот обходной путь, вы меняете поведение всего приложения, а не только своей библиотеки. Кроме того, люди всегда должны быть осторожны с подходом «слепо возвращать истину». Это имеет серьезные последствия для безопасности. Он должен прочитать / * проверить предоставленные параметры и затем тщательно решить, следует ли * / возвращать true;
scottt732
@ scottt732 Безусловно, верный момент, о котором стоит упомянуть, но это все же верное решение. Возможно, после повторного чтения исходного вопроса ОП выяснилось, что он уже знал об обработчике
ServerCertificateValidationCallback
2
Я бы рекомендовал хотя бы отфильтровать по некоторым критериям, таким как отправитель
Боас Энклер
2
Я действительно должен порекомендовать вариант WebRequestHandler по сравнению с глобальным ServicePointManager.
Thomas S. Trias
3
Вам не нужно использовать ServicePointManager глобально, вы можете использовать ServicePointManager.GetServicePoint(Uri) (см. Документацию ), чтобы получить точку обслуживания, которая применяется только к вызовам этого URI. Затем вы можете установить свойства и обрабатывать события на основе этого подмножества.
oatsoda
87

Взгляните на класс WebRequestHandler и его свойство ServerCertificateValidationCallback :

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
dtb
источник
5
Спасибо за ответ, однако я уже рассмотрел это - его нет в .NET для приложений Магазина Windows 8.
Джейми
Однако создать собственный производный класс HttpClientHandler или HttpMessageHandler достаточно легко, особенно с учетом того факта, что исходный код WebRequestHandler легко доступен.
Thomas S. Trias
@ ThomasS.Trias какие-нибудь образцы об этом? derived class?
Kiquenet 02
2
Используя HttpClientHandler?
Kiquenet 06
1
@Kiquenet, этот ответ из 2012 года, ему уже 6 лет, и да - многие люди в то время были уверены, что это правильный способ использования httpclient :)
justmara
63

Если вы пытаетесь сделать это в стандартной библиотеке .NET, вот простое решение со всеми рисками возврата trueв обработчике. Я оставляю безопасность на ваше усмотрение.

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

var client = new HttpClient(handler);
ctacke
источник
1
это приводит к тому, что платформа не поддерживается при ссылке из uwp
Иван
у меня работал в UWP. возможно, срок действия поддержки наступит за 14 месяцев. примечание: этот хак должен быть необходим только в тестовой / dev env (где используются самоподписанные сертификаты)
Бен Макинтайр
Я запускал этот код и всегда встречал исключение System.NotImplementedException. Не знаю почему.
Лю Фэн
25

Или вы можете использовать для HttpClient в Windows.Web.Httpпространстве имен:

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ä
источник
Могу ли я использовать System.Net.Http, System.Webи Windows.Web.Httpвместе?
Kiquenet 02
2
HttpClient, похоже, не имеет этого переопределения в .NET Standard или UWP.
Кристиан Финдли
не используйте шаблон «использование»: docs.microsoft.com/en-us/azure/architecture/antipatterns/…
Бернхард,
16

Если вы используете, System.Net.Http.HttpClientя считаю, что правильный образец

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

var http = new HttpClient(handler);
var res = http.GetAsync(url);
Якуб Штурц
источник
8

В большинстве ответов здесь предлагается использовать типичный шаблон:

using (var httpClient = new HttpClient())
{
 // do something
}

из-за интерфейса IDisposable. Пожалуйста, не надо!

Microsoft сообщает вам, почему:

А здесь вы можете найти подробный анализ того, что происходит за кулисами: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Относительно вашего вопроса SSL и на основе https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Вот ваша выкройка:

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;
  }
 }
}
Бернхард
источник
Вопрос касается доверительных отношений самоподписанного сертификата. Весь ваш ответ (даже если он считался действительным) о том, чтобы сделать потокобезопасным HttpClient.
Адарша 02
1
Дело не только в «потокобезопасности». Я хотел показать «правильный» способ использования httpclient с «отключенной» проверкой SSL. Другие ответы "глобально" изменяют диспетчер сертификатов.
Бернхард
7

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

Документы здесь: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

То же самое, если он из ЦС, которому не доверяют (например, частного ЦС, которому сама машина не доверяет) - вам нужно получить общедоступный сертификат ЦС, добавить его в качестве содержимого в приложение, а затем добавить его в манифест.

Как только это будет сделано, приложение увидит в нем правильно подписанный сертификат.

Клэр Новотны
источник
2

У меня нет ответа, но у меня есть альтернатива.

Если вы используете Fiddler2 для мониторинга трафика и включите расшифровку HTTPS, ваша среда разработки не будет жаловаться. Это не будет работать на устройствах WinRT, таких как Microsoft Surface, поскольку на них нельзя устанавливать стандартные приложения. Но ваш компьютер Win8 разработки будет в порядке.

Чтобы включить шифрование HTTPS в Fiddler2, перейдите в Инструменты> Параметры Fiddler> HTTPS (вкладка)> Установите флажок «Расшифровать трафик HTTPS» .

Я буду следить за этой веткой, надеясь, что у кого-то найдется элегантное решение.

Laith
источник
2

Я нашел пример в этом клиенте Kubernetes, где они использовали X509VerificationFlags.AllowUnknownCertificateAuthority для доверия самозаверяющим самоподписанным корневым сертификатам. Я немного переработал их пример для работы с нашими собственными корневыми сертификатами в кодировке PEM. Надеюсь, это кому-то поможет.

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

Я нашел в Интернете пример, который, кажется, работает хорошо:

Сначала вы создаете новый 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;
  }
}

Затем просто используйте это перед отправкой вашего http-запроса следующим образом:

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

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

TombMedia
источник
1
ServicePointManager.CertificatePolicyустарел: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop