我正在为REST API编写客户端并对API进行身份验证我必须使用提供给我的证书。
此代码如下:
public string GetCustomer(int custId)
{
X509Certificate2 Cert = new X509Certificate2();
Cert.Import(@"C:\users\foo\desktop\api\pubAndPrivateCert.pkcs12", "", X509KeyStorageFlags.PersistKeySet);
ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://api.foo.net/api/customer/v1/" + custId);
req.ClientCertificates.Add(Cert);
req.UserAgent = "LOL API Client";
req.Accept = "application/json";
req.Method = WebRequestMethods.Http.Get;
string result = null;
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
}
return result;
}
每次我发出请求时都会收到错误400,当使用Fiddler查看响应时,我会得到以下内容
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/0.6.32</center>
</body>
</html>
我认为没有理由不发送证书,但对SSL进行故障排除并不容易。我添加了一些调试语句来添加一些细节并停止使用fiddler,他就是我得到的
这些错误来自ValidateServerCertificate()
Certificate error: RemoteCertificateChainErrors
NotSignatureValid
The signature of the certificate cannot be verified.
1048576
Unknown error.
这些是引发的WebExecption的错误。
Cought Exception ProtocolError
The remote server returned an error: (400) Bad Request.
BadRequest
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/0.6.32</center>
</body>
</html>
这是ValidateServerCertificate()代码。总是返回true以忽略任何证书错误。
public static bool ValidateServerCertificate(object sender, X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
foreach (var chainstat in chain.ChainStatus)
{
Console.WriteLine("{0}", chainstat.Status);
Console.WriteLine("{0}", chainstat.StatusInformation);
}
return true;
}
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// allow this client to communicate with unauthenticated servers.
return true;
}
答案 0 :(得分:10)
您的代码从本地文件加载客户端证书。如果将客户端证书导入证书存储区(强烈建议保护私钥),则应该取得更大成功。然后你的代码应该更像这样:
public string GetCustomer(int custId)
{
// EDIT THIS TO MATCH YOUR CLIENT CERTIFICATE: the subject key identifier in hexadecimal.
string subjectKeyIdentifier = "39b66c2a49b2059a15adf96e6b2a3cda9f4b0e3b";
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindBySubjectKeyIdentifier, subjectKeyIdentifier, true);
X509Certificate2 certificate = certificates[0];
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://api.foo.net/api/customer/v1/" + custId);
req.ClientCertificates.Add(certificate);
req.UserAgent = "LOL API Client";
req.Accept = "application/json";
req.Method = WebRequestMethods.Http.Get;
string result = null;
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
}
return result;
}
有关说明,请参阅Import a Certificate。该代码假定您已将包含公钥和私钥的证书导入到当前用户的个人证书文件夹(&#34;我的&#34;)。
您无需提供ServicePointManager.ServerCertificateValidationCallback
。这允许您的应用程序更改服务器证书的验证方式。这不会影响服务器验证客户端证书的方式。