Дозволити ненадійні SSL-сертифікати з HttpClient


114

Я намагаюся змусити свою програму 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

Суть рішення полягає в тому, щоб загорнути обробник клієнтів Windows http і використовувати його як реалізацію 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. Зараз я віддаю перевагу будь-якому з цих двох, навіть мої інтеграційні тести щодо того, як я це робив, якщо я не можу знайти жодного іншого гака.


32
Не суттєвий результат, але одна з найбільших проблем з ServerCertificateValidationCallback полягає в тому, що він в основному глобальний для вашого AppDomain. Отже, якщо ви пишете бібліотеку, якій потрібно здійснювати дзвінки на сайт з ненадійним сертифікатом і використовувати це рішення, ви змінюєте поведінку всієї програми, а не лише вашої бібліотеки. Крім того, люди завжди повинні бути обережними з підходом «сліпо повернути справжню». Це має серйозні наслідки для безпеки. Він повинен прочитати / * перевірити надані параметри, а потім уважно вирішити, чи потрібно * / повернути істину;
scottt732

@ scottt732 Безумовно, достовірний момент і варто згадати, але це все-таки вірне рішення. Можливо, після перечитання оригінального запитання ОП виявляється, що він уже знав обробника
ServerCertificateValidationCallback

2
Я рекомендую хоча б фільтрувати за такими критеріями, як відправник
Боас Енклер

2
Я дійсно повинен рекомендувати параметр WebRequestHandler проти глобального ServicePointManager.
Томас С. Тріас

3
Вам не потрібно використовувати ServicePointManager в усьому світі, ви можете використовувати ServicePointManager.GetServicePoint(Uri) (див. Документи ), щоб отримати точку обслуговування, яка застосовується лише до дзвінків до цього URI. Потім можна встановити властивості та обробляти події на основі цього підмножини.
oatsoda

87

Погляньте на клас WebRequestHandler та його властивість ServerCertificateValidationCallback :

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

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

5
Дякую за відповідь, проте я вже розглядав це - його немає в .NET для додатків Windows 8 Store.
Джеймі

Однак створити власний клас похідних HttpClientHandler або HttpMessageHandler досить легко, особливо враховуючи той факт, що джерело WebRequestHandler легко доступний.
Томас С. Тріас

@ ThomasS.Trias будь-який зразок про це? derived class?
Кікенет

2
Використовуючи HttpClientHandler?
Кікенет

1
@Kiquenet ця відповідь від 2012 року, їй вже 6 років, і так - багато людей у ​​цей час були впевнені, що це правильний спосіб використання httpclient :)
justmara

63

Якщо ви намагаєтеся зробити це в бібліотеці .NET Standard, ось просте рішення, з усіма ризиками повернення trueв обробник. Я залишаю вам безпеку.

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

var client = new HttpClient(handler);

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)) {
    ...
}

Можу чи я використовувати System.Net.Http, System.Webі Windows.Web.Httpразом?
Кікенет

2
Здається, HttpClient не має цього перебору в .NET Standard або UWP.
Крістіан Фіндлей

не використовуйте шаблон "використовуючого": docs.microsoft.com/en-us/azure/architecture/antipatterns/…
Bernhard

15

Якщо ви використовуєте, 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 безпечною.
Адарша

1
Йдеться не лише про "безпеку ниток". Я хотів показати "правильний" спосіб використання httpclient з "відключеною" верифікацією SSL. Інші відповіді "глобально" змінюють менеджер сертифікатів.
Бернхард

7

Якщо це програма для роботи з програмою Windows Runtime, вам потрібно додати сертифікат самопідписаного до проекту та вказати його в appxmanifest.

Документи тут: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Те саме, якщо це з ЦА, якому не довіряють (як-от приватний ЦА, якому машина сама не довіряє) - потрібно отримати відкритий сертифікат ЦА, додати його як вміст у додаток, а потім додати його до маніфесту.

Після цього додаток побачить це як правильно підписаний сертифікат.


2

У мене немає відповіді, але у мене є альтернатива.

Якщо ви використовуєте Fiddler2 для моніторингу трафіку та включення HTTPS-розшифровки, ваше середовище розробки не скаржиться. Це не працюватиме на пристроях WinRT, таких як Microsoft Surface, тому що ви не можете встановити на них стандартні програми. Але ваш комп'ютер Win8 з розробкою буде добре.

Щоб увімкнути шифрування HTTPS у Fiddler2, перейдіть до Інструменти> Параметри Fiddler> HTTPS (вкладка)> Поставте прапорець "Розшифрувати трафік HTTPS" .

Я збираюся стежити за цією ниткою, сподіваючись, що хтось матиме елегантне рішення.


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

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/


1
ServicePointManager.CertificatePolicyзастаріло: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.