根證書含義 - 孤舟蓑笠翁,獨釣寒江雪
- 博客頻道 - CSDN.NET?
本文想簡單談談那個所謂的“根證書”。在訪問鐵道部網上售票官網 www.12306.cn 后,有一個醒目的提示,為保證順暢購票,需要下載安裝根證書。那么什么是根證書?為什么買火車票的時候需要下載和安裝,在淘寶等在線交易網站購物時候為什么就不需要這樣做?
今年開始,人民群眾們終于可以通過互聯網購買火車票了。雖然說在線買的難度不比以往排隊購買低多少,但這總算是一次值得鼓勵的嘗試。不過在線購票系統一經退出,在技術上就已經被人批得體無完膚。
為了確保安全,很多涉及在線交易的網站,例如網上銀行、購物網站等,都會使用SSL技術對頁面內容進行加密。SSL技術在這里的主要用途有兩個:
確保網站服務器和用戶瀏覽器之間的通訊不被竊聽:這一點很好理解。SSL屬于一種公鑰加密體系,簡單來說,一個SSL證書分為兩部分:公鑰和私鑰。其中私鑰會被網站所有者妥善保管,并在服務器端用私鑰將網絡通訊全部加密;而公鑰會在網上廣為傳播,一個公鑰加密后的數據只有用所對應的私鑰才能解密。因此只要SSL證書本身可以保證安全,那么在訪問網站的時候就可以保證網絡通訊不被他人所竊取,并且如果有人進行中間人攻擊,因為沒有相應的密鑰,導致篡改后的數據無法通過校驗,因此可以及時察覺。
確保網站所宣稱的身份真實可靠:這一點也不難理解。網上有個網站叫做支付寶,可如何保證這個網站就是那個真正的支付寶,而不是其他人偽造的釣魚網站?因此真正的支付寶可以使用SSL證書,這種針對企業用的證書的申請手續比較繁瑣,有一大堆審查流程,需要提交大量相關的證明文件,因此可以保證只有真正的某公司才能以這個公司的名義申請證書,只要申請到證書,就可以確信身份的可靠。此外近些年還有一種更可靠的EVSSL證書。
試試看訪問“https://www.alipay.com”這個域名,隨后可以看到,在瀏覽器的地址欄,“Http”協議后面出現了“s”這個字母,并且IE地址欄的右側顯示了一個鎖頭圖標,因此證明該網站是SSL加密的。點擊這個鎖頭圖標后,還可以看到網站所用證書的相關信息。
加密功能基本上沒什么需要解釋的,因此下文的重點將放在身份可靠性這塊。在上圖所示界面上點擊“查看證書”鏈接,隨后可以看到該網站SSL證書的詳情:
上圖顯示的“VeriSign Class 3 International Server CA – G3”是證書頒發機構,而“www.alipay.com”是證書持有人。那么到底應該怎樣通過這些信息判斷網站是否可靠?
在這里一個很通用的規則是:如果瀏覽器檢測到加密網站所用的證書是正常的,那么地址欄就會顯示為綠色(使用EVSSL證書)或者白色(使用普通SSL證書),這種情況下可以放心地瀏覽該網站,并提交自己的數據;如果瀏覽器檢測到網站的證書有問題,那么地址欄就會顯示為紅色,提醒我們注意,同時取決于具體情況,地址欄右側會顯示有“證書錯誤”按鈕,而且網站內容不會顯示,取而代之的是瀏覽器的警告信息?;旧现髁鳛g覽器的最新版本都已經具備這樣的功能。
這個過程的基本原理是:假設我們信任A公司,而A公司信任B公司,那么我們就可以信任B公司。很明顯,“www.alipay.com”的證書是“VeriSign
Class 3 International Server CA –
G3”頒發的,這表示后者信任前者,可以證明前者的真實身份,但是我們又憑什么信任后者這個證書頒發機構?
上述文字里不止一次提到“信任”一詞,那么“信任”在這里是什么意思?是否像我們平時講話時說的“我信任他”那樣,代表我們相信他是個好人,不會干壞事?其實完全不是這樣。這里所說的“信任”,只是說明證書持有人的身份是真實可靠的,至于持有人用這個證書干什么事情,不在“信任”的范疇內。例如,網上很多臭名昭著的惡意軟件,現在都帶有數字證書(倒是很多正規用途的軟件因為開發商缺乏安全觀念不帶證書),同時因為這些數字證書的“根”都是我們信任的根證書頒發機構,因此,操作系統和瀏覽器是信任這些公司的身份的,但并不代表這些公司的軟件不會干壞事。只要肯花錢,任何人都可以在商業性質的證書頒發機構買到直接被我們的系統所信任的證書。
其實Windows和任何操作系統本身就包含一些受信任證書頒發機構的根證書,要查看這些根證書,可以運行“certmgr.msc”打開證書控制臺,然后從控制臺窗口左側的控制臺樹中依次進入“證書-當前用戶”→“受信任的根證書頒發機構”→“證書”,隨后右側的窗口中會顯示本機預置的所有根證書頒發機構,其中就有“CA
– G3”,這表示我們信任“CA – G3”,而“CA –
G3”信任“www.alipay.com”,因此我們可以信任“www.alipay.com”。如果從證書控制臺中刪除“CA –
G3”的根證書,表示我們不再信任它,那么它所信任的公司也將不再被我們信任。
按照上文的方法打開證書控制臺,并進入到“不信任的證書”→“證書”節點后,還會發現里面列出了多個頒發給大量知名企業的證書,例如Microsoft、Google、Skype、Yahoo等。為什么會不信任這些大牌互聯網企業?就是因為某家根證書頒發機構被黑客攻破,導致這些大企業所用的證書私鑰被竊取。因此為了保證安全,這些被盜證書已經被吊銷。因而將原本被盜的證書都添加到“不信任的證書”節點下,這樣盜取證書的黑客就算使用這些證書給病毒簽名,偽裝成這些大企業,也會因為證書吊銷的緣故不會讓人輕易上當。這家倒霉的公司就是荷蘭的DigiNotar,該公司因為這次事件現在已經破產,而從中也足以證明證書吊銷機制的重要性。這一點會在下文詳細介紹。微軟已經通過KB
2607712補丁將受影響的證書全部設置為不信任。
這里要重點提出“根證書”這個概念,全世界具有提供數字證書業務的公司有很多,而Windows自帶的“根證書”很少,默認情況下,我們是如何信任這么多不同公司頒發的不同證書的?其實這就是“根”這個字的含義,因為可以頒發證書的公司雖然很多,但最基本的根證書頒發機構只有有限的幾個,默認情況下都是被操作系統所信任的。那么既然操作系統能信任根證書頒發機構,自然也就可以信任被根證書頒發機構信任的公司,進而可以信任被這些公司所信任的下一級公司。
如何證明這一點?可以單擊上圖中的“查看證書”鏈接,隨后打開“證書”對話框,切換到“證書路徑”選項卡后,可以看到下圖所示界面。從該圖中可以看出,整個證書信任鏈的路徑分為三個層次,最頂層的是我們信任的根證書頒發機構,該機構給“International
Server CA –
G3”頒發了證書,因此,我們信任“CA-G3”;隨后“CA-G3”又給“www.alipay.com”頒發了證書,因此也可以信任“www.alipay.com”。如果“www.alipay.com”再給別人頒發證書,那么這個人的身份依然可以被我們信任。
而鐵道部的在線訂票網站并沒有走這個一般意義上,涉及金融交易的商業化網站都嚴格遵守的方法。也就是說,鐵道部并沒有花錢在商業性質的CA(證書頒發機構)購買受信證書,他們直接自己給自己頒發證書。這樣的做法一般主要是用于測試或學習等非正式場合,但如果一個商業化網站想要正式上線運營,通常并不會這樣做,因為對用戶來說風險太大。
當我們查看12306.cn 這個網站的證書信息時就會發現,這個證書根本沒有一個有效的受信任CA,完全是自己給自己發著玩的。
上圖信息顯示,該證書的頒發者以及根證書是“SRCA”,不知道這個縮寫代表什么意思,可能是鐵道部內部的某個系統。因為SRCA的身份不被系統自帶的任何一個根證書所信任,因此12306.cn網站所用的證書默認也不會被任何一個系統所信任。因而鐵道部要求安裝根證書的原因,就是讓訪客將這個證書手工添加到“受信任的根證書頒發機構”節點下。
很遺憾的是,這種做法雖然很不安全,可國內的大佬們很喜歡使用。例如在我的系統中,這里就有建行和工行網銀自行添加的根證書。
那么這種做法除了能省幾個錢之外,對用戶來說有什么危害?
在12306.cn訂票的很多人可能會看到過“該站點安全證書的吊銷信息不可用,是否繼續”這樣的信息。這是什么意思?
還是以上文那個DigiNotar的例子來說,假設某個大型CA被攻擊,私鑰被竊取,這時候有兩種方法盡量避免損失:
類似Windows補丁這樣,由軟件廠商通過發布更新的方式,將被盜證書強制設置為不信任。
使用證書吊銷列表(CRL)。
第一種方式比較好理解,但并不是所有公司都能獲此殊榮。畢竟主流操作系統的用戶數量龐大,根本不可能針對一個地區性的,或者規模很小的公司的被盜證書發布更新,強制不信任。因此第二種情況就至關重要了。每個證書中都包含CRL,其實這個可以理解為一個網址,通過這個網址可以獲得證書吊銷的相關信息。
因此如果一個小公司通過商業性CA購買的證書被盜了,只要將相關信息告知CA,這家CA就會將這個證書的內容添加到CRL中。隨后任何一個用戶在執行涉及到證書的操作,例如安裝帶有數字簽名的軟件,或者訪問SSL網站的時候,系統都會通過這個CRL地址檢索吊銷清單,并查看當前軟件或網站使用的證書是否位于清單中。如果不在,就證明這個證書依然是可信任的;如果在,就證明該證書已經被盜,因此軟件或網站存在仿冒的可能。
而因為12306.cn使用了自己給自己頒發的證書,因此也就根本無法在自己的證書中包含CRL信息,所以會看到“吊銷信息不可用”的提示,這意味著瀏覽器在告訴你,你所訪問的網站,不一定能完全證明其可靠,這可能是真網站,但也有是釣魚網站的可能。
而如果12306.cn的證書私鑰丟失或被盜(看看去年底的大規模泄密事件,誰敢保證沒有這種可能性),持有該證書的人想要偽造一個釣魚網站,或者以鐵道部的身份發布惡意軟件,那真是輕而易舉,并且鐵道部對此會束手無策。
畢竟在線購買火車票的人全都安裝了這個根證書,而該證書根本無法通過CRL吊銷。此外還有一個更重要的問題,如果盜取該證書的人繼續使用“SRCA”的身份給其他人以其他身份頒發證書,例如以銀行或支付中介的名義,結果會怎樣?因為所有在線購買過火車票的人,由于根證書的關系,系統已經信任SRCA的根證書,因此這些偽造的證書也會直接被信任。最壞的情況下,所有熱門的SSL加密網站(購物、銀行、股票….)要想被偽造都是輕而易舉的。
什么意思呢?如果某天你訪問的“支付寶”網站的證書信任鏈是下面這樣,你覺得會是什么后果?
真心希望這種情況永遠不會發生。同時更加希望國內這些大佬們有更多安全意識,盡快認識到目前這種做法的不足。商業受信證書雖然需要花錢買,但不是太貴,真的!
當然,上述希望可能永遠不會成真,因此作為一般用戶,如果你已經成功在線購買到火車票(恭喜你啊,你要不要去買個彩票試試手氣),那么建議你在“受信任的根證書頒發機構”節點下將SRCA的根證書徹底刪除。
Java編程語言的一個杰出之處就在于開源社區可以以較低的成本或者甚至是免費地提供優秀的應用程序。其中一個例子就是Apache Tomcat,它為使用servlet或JSP技術的開發提供了一個健壯的Web服務器。現在Web服務技術正日趨成熟,所以有些應用程序就有可能利用 Swing特性豐富的前端瘦客戶端結合Web或ejb層已經開發出來的數據驗證和業務邏輯。此類應用程序只有在受到保護的情況下才能正常運行,不過,安全性不一定意味著昂貴的成本。本文的目的就是要演示Web服務客戶端如何通過安全的HTTPS協議使用自簽名的安全證書。
HTTPS
通??梢詿o縫地與不安全的HTTP協議一起使用,而不中斷用戶的體驗。這是由于SSL被設計為由可信的第三方進行驗證和簽名。Verisign是一家流行的認證機構。如果您的Web應用程序要求安全的通信,那么您就可以付錢給Verisign來簽名您的SSL證書。經過Verisign簽名之后,您的
Web站點上的用戶就可以不中斷地在HTTP與HTTPS之間進行切換,因為所有主流Web瀏覽器都信任由Verisign簽名的證書。但是
Verisign并不是獲得簽名證書的惟一選擇。為了節省運作成本,或者為了個人使用方便,您也可以自簽名自己的證書。但是,自簽名證書會中斷Web站點用戶的體驗。通常Web瀏覽器會顯示一個對話框,詢問您是否希望信任一個自簽名證書。
Web瀏覽器的這一特性很好,因為當其獲得一個由未知認證機構簽名的證書時,還有機會進行處理。在開發用于通過HTTPS進行通信的Web服務客戶端時,這就沒那么容易了。在運行Java代碼時,不會出現詢問是否信任一個不可信的認證機構的對話框。JRE會拋出一個異常,說明試圖通過HTTPS連接到一個具有不可信證書的Web站點:
Caused by:sun.security.validator.ValidatorException:No trusted certificate found
無法捕獲此異常并繼續。要讓Web服務使用自簽名證書,JRE必須以某種方式將您當作認證機構信任。
為演示此問題的解決方案,我將執行以下步驟:
生成并自簽名我自己的證書;
為Tomcat配置SSL,使其使用該證書;
創建一個示例Web服務,以便通過HTTPS調用;
從WSDL生成Web服務客戶端代碼;
使用定制的密鑰庫解決方案演示客戶端;
JDK附帶了一個工具,keytool.exe,用于管理SSL公鑰/私鑰。密鑰在文件系統的一個二進制文件中進行添加和刪除。默認的密鑰庫文件是
JAVA_HOME\jre\lib\security\cacerts。該文件包含了JRE所信任的認證機構的列表。一個知名可信公司(比如
Verisign)的列表已經存在于密鑰庫中了。要查看該列表,可使用口令changeit執行以下代碼:
D:\>keytool -list -rfc -keystore JAVA_HOME\jre\lib\security\cacerts
Keytool應用程序可用于編輯此文件。但是,為了防止出錯,最好還是創建一個新文件。如果不告知keytool使用哪個文件,它就會默認地創建HOME/.keystore。
要生成自己的自簽名證書,可執行:
D:\>keytool.exe -genkey -alias Tomcat -keyalg RSA -storepass bigsecret -keypass bigsecret -dname "cn=localhost"
執行完該命令后,就會在HOME目錄下生成一個.keystore文件。下面是各種切換命令的含義:
genkey:告訴keytool應用程序生成新的公鑰/私鑰對。
alias:用于引用密鑰的名稱。記住,.keystore文件可包含多個密鑰。
Keyalg:使用RSA算法生成公鑰/私鑰對。
Storepass:訪問.keystore文件所需的口令。
Keypass:管理密鑰所需的口令。
dname:該值非常重要。.我使用了localhost,因為該示例被設計為本地運行。如果一個Web應用程序被注冊為http://www.myserver.com,那么該值就必須是www.myserver.com。如果名稱不匹配,證書就會自動被拒絕。
一旦keytool應用程序創建了一個新的公鑰/私鑰對,它就自動自簽名該密鑰。我們剛剛生成了自己的自簽名證書,它可用于HTTPS通信。只需提取出自簽名公鑰。后面我將展示如何做。
現在必須配置Tomcat,使其使用自簽名證書。我使用的是Tomcat
5.0.30。編輯TOMCAT/conf/server.xml文件。在文件中搜索“8443”,并取消綁定到該端口的注釋。然后必須向添加下屬屬性:
keystorePass="bigsecret"
當JRE啟動時,它將自動找到HOME/.keystore文件,并且Tomcat會試著使用口令“bigsecret”訪問它。在Tomcat啟動時,控制臺應該有如下輸出:
Feb 4, 2006 3:11:23 PM org.apache.coyote.http11.Http11Protocol start
INFO:Starting Coyote HTTP/1.1 on http-8443
這意味著成功地讀取了.keystore文件,現在可以通過8443端口進行安全的HTTPS連接了。打開Web瀏覽器,并在地址欄輸入https://localhost:8443/。因為該證書是自簽名的,所以Web瀏覽器將顯示一個對話框,詢問是否信任該連接。如果接受,則所有的通信都將通過HTTPS進行,從而成為安全的。
我將使用Apache Axis項目創建一個非常簡單的Web服務。該Web服務將模擬檢查新的電子郵件消息。Web服務客戶端傳遞一個惟一地識別一個用戶的令牌。Web服務返回一個新電子郵件消息的列表(參見清單1)。
import java.util.*;
public class Email {
public List
getNewMessages(String id)
{
List l = new ArrayList(3);
l.add("1");
l.add("2");
l.add("3");
return l;
}
}
要獲取已部署的Web服務,執行以下步驟:
從清單1剪切并粘貼代碼到Webapp的根目錄下的Email.jws文件。
編輯Web.xml文件,添加Axis servlet以及一個*.jws映射(清單2)。
將Axis jar文件放入WEB-INF/lib。請參見文章末尾的“參考資料”部分,獲取Axis項目URL。
AxisServlet
Apache-Axis Servlet
org.apache.axis.transport.http.AxisServlet
AxisServlet
*.jws
在部署了本文所附帶的WAR文件(并為Tomcat配置SSL)之后,Web就可以安全地通過HTTPS使用下面的URL來訪問了:
https://localhost:8443/JDJArticleWebService/Email.jws
Axis項目提供了一個名為WSDL2Java的工具,它獲取一個Web服務WSDL并自動創建使用該Web服務所需的Java源代碼。參見清單3中用于生成Email.jws Web服務代碼的命令行。
java
-classpath
.;axis.jar;log4j-1.2.8.jar
;commons-logging-1.0.4.jar
;commons-discovery-0.2.jar
;jaxrpc.jar;saaj.jar
;wsdl4j-1.5.1.jar
org.apache.axis.wsdl.WSDL2Java
-p jdj.wsclient.shared
http://localhost:8080/JDJArticle/Email.jws?wsdl
注意清單3
中用于訪問WSDL的URL。它在8080端口使用了不安全的HTTP協議。為什么不在8443端口使用HTTPS呢?這是因為自簽名證書,WSDL2Java工具將遇到與本文所試圖解決的證書問題完全相同的問題。所以現在必須使用使用不安全的協議。這意味著生成的代碼必須有一點改變,使用“https”和“8443”替換“http”和“8080”引用。本文所附帶的客戶端zip文件包含了更改后的代碼。
JRE的默認密鑰庫是JAVA_HOME\jre\lib\security\cacerts。只要出現自簽名證書,Java應用程序就會拋出異常,因為該證書不在密鑰庫中。因此,在開發客戶端時有兩種選擇。第一種選擇是將自簽名證書放入該JRE的默認密鑰庫中。雖然這種方法有效,但是它并不是一個好的解決方案,因為需要在每個客戶端機器上進行定制化。第二種選擇是生成一個定制的密鑰庫,將自簽名證書放入其中,并將定制密鑰庫作為應用程序的一部分分發(通常在一個
jar文件中)。
要為客戶端創建定制密鑰庫,需要執行以下步驟:
從HOME/.keystore導出自簽名公鑰。
將自簽名公鑰導入到為客戶端創建的新密鑰庫中。
要從HOME/.keystore導出自簽名公鑰,可執行以下代碼:
D:\>keytool.exe -genkey -alias Tomcat -keyalg RSA -storepass bigsecret -keypass bigsecret -dname "cn=localhost"
現在通過導入Tomcat.cer,為客戶端創建定制密鑰庫:
D:>keytool.exe -import -noprompt
-trustcacerts -alias Tomcat -file Tomcat.cer -keystore CustomKeystore
-storepass littlesecret
使用 “-keystore
CustomKeystore”,將會在當前工作目錄中創建一個名為CustomKeystore的新密鑰庫文件??梢栽诒疚牡目蛻舳藌ip文件的
/classpath/resources/keystore目錄下找到CustomKeystore文件。使用剛剛生成的文件替換該文件。
現在只剩下創建一個使用該定制密鑰庫的客戶端了。.我將演示兩種實現方法。
第一種方法是使用Java系統屬性javax.net.ssl.trustStore和javax.net.ssl.trustStorePassword來指向
CustomKeystore文件,并提供訪問該文件的口令。jdj.wsclient.truststore包中的示例Web服務客戶端使用的就是這種方法(參見清單4)。
public static
void main(String[] args)
throws Exception
{
System.setProperty(
"javax.net.ssl.trustStore",
"classpath/resources/keystore/CustomKeystore");
System.setProperty(
"javax.net.ssl.trustStorePassword",
"littlesecret");
EmailServiceLocator wsl =
new EmailServiceLocator();
Email_PortType ews =
wsl.getEmail();
Object [] objects =
ews.getNewMessages("12345");
out("Msg Count: " + objects.length);
}
main()方法設置系統屬性,然后創建使用該Web服務的對象。當JRE需要訪問密鑰庫時,它就在文件系統中尋找
classpath/resources/keystore/CustomKeystore文件。雖然這只是一個簡單的解決方案,但它還是存在問題,因為密鑰庫文件必須放在文件系統中,而客戶端代碼也必須知道在哪里找到它。
第二種解決方案具有更好的可移植性,它將資源放在jar文件中,從而避免了文件系統問題??蛻舳舜a負責讀取CustomKeystore文件,并以某種方式使用它創建到服務器的安全連接。jdj.wsclient.socketfactory包中的示例Web服務客戶端使用的就是這種方法(參見清單5)。
public MySocketFactory(Hashtable table)
throws Exception
{
out("Created!");
KeyStore ks =
KeyStore.getInstance(
KeyStore.getDefaultType()
);
char [] password =
"littlesecret".toCharArray();
String keystore =
"/resources/keystore/CustomKeystore";
Class tclass =
this.getClass();
InputStream is =
tclass.getResourceAsStream(
keystore
);
ks.load(is, password);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("SunX509");
kmf.init(ks,password);
TrustManagerFactory tmf =
TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext context =
SSLContext.getInstance("SSL");
context.init(
kmf.getKeyManagers(),
tmf.getTrustManagers(),
new SecureRandom()
);
factory =
context.getSocketFactory();
}
清單5顯示了如何將CustomKeystore文件作為資源讀取,并使用它來創建javax.net.ssl.SSLSocketFactory。配置Axis可插入架構,然后可使用MySocketFactory類從該工廠創建安全的Socket對象。
本文以一個簡單的問題開始:我希望使用自簽名的證書保護通過HTTPS的Web服務通信。默認情況下,JRE會拒絕應用程序的自簽名證書,因為它不是來自于可信的認證機構。要讓安全的通信可運行,必須讓Web服務客戶端JRE信任自簽名證書。為此,我使用keytool應用程序生成一個新的公鑰/私鑰對,提取出自簽名公鑰,然后創建一個新的密鑰庫,并導入該自簽名證書。然后我創建一個不需要任何客戶端配置的、完全自包含的Web服務客戶端