通过SSL调用REST Web服务

时间:2015-02-23 15:58:29

标签: c# web-services rest ssl certificate

我很难连接到一个只能通过我的.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:无法从传输连接读取数据:远程主机强行关闭现有连接。

     

SocketException:远程主机强行关闭现有连接

并且没有回复 - 即使与CURL的服务通信工作得很好.....

我错过了什么?所有这些证书,私钥,服务点管理器选项等让我感到有点困惑和困惑 - 只是太多的旋钮和开关转动,设置或关闭 - 什么是 RIGHT 设置在这里?

更新

如果我使用

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
}

2 个答案:

答案 0 :(得分:3)

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

network.log for working pfx:

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中。

答案 1 :(得分:3)

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

  

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

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

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

  

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

相关问题