Cho phép chứng chỉ SSL không đáng tin cậy với HttpClient


114

Tôi đang đấu tranh để ứng dụng Windows 8 của mình giao tiếp với API web thử nghiệm của tôi qua SSL.

Có vẻ như HttpClient / HttpClientHandler không cung cấp và tùy chọn bỏ qua các chứng chỉ không đáng tin cậy như WebRequest cho phép bạn (mặc dù theo cách "khó hiểu" với ServerCertificateValidationCallback).

Bất kì sự trợ giúp nào đều được đánh giá cao!


5
Trong trường hợp bạn đang sử dụng .NET Core, bạn có thể quan tâm đến câu trả lời này .
kdaveid

Câu trả lời:


13

Với Windows 8.1, giờ đây bạn có thể tin tưởng các chứng chỉ SSL không hợp lệ. Bạn phải sử dụng Windows.Web.HttpClient hoặc nếu bạn muốn sử dụng System.Net.Http.HttpClient, bạn có thể sử dụng bộ điều hợp xử lý thư mà tôi đã viết: http://www.nuget.org/packages/WinRtHttpClientHandler

Tài liệu có trên GitHub: https://github.com/onovotny/WinRtHttpClientHandler


12
Có cách nào để làm điều này mà không cần sử dụng tất cả mã của bạn không? Nói cách khác, ý chính của giải pháp của bạn là gì?
wensveen

Ý chính của giải pháp là bao bọc trình xử lý máy khách windows http và sử dụng trình xử lý đó làm việc triển khai HttpClient. Tất cả đều nằm trong tệp này: github.com/onovotny/WinRtHttpClientHandler/blob/master/… hãy sao chép nó vào nhưng không chắc tại sao không chỉ sử dụng gói.
Claire Novotny

@OrenNovotny để giải pháp của bạn không bị ràng buộc với phiên bản Windows?
chester89

Tôi đã triển khai điều này trong ứng dụng UWP của mình và tôi đã sử dụng ví dụ về bộ lọc bên dưới. Tôi vẫn gặp lỗi tương tự.
Christian Findlay

158

Một giải pháp nhanh chóng và bẩn là sử dụng ServicePointManager.ServerCertificateValidationCallbackđại biểu. Điều này cho phép bạn cung cấp xác thực chứng chỉ của riêng bạn. Việc xác thực được áp dụng trên toàn cầu trên toàn bộ Miền ứng dụng.

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

Tôi sử dụng điều này chủ yếu để kiểm tra đơn vị trong các tình huống mà tôi muốn chạy với một điểm cuối mà tôi đang lưu trữ trong quá trình xử lý và đang cố gắng đánh nó với một máy khách WCF hoặc HttpClient.

Đối với mã sản xuất, bạn có thể muốn kiểm soát chi tiết hơn và tốt hơn là sử dụng thuộc tính WebRequestHandlervà thuộc tính ServerCertificateValidationCallbackđại biểu của nó (Xem câu trả lời của dtb bên dưới ). Hoặc câu trả lời ctacke bằng cách sử dụng HttpClientHandler. Tôi thích một trong hai điều này ngay cả với các bài kiểm tra tích hợp của tôi về cách tôi đã từng làm trừ khi tôi không thể tìm thấy bất kỳ móc nào khác.


32
Không phải là downvoter, mà một trong những vấn đề lớn nhất với ServerCertificateValidationCallback là về cơ bản nó sẽ toàn cầu đối với AppDomain của bạn. Vì vậy, nếu bạn đang viết một thư viện cần thực hiện các cuộc gọi đến một trang web có chứng chỉ không đáng tin cậy và sử dụng giải pháp này, bạn đang thay đổi hành vi của toàn bộ ứng dụng, không chỉ thư viện của bạn. Ngoài ra, mọi người nên luôn cẩn thận với cách tiếp cận 'trả lại sự thật một cách mù quáng'. Nó có ý nghĩa bảo mật nghiêm trọng. Nó sẽ đọc / * kiểm tra các tham số được cung cấp và sau đó cẩn thận quyết định xem có nên * / trả về true hay không;
scottt732

@ scottt732 Chắc chắn là một điểm hợp lệ và đáng nói nhưng nó vẫn là một giải pháp hợp lệ. Có lẽ sau khi tái đọc câu hỏi ban đầu của OP dường như ông đã nhận thức được xử lý ServerCertificateValidationCallback
Bronumski

2
Tôi muốn giới thiệu ít nhất là bộ lọc trên một số tiêu chí như người gửi
Boas Enkler

2
Tôi thực sự phải giới thiệu tùy chọn WebRequestHandler so với ServicePointManager toàn cầu.
Thomas S. Trias

3
Bạn không phải sử dụng ServicePointManager trên toàn cầu, bạn có thể sử dụng ServicePointManager.GetServicePoint(Uri) (xem tài liệu ) để lấy điểm dịch vụ chỉ áp dụng cho các cuộc gọi đến URI đó. Sau đó, bạn có thể đặt thuộc tính và xử lý các sự kiện dựa trên tập hợp con đó.
yến mạch

87

Xem qua Lớp WebRequestHandlerThuộc tính ServerCertificateValidationCallback của nó :

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

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

5
Cảm ơn bạn đã trả lời. Tuy nhiên, tôi đã xem xét điều đó - nó không có trong ứng dụng .NET dành cho Windows 8 Store.
Jamie

Tuy nhiên, việc tạo lớp dẫn xuất HttpClientHandler hoặc HttpMessageHandler của riêng mình là đủ dễ dàng, đặc biệt là với thực tế là nguồn WebRequestHandler có sẵn.
Thomas S. Trias

@ ThomasS. Có mẫu nào về nó không? derived class?
Kiquenet

2
Sử dụng HttpClientHandler?
Kiquenet

1
@Kiquenet trả lời này là từ năm 2012, nó là 6 tuổi đã có, và có - nhiều người những thời gian này là chắc chắn rằng đây là cách đúng đắn để sử dụng httpclient :)
justmara

63

Nếu bạn đang cố gắng thực hiện việc này trong thư viện .NET Standard, đây là một giải pháp đơn giản, với tất cả các rủi ro chỉ trả về truetrong trình xử lý của bạn. Tôi để lại sự an toàn cho bạn.

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

var client = new HttpClient(handler);

1
điều này dẫn đến nền tảng không được hỗ trợ khi được tham chiếu từ uwp
Ivan

đã làm việc cho tôi ở UWP. có lẽ bảo hiểm hỗ trợ đã đáo hạn trong 14 tháng. lưu ý: hack này nên cần thiết trong kiểm tra / dev env chỉ (nơi Certs tự ký đang được sử dụng)
Bến McIntyre

Tôi đã chạy mã này và luôn gặp System.NotImplementedException. Tôi không biết tại sao.
Liu Feng

25

Hoặc bạn có thể sử dụng cho HttpClient trong Windows.Web.Httpkhông gian tên:

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

Tôi có thể sử dụng System.Net.Http, System.WebWindows.Web.Httpcùng nhau?
Kiquenet

2
HttpClient dường như không có tính năng ghi đè này trong .NET Standard hoặc UWP.
Christian Findlay


15

Nếu bạn đang sử dụng, System.Net.Http.HttpClienttôi tin rằng mẫu chính xác là

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

var http = new HttpClient(handler);
var res = http.GetAsync(url);

8

Hầu hết các câu trả lời ở đây đề xuất sử dụng mẫu điển hình:

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

vì giao diện IDisposable. Xin đừng!

Microsoft cho bạn biết tại sao:

Và ở đây bạn có thể tìm thấy phân tích chi tiết về những gì đang diễn ra đằng sau hậu trường: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Về câu hỏi SSL của bạn và dựa trên https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/#how-to-fix-the-problem

Đây là mẫu của bạn:

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

Câu hỏi dành cho mối quan hệ tin cậy của cert tự ký. Toàn bộ câu trả lời của bạn là (thậm chí được cho là hợp lệ) về việc làm cho chuỗi HttpClient an toàn.
Adarsha

1
Nó không chỉ về "an toàn luồng". Tôi muốn chỉ ra cách sử dụng "đúng đắn" của httpclient với xác minh SSL "bị vô hiệu hóa". Các câu trả lời khác "toàn cầu" sửa đổi trình quản lý chứng chỉ.
Bernhard

7

Nếu đây là ứng dụng Windows Runtime, thì bạn phải thêm chứng chỉ tự ký vào dự án và tham chiếu nó trong appxmanifest.

Tài liệu ở đây: http://msdn.microsoft.com/en-us/library/windows/apps/hh465031.aspx

Điều tương tự nếu đó là từ một CA không đáng tin cậy (như CA riêng mà bản thân máy không tin cậy) - bạn cần lấy chứng chỉ công khai của CA, thêm nó làm nội dung vào ứng dụng rồi thêm nó vào tệp kê khai.

Sau khi hoàn tất, ứng dụng sẽ xem nó như một chứng chỉ được ký chính xác.


2

Tôi không có câu trả lời, nhưng tôi có một giải pháp thay thế.

Nếu bạn sử dụng Fiddler2 để theo dõi lưu lượng VÀ kích hoạt Giải mã HTTPS, môi trường phát triển của bạn sẽ không phàn nàn. Điều này sẽ không hoạt động trên các thiết bị WinRT, chẳng hạn như Microsoft Surface, vì bạn không thể cài đặt các ứng dụng tiêu chuẩn trên chúng. Nhưng máy tính Win8 phát triển của bạn sẽ ổn.

Để bật mã hóa HTTPS trong Fiddler2, hãy đi tới Công cụ> Tùy chọn Fiddler> HTTPS (Tab)> Chọn "Giải mã lưu lượng HTTPS" .

Tôi sẽ theo dõi chủ đề này để hy vọng ai đó có một giải pháp thanh lịch.


2

Tôi đã tìm thấy một ví dụ trong ứng dụng Kubernetes này , nơi họ đang sử dụng X509VerificationFlags.AllowUnknownCertificateAuthority để tin cậy chứng chỉ gốc tự ký. Tôi đã làm lại một chút ví dụ của họ để làm việc với các chứng chỉ gốc được mã hóa PEM của riêng chúng tôi. Hy vọng rằng điều này sẽ giúp ai đó.

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

Tôi đã tìm thấy một ví dụ trực tuyến có vẻ hoạt động tốt:

Đầu tiên, bạn tạo ICertificatePolicy mới

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

Sau đó, chỉ cần sử dụng cái này trước khi gửi yêu cầu http của bạn như sau:

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

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


1
ServicePointManager.CertificatePolicykhông được dùng nữa: docs.microsoft.com/en-us/dotnet/framework/whats-new/…
schmidlop
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.