通过 SSL 调用 REST Web 服务

Calling a REST web service over SSL

我很难从我的 .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: The underlying connection was closed: An unexpected error occurred on a send.

IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

SocketException: An existing connection was forcibly closed by the remote host

没有响应 - 尽管使用 CURL 与服务通信工作正常......

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

更新:

如果我用

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

那么错误就是:

WebException: The request was aborted: Could not create SSL/TLS secure channel.

解决方案:

最后,通过查看 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
}

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

network.log 工作 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 中。

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

Initializes a new instance of the X509Certificate class using the name of a PKCS7 signed file and a password to access the certificate.

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

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

This constructor creates a new X509Certificate2 object using a certificate file name and a password needed to access the certificate. It is used with PKCS12 (PFX) files that contain the certificate's private key. Calling this constructor with the correct password decrypts the private key and saves it to a key container.