代码之家  ›  专栏  ›  技术社区  ›  nothrow

使用不在证书存储中的客户端证书

  •  14
  • nothrow  · 技术社区  · 15 年前

    我正在尝试使用我的客户端证书对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 回复  |  直到 7 年前
        1
  •  23
  •   Anthony Horne Eelke    7 年前

    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 ());
        }
    }
    

    如果您想让我上传服务器代码和测试两边使用的证书,请通知我。

        2
  •  2
  •   PanJanek    15 年前

    潜在的问题可能是SSL会话的缓存(schannel缓存)。只有第一个请求才能协商SSL握手。随后的请求将使用相同的会话ID,并希望服务器接受它。如果服务器清除sessionid,请求将失败,并出现403错误。要禁用本地SSL会话缓存(并为每个请求强制进行SSL协商),必须打开Windows注册表文件夹:

    [hkey_local_machine][system][currentcontrolset][control][securityproviders][schannel]

    并添加名为clientcachetime(dword)的值为0的键。

    这个问题在这里讨论:

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

        3
  •  2
  •   Michael Myers KitsuneYMG    15 年前

    你有可能遇到至少两个问题…

    第一。。。

    除非使用密码访问,否则客户端证书文件不能包含私钥。您应该使用带有密码的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;
    
        4
  •  1
  •   jle    15 年前

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

    X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword);