it-swarm.com.ru

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

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

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

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

79
Jamie

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

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

9
Oren Novotny

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

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

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

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

116
Bronumski

Посмотрите на класс WebRequestHandler и его Свойство ServerCertificateValidationCallback :

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

    using (var client = new HttpClient(handler))
    {
        ...
    }
}
69
dtb

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

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

var client = new HttpClient(handler);
21
ctacke

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

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

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

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

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

6
Oren Novotny

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

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

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

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

2
Laith

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

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

1
TombMedia

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

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

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