我正在尝试使用我的客户端证书对WebService进行身份验证,但是,由于某些原因(我解释),我不想从商店加载证书,而是从光盘中读取它。
以下内容:
// gw is teh WebService client
X509Certificate cert = new X509Certificate(PathToCertificate);
_gw.ClientCertificates.Add(ClientCertificate());
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true;
_gw.DoSomeCall();
始终返回403 - 服务未授权我。但是,当我将该证书保存到CertStore时,它可以工作。 (如MSDN中所述。)
是否可以使用不在店内的证书?
(原因是,我得到了Windows服务(客户端)有时会调用webservice(服务器),并且在未指定的时间后服务'忘记'我的证书并且没有对服务器进行授权,没有明显的原因)
答案 0 :(得分:24)
PathToCertificate是什么类型的文件?如果它只是一个.cer文件,它将不包含证书的私钥,并且尝试将该证书用于SSL / TLS将失败。
但是,如果您的PKCS7或PKCS12文件包含证书的公钥和私钥,则您的代码将起作用(如果私钥有密码,您可能需要使用带密码的重载)。
为了测试这个,我去了http://www.mono-project.com/UsingClientCertificatesWithXSP并按照这些说明创建了我的client.p12文件。我还使用HttpListener创建了一个简单的HTTPS服务器进行测试。
然后我将以下程序编译成'client.exe'并运行如下:
client.exe https://<MYSSLSERVER>/ client.p12 password
其中client.p12是之前生成的PKCS12文件,'password'是我为证书的私钥设置的密码。
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
public class HttpWebRequestClientCertificateTest : ICertificatePolicy {
public bool CheckValidationResult (ServicePoint sp, X509Certificate certificate,
WebRequest request, int error)
{
return true; // server certificate's CA is not known to windows.
}
static void Main (string[] args)
{
string host = "https://localhost:1234/";
if (args.Length > 0)
host = args[0];
X509Certificate2 certificate = null;
if (args.Length > 1) {
string password = null;
if (args.Length > 2)
password = args [2];
certificate = new X509Certificate2 (args[1], password);
}
ServicePointManager.CertificatePolicy = new HttpWebRequestClientCertificateTest ();
HttpWebRequest req = (HttpWebRequest) WebRequest.Create (host);
if (certificate != null)
req.ClientCertificates.Add (certificate);
WebResponse resp = req.GetResponse ();
Stream stream = resp.GetResponseStream ();
StreamReader sr = new StreamReader (stream, Encoding.UTF8);
Console.WriteLine (sr.ReadToEnd ());
}
}
如果您希望我上传服务器代码和测试两端使用的证书,请告诉我。
答案 1 :(得分:2)
你有可能至少有两个问题......
首先...
除非使用密码访问,否则您的客户端证书文件不能包含私钥。您应该使用带有密码的PKCS#12(* .pfx)证书,以便您的客户端可以访问私钥。您打开证书时,客户端代码必须提供密码,而其他人已经发布了密码。有几种方法可以创建它,最简单的方法是使用以下命令行首先生成证书,然后使用MMC证书管理器导出证书私钥:
Process p = Process.Start(
"makecert.exe",
String.Join(" ", new string[] {
"-r",// Create a self signed certificate
"-pe",// Mark generated private key as exportable
"-n", "CN=" + myHostName,// Certificate subject X500 name (eg: CN=Fred Dews)
"-b", "01/01/2000",// Start of the validity period; default to now.
"-e", "01/01/2036",// End of validity period; defaults to 2039
"-eku",// Comma separated enhanced key usage OIDs
"1.3.6.1.5.5.7.3.1," +// Server Authentication (1.3.6.1.5.5.7.3.1)
"1.3.6.1.5.5.7.3.2", // Client Authentication (1.3.6.1.5.5.7.3.2)
"-ss", "my",// Subject's certificate store name that stores the output certificate
"-sr", "LocalMachine",// Subject's certificate store location.
"-sky", "exchange",// Subject key type <signature|exchange|<integer>>.
"-sp",// Subject's CryptoAPI provider's name
"Microsoft RSA SChannel Cryptographic Provider",
"-sy", "12",// Subject's CryptoAPI provider's type
myHostName + ".cer"// [outputCertificateFile]
})
);
其次...
你的下一个问题是服务器端问题。服务器必须允许此证书。您有正确的逻辑,但在线路的错误一侧,将此行移动到处理请求的Web服务器。如果不能,则必须将上面保存的“.cer”文件带到服务器并将其添加到服务器计算机的信任列表中:
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true;
答案 2 :(得分:2)
潜在的问题可能是缓存SSL会话(Schannel缓存)。只有第一个请求才能协商SSL握手。后续请求将使用相同的会话ID,并希望服务器接受它。如果服务器清除SessionId,请求将失败,并显示403错误。要禁用本地ssl会话缓存(并强制每个请求进行SSL协商),您必须打开Windows注册表文件夹:
[HKEY_LOCAL_MACHINE] [系统] [CURRENTCONTROLSET] [控制] [SecurityProviders] [SCHANNEL]
并添加名为ClientCacheTime(DWORD)的值为0的键。
此问题包含在此处:
答案 3 :(得分:1)
您需要证书密码吗?如果是这样,构造函数中就有一个字段。
X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword);