企業自頒布服務器證書的有效性驗證(C#為例)


版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/notjusttech/article/details/72779904

目前根據項目的需要,整理了一下公司內部的安全通信規范,將其中涉及證書驗證的部分分享出來,供探討。

處於通信安全的考慮,很多通過互聯網進行的通信,

原則上均要求使用加密通信,即采用基於SSL或TLS的安全通信。對於Web應用及Web API來說,原則上應該一律使用HTTPS,禁用HTTP;對於Socket通信來說,原則上應采用SSL協議加密傳輸。

基於SSL協議,需要服務器證書。對於Web應用及Web API(統稱站點),原則上應使用由全球可信機構頒發的證書。但是考慮到證書費用、站點條件等一些因素,對於下列情形,可考慮使用自頒發證書:

1.      站點只在特定局域網、專網內部署使用;

2.      站點只有IP地址,沒有注冊域名;

3.      站點所有者不願意支付證書費用。

 

若考慮企業自己建個證書頒發機構(CA),那就涉及到客戶端對服務器證書的驗證問題。目前很多人提到的做法都是實現自定義驗證,但所謂的自定義驗證就是直接返回true。這樣處理,雖然能進行HTTPS加密通信,但由於實際上未對服務器證書做任何驗證,因此是存在中間人攻擊風險的。

本人研究了一下這個問題,發現針對.net開發,在中文網站上似乎沒有很好的實現建議,因此才想到整理這篇博文和大家分享我們的做法,歡迎各位大拿批評指正。

首先,要使用自頒發證書實現HTTPS通信,最好是要對企業自己的CA做一個統一管理,CA必須具有唯一性,並且CA一旦建立,原則上應保證永不更改,從而保證企業CA的權威性。CA的權威性,是通信過程中對服務器證書進行可信驗證、防止中間人攻擊的基礎。

其次,對自頒發證書的申請與獲取,也要遵循一定的原則,主要是證書的使用者要和實際站點IP地址或域名一致,另外根據需要考慮證書有效期問題。

最后,就是在客戶端對服務器證書的有效性驗證了。我們將這個問題分為兩部分:

(一)Web應用(網站)

 

對於Web應用來說,都是通過瀏覽器訪問,而瀏覽器對證書的驗證過程我們是無法干預的。對於自頒發證書,瀏覽器會提示證書無效。對於這個問題,我們的做法是參考鐵總12306網站的做法:

將我們的CA證書即根證書(公鑰)文件通過某種途徑,發送給所有用戶,要求用戶將根證書安裝到“受信任的根證書頒發機構”。從而防止中間人攻擊。

(二)Web API

 

Web API均為某種客戶端程序調用,調用接口需做業務權限驗證。因此對於證書的驗證,是在自研程序中執行。

對於自頒發證書,各研發平台的默認驗證肯定是不通過的,有兩種方式:

1、             跟Web網站一樣,要求所有客戶端設備(電腦、終端設備)均將我們的根證書安裝到可信證書區域,從而可使用默認的驗證方式,代碼不用做任何額外處理。但這種方式,會有一定局限性,很多時候不大可行。

2、             將根證書集成到項目中,通過代碼實現自定義驗證。自定義驗證項目包括:

a)        構建證書鏈,看服務器證書是否能鏈接到我們的根證書;這里對於不同的開發語言,處理可能不同。在C#中,需要將我們的根證書放到鏈引擎可搜索到的地方,然后設置合適的鏈策略,再重新構建證書鏈。

b)        上述驗證通過后,驗證服務器證書的使用者是否就是當前請求Uri的主機地址(IP或域名),是則通過驗證;否則就是A地址的證書,用在了B地址的網站上。

服務器證書的有效期可以不用驗證。由於默認的有效期是1年,如果驗證有效期的話,則服務器證書每年都要更新,對於自頒發證書,沒有這個必要,就讓它永遠有效。

 

如果客戶端是C#,可以按如下方式進行驗證

根據項目情況做一些初始化,比如初始化根證書等。

 1 static String caFilePath = "D:\\…….cer"; //本地根證書路徑
 2 static X509Certificate2 ca; //本地根證書對象
 3 
 4 //驗證代碼一
 5 WebRequestHandler wrh;
 6 wrh = new WebRequestHandler();
 7 wrh.ServerCertificateValidationCallback += RemoteCertificateValidate;
 8 //驗證代碼二 
 9 ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidate;//驗證服務器證書回調自動驗證
10 
11 
12 if (File.Exists(caFilePath))
13 {
14     ca = new X509Certificate2(caFilePath); 
15 }

 

使用該WebRequestHandler處理Https連接,以便實現自定義證書驗證

1 HttpClient client = new HttpClient(wrh);
2 ……

自定義驗證的實現:

 1 private bool RemoteCertificateValidate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
 2 {
 3     if (null != ca)
 4     {
 5         /*
 6          * 根證書未安裝到“受信任的根證書頒發機構”時,默認是無法形成可信證書鏈的。(chain中將只有服務器證書本身)
 7          * 需更改鏈策略,然后重新構建證書鏈。
 8         */
 9         // 將我們的根證書放到鏈引擎可搜索到的地方
10         chain.ChainPolicy.ExtraStore.Add(ca);
11         //不執行吊銷檢查
12         chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
13         //忽略CA未知情況、不做時間檢查
14         chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority | X509VerificationFlags.IgnoreNotTimeNested | X509VerificationFlags.IgnoreNotTimeValid;
15         //重新構建可信證書鏈
16         bool isOk = chain.Build(cert as X509Certificate2);
17         if (isOk)
18         {
19             X509Certificate2 cacert = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;  //獲取最前面的證書,認為是根證書
20             //與本地根證書比較
21             if (ca.GetPublicKeyString().Equals(cacert.GetPublicKeyString()) && ca.Thumbprint.Equals(cacert.Thumbprint))
22             {
23                 HttpWebRequest req = sender as HttpWebRequest;
24                 if (null != req && cert.Subject.Contains("CN=" + req.Address.Host))
25                 {
26                     return true;        //根證書可信且服務器證書確實是指定服務器的,驗證通過
27                 }
28             }
29         }
30     }
31     return false;
32 }
通過上述方式,可以靈活執行自定義的驗證,比如既驗證服務器證書確實是我們頒發給指定網站的,又可根據需要跳過證書有效性的驗證。

 

對於在java中做自定義驗證,可參考一下博文(本人沒仔細研究)

http://pingguohe.net/2016/02/26/Android-App-secure-ssl.html

--------------------- 本文來自 Darlzan 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/notjusttech/article/details/72779904

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM