Dot-Net

在 C# 中覆蓋 ServicePointManager.ServerCertificateValidationCallback 時如何呼叫預設證書檢查?

  • January 30, 2012

我需要信任應用程序中的一些自簽名證書,所以我像這樣覆蓋驗證回調:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
...

public static bool MyRemoteCertificateValidationCallback(
           Object sender,
           X509Certificate certificate,
           X509Chain chain,
           SslPolicyErrors sslPolicyErrors)
{

   if (sslPolicyErrors == SslPolicyErrors.None)
       return true;

   if (IsAprrovedByMyApplication(sender, certificate))  // <-- no matter what the check here is
      return true;
   else 
      return false;  // <-- here I'd like to call the default Windows handler rather than returning 'false'
}

但是當有一些策略錯誤,並且我連接的站點沒有被應用程序批准時,就會拋出異常。這裡的問題是它不同於標準的 Windows 行為。

考慮這個網站:https ://www.dscoduc.com/

它的證書有一個未知的頒發者,因此不受信任。我已將它與 MMC 一起添加到本地電腦的受信任的人(它是 Windows 7)中。

如果我在不覆蓋證書驗證回調的情況下執行此程式碼:

HttpWebRequest http = (HttpWebRequest)HttpWebRequest.Create("https://www.dscoduc.com/");
using (WebResponse resp = http.GetResponse())
{
   using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
   {
       string htmlpage = sr.ReadToEnd();
   }
}

它連接成功。這意味著 Windows 預設驗證器決定信任此證書。

但是一旦我覆蓋了 ServerCertificateValidationCallback,我的回調就會被SslPolicyErrors.RemoteCertificateChainErrors呼叫, 並且鏈包含一個狀態為X509ChainStatusFlags.PartialChain的元素(實際上我希望這裡不會收到任何錯誤,因為目前的證書應該是可信的)

該站點不包含在我的信任列表中,並且不想從我的回調中返回“true”。但我也不想返回’false’,否則我會得到一個異常:“根據驗證程序,遠端證書無效”,這顯然不是<https://www.dscoduc.com/>所期望的,因為它已添加到受信任的人儲存中,並且在證書回調未被覆蓋時由 Windows 批准。因此,我希望 Windows 採用該站點的預設驗證程序。我不想親自查看 Windows 受信任的商店並瀏覽所有鏈元素,因為它已經(並且希望正確)在 Windows 中實現。

換句話說,我需要明確信任使用者批准的站點(儲存在他的設置中的某個位置),並為所有其他站點呼叫預設認證檢查。

ServicePointManager.ServerCertificateValidationCallback 的預設值為 null,因此沒有“預設”回調供我稍後呼叫。我應該如何呼叫這個“預設”證書處理程序?

這樣的事情可能會奏效。請注意,X509CertificateValidator 允許您選擇是否在驗證中包含 Trusted People 儲存。

private static bool CertificateValidationCallBack(
   object sender,
   X509Certificate certificate,
   X509Chain chain,
   SslPolicyErrors sslPolicyErrors)
{
   // Your custom check here...
   if (isYourSpecialCase)
   {
       return true;
   }

   // If it is not your special case then revert to default checks...

   // Convert the certificate to a X509Certificate2
   var certificate2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate);

   try
   {
       // Choose the type of certificate validation you want
       X509CertificateValidator.PeerOrChainTrust.Validate(certificate2);
       //X509CertificateValidator.ChainTrust.Validate(certificate2);
   }
   catch
   {
       return false;
   }

   // Sender is always either a WebReqest or a hostname string
   var request = sender as WebRequest;
   string requestHostname = request != null ? request.RequestUri.Host : (string)sender;

   // Get the hostname from the certificate
   string certHostname = certificate2.GetNameInfo(X509NameType.DnsName, false);

   return requestHostname.Equals(certHostname, StringComparison.InvariantCultureIgnoreCase);
}

從回調中走鏈並沒有您想像的那麼困難。

看看<http://msdn.microsoft.com/en-us/library/dd633677(v=exchg.80).aspx>

該範例中的程式碼檢查證書鏈以確定證書是否是自簽名的,如果是,則信任它。您可以調整它以接受 aPartialChain而不是或也一樣。你會想要做這樣的事情:

if (status.Status == X509ChainStatusFlags.PartialChain ||
   (certificate.Subject == certificate.Issuer &&
    status.Status == X509ChainStatusFlags.UntrustedRoot)
{
   // Certificates with a broken chain and
   // self-signed certificates with an untrusted root are valid. 
   continue;
}
else if (status.Status != X509ChainStatusFlags.NoError)
{
   // If there are any other errors in the certificate chain,
   // the certificate is invalid, so the method returns false.
   return false;
}

或者,檢查Subject屬性:

private static bool CertificateValidationCallBack(
   object sender,
   System.Security.Cryptography.X509Certificates.X509Certificate certificate,
   System.Security.Cryptography.X509Certificates.X509Chain chain,
   System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
   return certificate.Subject.Contains(".dsoduc.com");
}

引用自:https://stackoverflow.com/questions/9058096