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

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

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

Любая помощь будет очень признательна!

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 4

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

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

var client = new HttpClient(handler);

Ответ 5

Или вы можете использовать для 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)) {
    ...
}

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

Ответ 10

Я нашел пример в этом клиенте 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>();
  }
}