使用不在证书库中的客户端证书

时间:2009-10-29 13:14:30

标签: c# web-services certificate

我正在尝试使用我的客户端证书对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(服务器),并且在未指定的时间后服务'忘记'我的证书并且没有对服务器进行授权,没有明显的原因)

4 个答案:

答案 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的键。

此问题包含在此处:

http://support.microsoft.com/?id=247658

答案 3 :(得分:1)

您需要证书密码吗?如果是这样,构造函数中就有一个字段。

X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword);