【问题标题】:Calling a REST web service over SSL通过 SSL 调用 REST Web 服务
【发布时间】:2015-04-25 00:23:02
【问题描述】:

我正在努力从我的 .NET 应用程序连接到仅通过 HTTPS/SSL 工作的 REST Web 服务。

我收到了证书和私钥作为两个单独的文件使用 - 一个包含证书的 certificate.pem 文件和包含私钥的 webservice.key 文件。这些都是文本文件,其中包含 BASE64 编码的二进制数据。

供应商还给我发了一份 PDF,展示了如何使用 CURL 和这两个文件调用该 Web 服务,效果很好:

curl.exe -k -v "https://(URL)" --cert certificate.pem --key webservice.key

我需要使用-k 选项,因为证书层次结构中的某处似乎有一个自签名证书。没有这个选项,调用会失败。

为了从 .NET 应用程序(目前是控制台应用程序)调用此 Web 服务,我使用 OpenSSL(在 Windows 上)使用以下命令将这两个文件组合成一个 *.pfx 文件:

openssl pkcs12 -export -out webservice.pfx -in certificate.pem -inkey webservice.key 

这似乎也有效 - 没有报告错误,文件已创建并且大小约为 3K,并且是一个完全二进制文件。

现在,我尝试从我的 .NET 代码中调用该 Web 服务,如下所示:

try
{
    // use the SSL protocol (instead of TLS)
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

    // ignore any certificate complaints
    ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { return true; };

    // create HTTP web request with proper content type
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
    request.ContentType = "application/xml;charset=UTF8";

    // grab the PFX as a X.509 certificate from disk
    string certFileName = Path.Combine(certPath, "webservice.pfx");

    // load the X.509 certificate and add to the web request
    X509Certificate cert = new X509Certificate(certFileName, "(top-secret password)");
    request.ClientCertificates.Add(cert);
    request.PreAuthenticate = true;

    // call the web service and get response
    WebResponse response = request.GetResponse();

    Stream responseStream = response.GetResponseStream();
}
catch (Exception exc)
{
    // log and print out error
}

但是,我可以尝试任何我喜欢的东西(在ServicePointManagerHttpWebRequest 上摆弄各种设置,但我不断收到这些错误:

WebException:底层连接已关闭:发送时发生意外错误。

IOException: Unable to read data from the transport connection: 一个现有的连接被远程主机强行关闭。

SocketException:一个现有的连接被远程主机强行关闭

并且没有响应 - 即使使用 CURL 与服务通信工作正常.....

我错过了什么?我对所有这些证书、私钥、服务点管理器选项等感到有点困惑和迷惑——只是 waaaaay 太多的旋钮和开关无法打开、设置或关闭——正确是什么设置在这里??

更新:

如果我使用

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

那么错误就是:

WebException:请求被中止:无法创建 SSL/TLS 安全通道。

S O L U T I O N:

最后,通过查看curl 的输出以及来自@Alexandru 和@JurajMajer 的大量帮助,我得以使用此代码:

try
{
    // use the TLS protocol 
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

    // create HTTP web request with proper content type
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
    request.ContentType = "application/xml;charset=UTF8";

    // grab the PFX as a X.509 certificate from disk
    string certFileName = Path.Combine(certPath, "webservice.pfx");

    // load the X.509 certificate and add to the web request
    X509Certificate2 cert = new X509Certificate2(certFileName, "(top-secret password)");
    request.ClientCertificates.Add(cert);
    request.PreAuthenticate = true;

    // call the web service and get response
    WebResponse response = request.GetResponse();

    Stream responseStream = response.GetResponseStream();

    string xmlContents = new StreamReader(responseStream).ReadToEnd();
}
catch (Exception exc)
{
    // log and print out error
}

【问题讨论】:

  • 您的异常中是否有任何有意义的内部异常?
  • @Alexandru:这是我得到的唯一一个异常 (WebException) 及其两个内部异常
  • 如果您不忽略证书投诉,是否会引发一些有意义的错误?
  • 顺便说一句,您可能想考虑让 Fiddler 调试这个问题 (telerik.com/download/fiddler)。工具 -> Fiddler 选项 -> HTTPS -> 勾选 Capture HTTPS CONNECTs 和 Decrypt HTTPS 流量。它安装了一个根 CA,开始签署所有证书,因此它在本地执行中间人攻击以解密通信。
  • @Alexandru:不,忽略证书问题不会改变任何事情。而且我已经广泛尝试了 Fiddler,但我从来没有能够让它与这个服务一起工作(即使是关于这个的关于这个的问题也没有导致任何解决方案)

标签: c# web-services rest ssl certificate


【解决方案1】:

您已将 X509Certificate(String, String) 构造函数与 PKCS#12 证书一起使用,但正如 MSDN 所说,该构造函数仅适用于 PKCS#7 证书...

使用名称初始化 X509Certificate 类的新实例 PKCS7 签名文件和访问证书的密码。

PKCS#7 不包括您需要的证书/私钥对的私钥(密钥)部分。这意味着根据证书的性质,您将需要使用 PKCS#12 证书。

您可能希望使用现有的 PKCS#12 证书尝试 X509Certificate2(String, String) 构造函数,因为正如 MSDN 所说,此构造函数与包含证书私钥的 PKCS#12 (PFX) 文件一起使用...

此构造函数使用一个新的 X509Certificate2 对象 证书文件名和访问证书所需的密码。 它与包含证书的 PKCS12 (PFX) 文件一起使用 私钥。使用正确的密码调用此构造函数 解密私钥并将其保存到密钥容器中。

【讨论】:

  • 谢谢,亚历山德鲁。不幸的是,即使使用X509Certificate2,我仍然有同样的错误.....
  • 也许有一种方法可以设置与您的客户类似的环境并跟踪以查看服务器端连接关闭时发生的情况,因为客户端日志不是很有意义。
  • 今天美好的一天 - 通过查看curl 输出,我终于能够弄清楚如何让它工作。我需要将 SecurityProtocol 设置为 TLS,将 Http 版本设置为 1.1,并使用您在此处提到的 X509Certificate2 构造函数。综上所述,现在它实际上 W O R K S ! :-) :-) :-) 感谢所有 cmets 和所有帮助!非常感谢!
  • 听到这个消息让我非常高兴。我从我们一起的旅程中学到了一些新东西。 :)
