允许HttpClient使用不受信任的SSL证书


114

我正在努力让Windows 8应用程序通过SSL与我的测试Web API进行通信。

似乎HttpClient / HttpClientHandler不提供,并且可以忽略诸如WebRequest之类的不受信任的证书的选项使您能够(尽管使用hacky的方式ServerCertificateValidationCallback)。

任何帮助将非常感激!


5
如果您使用的是.NET Core,则可能对此答案感兴趣。
kdaveid

Answers:


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 2015年

解决方案的要点是包装Windows http客户端处理程序,并将其用作HttpClient的实现。一切都在此文件中:github.com/onovotny/WinRtHttpClientHandler/blob/master/…随时可以复制它,但不确定为什么不只使用该软件包。
克莱尔·诺沃特尼

@OrenNovotny,因此您的解决方案不限于Windows版本吗?
chester89 '18

我在UWP应用中实现了此功能,并使用了以下过滤器示例。我仍然遇到相同的错误。
Christian Findlay

158

一种快速而肮脏的解决方案是使用ServicePointManager.ServerCertificateValidationCallback委托。这使您可以提供自己的证书验证。验证将在整个应用程序域中全局应用。

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

我主要在需要针对正在托管的终结点计算机上运行并尝试使用WCF客户端WCF客户端进行测试的情况下,将其用于单元测试HttpClient

对于生产代码,您可能需要更细粒度的控制,并且最好使用WebRequestHandler和及其ServerCertificateValidationCallback委托属性(请参见下面的dtb的答案)。或使用ctacke 回答HttpClientHandler。我现在更喜欢使用这两个方法中的任何一个,即使在进行集成测试时,也比过去更喜欢使用它,除非找不到其他钩子。


32
不是降低投票权,而是ServerCertificateValidationCallback的最大问题之一是,它基本上对您的AppDomain是全局的。因此,如果编写的库需要使用不受信任的证书调用站点并采用此变通办法,那么您将在更改整个应用程序的行为,而不仅仅是更改库的行为。同样,人们应该始终谨慎对待“盲目归真”的方法。它具有严重的安全隐患。它应该读取/ *检查提供的参数,然后仔细决定是否* /返回true;
scottt732 2014年

@ scottt732当然是有效的观点,值得一提,但它仍然是有效的解决方案。也许在重新阅读了OP的原始问题之后,看来他已经知道ServerCertificateValidationCallback处理程序
Bronumski 2014年

2
我建议至少过滤某些条件,例如发件人
Boas Enkler 2014年

2
我真的必须推荐WebRequestHandler选项与全局ServicePointManager。
Thomas S. Trias

3
您不必全局使用ServicePointManager,可以使用ServicePointManager.GetServicePoint(Uri) (请参阅docs)获取仅适用于对该URI的调用的服务点。然后,您可以设置属性并基于该子集处理事件。
oatsoda

87

看一下WebRequestHandler类及其ServerCertificateValidationCallback属性

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

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

5
感谢您的答复,但是我已经研究过-Windows 8 Store应用程序的.NET中不存在。
Jamie

但是,创建自己的HttpClientHandler或HttpMessageHandler派生类很容易,尤其是考虑到WebRequestHandler源易于使用的事实。
Thomas S. Trias'2

@ ThomasS.Trias有任何样本吗?derived class
Kiquenet '18

2
使用HttpClientHandler
Kiquenet '18

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引用时,这导致平台不被支持
Ivan

在UWP中为我工作。支持范围可能已经在14个月内成熟。注意:仅在test / dev env中(使用自签名证书的情况下),才应使用此hack
Ben McIntyre

我运行了这段代码,并始终遇到System.NotImplementedException。我不知道为什么
刘枫

25

或者,您可以在名称空间中使用HttpClientWindows.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.HttpSystem.WebWindows.Web.Http在一起呢?
Kiquenet '18

2
HttpClient在.NET Standard或UWP中似乎没有此替代。
Christian Findlay '18


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/zh-CN/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线程安全。
阿达沙(Adarsha)

1
这不仅与“线程安全性”有关。我想展示使用带有“已禁用” SSL验证的httpclient的“正确”方法。其他答案“全局”修改证书管理器。
伯恩哈德

7

如果这是Windows运行时应用程序,则必须将自签名证书添加到项目中,并在appxmanifest中引用它。

该文档位于此处:http : //msdn.microsoft.com/zh-cn/library/windows/apps/hh465031.aspx

如果它来自不受信任的CA(例如计算机本身不信任的私有CA),则也是一样-您需要获取CA的公共证书,将其作为内容添加到应用程序中,然后将其添加到清单中。

完成后,应用程序会将其视为正确签名的证书。


2

我没有答案,但是我有替代方法。

如果您使用Fiddler2监视流量并启用HTTPS解密,则开发环境不会抱怨。这不适用于WinRT设备(例如Microsoft Surface),因为您无法在它们上安装标准应用程序。但是您开发的Win8电脑就可以了。

要在Fiddler2中启用HTTPS加密,请转至工具> 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.