2009-10-29 142 views
11

我試圖使用我的客戶端證書對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服務(客戶端)有時調用Web服務(服務器),以及之後的時間數額不詳的服務「忘記」我的證書和犯規授權對服務器,沒有明顯的理由)

回答

22

什麼類型的文件是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文件和「密碼」是我的私有密鑰設置密碼的證書。

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

讓我知道你是否希望我上傳服務器代碼和測試兩邊使用的證書。

+0

你知道如何使在Windows .NET相同的工作?出於某種原因,我無法在沒有在x509商店 – galets 2014-01-31 18:23:40

1

您是否需要證書密碼?如果是這樣,那麼在構造函數中有一個字段。

X509Certificate cert = new X509Certificate(PathToCertificate,YourPassword); 
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; 
+0

-1中註冊證書的情況下使其發揮作用。服務器不需要知道客戶端證書的私鑰。它只需要信任簽署客戶端證書的CA.在協商SSL3/TLS時,服務器將發送一個列表,其中包含所有允許的CA的DN。此時客戶應該看到客戶證書的CA。在列表中併發送它。 – Gonzalo 2009-11-09 20:29:28

+0

正確,我從來沒有說過SERVER需要私鑰。它必須信任證書或明確允許使用代碼。至於評論的其餘部分「...服務器發送一個包含所有CA的DN的列表......」,這是不正確的。 – 2009-11-09 20:51:59

+1

不正確?請參閱http://tools.ietf.org/html/rfc5246#page-53,第7.4.4節:certificate_authorities 可接受的 certificate_authorities的可分辨名稱[X501]列表,以DER編碼格式表示。這些專有名稱可以爲 根CA或從屬CA指定所需的可分辨名稱;因此,該消息可用於描述已知的根以及期望的授權空間。如果 certificate_authorities列表爲空,則客戶端MAY 將發送任何適當的ClientCertificateType證書, 單元 – Gonzalo 2009-11-09 22:04:45

2

潛在的問題可能是SSL會話(Schannel中的高速緩存)的緩存。只有第一個請求協商SSL握手。隨後的請求將使用相同的會話ID,並希望服務器接受它。如果服務器清除SessionId,則請求將失敗,並顯示403錯誤。要禁用本地SSL會話緩存(並迫使每個請求SSL協商)你要打開Windows的註冊表文件夾:

[HKEY_LOCAL_MACHINE] [系統] [CURRENTCONTROLSET] [CONTROL] [SecurityProviders] [SCHANNEL]

和與價值添加名爲ClientCacheTime(DWORD)的關鍵0

這個問題是這裏介紹:

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

+0

這是相當巨大的。如果你不知道這一點,你可能會花很多時間認爲你正在將「試錯法」解決方案削減到最簡單的形式,而實際上毫無疑問地打破了事情。 – Chazt3n 2016-03-10 16:20:01