【解决方案2】:

尝试在客户端的 App.config 中启用网络跟踪 - instructions here。这应该创建带有更多调试信息的 network.log。在我的测试环境中,我有一个可以工作的 pfx 和一个不工作的 pfx。

用于工作 pfx 的 network.log:

SecureChannel#9343812 - We have user-provided certificates. The server has specified 34 issuer(s). Looking for certificates that match any of the issuers. SecureChannel#9343812 - Left with 1 client certificates to choose from. SecureChannel#9343812 - Trying to find a matching certificate in the certificate store. SecureChannel#9343812 - Locating the private key for the certificate: SecureChannel#9343812 - Certificate is of type X509Certificate2 and contains the private key.

非工作 pfx 的网络日志:

SecureChannel#26756241 - We have user-provided certificates. The server has specified 34 issuer(s). Looking for certificates that match any of the issuers. SecureChannel#26756241 - Left with 0 client certificates to choose from.

所以对我来说,问题是我的非工作证书是由不在列表中的 CA 颁发的。

有趣的点(可能的问题):

1.) 服务器发送客户端证书的已知颁发者列表。

2.) 客户端代码正在证书存储事件中查找证书和私钥,尽管两者都在 pfx 中。

【讨论】:

  • 谢谢 - 我会尝试并回复您。
  • 好的,结果是:当尝试从磁盘上的文件提供证书时(没有将其安装在证书存储中),这似乎根本不起作用。 network.log 显示在证书存储中搜索了证书(但未找到),因此整个事情都不起作用。一旦我将 PFX 证书安装到证书存储中,该步骤就起作用了 - 证书在“当前用户”存储中找到并发送,但在那之后,连接仍然中止......
  • 有趣。您收到此错误:WebException: The request was aborted: Could not create SSL/TLS secure channel.?任何内部异常?网络日志中有什么可疑之处,主要是SecureChannel 条目?您是对 ServerCertificateValidationCallback 使用 lambda 表达式还是以其他方式成功验证服务器证书?也许您可以使用 WireShark 嗅探 SSL 握手,以查找错误的 WCF 案例和有效的 cURL 案例并比较两者。
猜你喜欢
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-25
  • 1970-01-01
相关资源
最近更新 更多