在 C# 中覆蓋 ServicePointManager.ServerCertificateValidationCallback 時如何呼叫預設證書檢查?
我需要信任應用程序中的一些自簽名證書,所以我像這樣覆蓋驗證回調:
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>
該範例中的程式碼檢查證書鏈以確定證書是否是自簽名的,如果是,則信任它。您可以調整它以接受 a
PartialChain而不是或也一樣。你會想要做這樣的事情: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"); }