如何用Tomcat和Openssl構建HTTPS雙向認證環境(HTTPS客戶端認證)
本文將介紹如何利用Tomcat的HTTPS功能,和一個自己建立的CA,來構建WEB伺服器證書和個人數字證書,最終建成一個HTTPS雙向認證環境(可以用於測試目的)。本文構建HTTPS雙向認證的業務流程大致如下:
1. 建立WEB伺服器公鑰金鑰,並生成伺服器證書請求。
2. 利用自建的CA,根據伺服器證書請求為伺服器簽發伺服器證書。然後把伺服器證書導回WEB伺服器中。
3. 利用openssl生成客戶端(IE)使用的個人數字證書,也由同樣的CA簽發個人證書。
4. 將個人數字證書(PKCS12格式,包含金鑰)匯入到瀏覽器(IE/Firefox)後,就可以進行HTTPS測試了。
一. 選擇HTTPS WEB伺服器
這裡我們選擇了Tomcat。當然還有其它方法,如Apache+Tomcat,讓Apache配置成HTTPS模式,而Tomcat只做HTTP業務處理,這樣有利於提高效能,但本文只建造一個簡單的HTTPS測試環境,只用Tomcat自帶的HTTPS功能。(當然,我以後會考慮研究一下Apache+Tomcat模式,到時再和大家一起分享經驗)
二. 建立一個自己的CA
建立一個自己的CA來模擬HTTPS構建環境(利用CA簽發證書),不僅有利於瞭解PKI概念,而且有利於瞭解真正應用的業務流程。關於如何建立一個簡單的測試用CA我已在本部落格發文,題目為《利用openssl建立一個簡單的CA》,請參考http://blog.csdn.net/jasonhwang/archive/2008/04/26/2329589.aspx
另外,如果你真的覺得建一個CA比較麻煩,且只使用幾個個人數字證書的話,當然也可以不建CA,下面章節也會提到不用CA如何構建雙向認證。
三. 建立伺服器公鑰金鑰及頒發伺服器證書
實際上有兩種方法生成伺服器證書,一種是用JDK帶的keytool,另一種是用openssl命令生成pkcs12格式的證書。個人更喜歡第二種,因為用openssl生成的pkcs12格式伺服器證書可以匯出明文的伺服器金鑰,便於用wireshark(ethereal的新名字)檢視HTTPS裡被加密過的HTTP訊息。keytool生成的keystore也有辦法匯出金鑰,但還要程式設計去弄,太麻煩了。
方法一,用keytool建立伺服器公鑰金鑰並用CA簽發伺服器證書:
keytool是JDK自帶的工具程式,確保keytool已在PATH路徑裡(或在以下命令裡指明keytool的全路徑,如$JAVA_HOME/bin/keytool)。
1. 用keytool生成伺服器公鑰金鑰:
在TOMCAT的conf目錄裡執行以下命令(假設conf目錄路徑為$TOMCAT_HOME/conf/):
keytool -keystore tomcat.jks -keypass 222222 -storepass 222222 -alias tomcat -genkey -keyalg RSA -dname "CN=servername, OU=servers, O=ABCom"
注:其中DN(證書的唯一取別名)裡的CN最好是伺服器的域名地址或IP地址(因為瀏覽器在發現伺服器證書的CN與訪問伺服器的域名或IP不符時,會報一個警告,不過這個問題不大)。
2. 生成伺服器證書請求,並讓測試CA簽發伺服器證書
實際上本步驟也可以省略,唯一不好的結果是使用者在開始訪問伺服器HTTPS服務時,會跳一個視窗警告使用者,使用者可以在該窗口裡看到伺服器證書是一個自簽名的證書(用伺服器私鑰自己給自己簽發的證書,keytool生成公鑰金鑰時自動生成這種證書)。但只要使用者選擇“信任該證書”,也照樣可以與伺服器建立HTTPS連線。
但正規的HTTPS伺服器,還是應該申請一個伺服器證書比較好。
2.1 生成伺服器證書請求:
keytool -keystore tomcat.jks -keypass 222222 -storepass 222222 -alias tomcat -certreq -file serverreq.pem
以下命令可以檢視一下證書請求的內容:
openssl req -in serverreq.pem -text -noout
2.2 用測試CA簽署伺服器證書:
把serverreq.pem拷貝到CA的某目錄下,我們就可以按照《利用openssl建立一個簡單的CA》裡的“CA的日常操作”的“1. 根據證書申請請求籤發證書”章節進行證書籤發了:
openssl ca -in serverreq.pem -out servercert.pem -config "$HOME/testca/conf/testca.conf"
執行過程中需要輸入CA私鑰的保護密碼。
2.3 把伺服器證書導回到伺服器的keystore裡:
前面命令裡生成的servercert.pem即為伺服器證書。從CA處把該檔案及CA的證書($HOME/testca/cacert.pem)拿來,一起放到Tomcat的conf目錄裡。
另外匯入前,還有一個工作需要做,需要手工編輯servercert.pem證書檔案,把‘-----BEGIN CERTIFICATE-----’該行前的所有內容都刪掉,因為keytool不認得這些說明性的內容。
匯入前也可以執行命令檢視一下證書內容:
keytool -printcert -file servercert.pem
匯入伺服器證書:
先匯入CA的證書:
keytool -keystore tomcat.jks -keypass 222222 -storepass 222222 -alias ca -import -trustcacerts -file cacert.pem
再匯入伺服器證書:
keytool -keystore tomcat.jks -keypass 222222 -storepass 222222 -alias tomcat -import -file servercert.pem
匯入後可以用以下命令檢視一下keystore裡的內容:
keytool -keystore tomcat.jks -keypass 222222 -storepass 222222 -list -v
3. 建立伺服器信任的客戶端CA證書庫:
用keytool建立一個證書庫,裡面存放伺服器信任的CA證書,也就是隻有這些CA簽發的客戶端個人證書才被伺服器信任,才能通過HTTPS訪問伺服器。這就是“HTTPS伺服器驗證客戶端證書”的關鍵配置。注:如果只是測試目的,為了簡單期間,也可以直接把客戶端未經CA簽發的自簽名證書直接匯入信任證書庫裡。
這裡,我們假設客戶端個人證書(後續章節介紹如何生成客戶端個人證書)也是由測試CA簽發的,所以我們要把cacert.pem證書匯入信任證書庫:
keytool -keystore truststore.jks -keypass 222222 -storepass 222222 -alias ca -import -trustcacerts -file cacert.pem
可以用以下命令檢視信任證書庫內容:
keytool -keystore truststore.jks -keypass 222222 -storepass 222222 -list -v
4. 配置Tomcat支援HTTPS雙向認證(伺服器將認證客戶端證書):
修改tomcat的conf目錄裡的server.xml檔案($TOMCAT_HOME/conf/server.xml),找到類似下面內容的配置處,新增配置如下:
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="conf/tomcat.jks" keystorePass="222222" keystoreType="JKS"
truststoreFile="conf/truststore.jks" truststorePass="222222" truststoreType="JKS" />
(題外話:其實HTTPS單向和雙向認證配置的唯一區別是,把clientAuth改為false,去掉truststore的相關配置,就是單向HTTPS認證了,單向HTTPS用的可能更多,它主要在瀏覽器與f伺服器互動的HTTP需要加密,而不需要驗證客戶端證書時使用。)
經以上配置後,重啟tomcat,伺服器就支援HTTPS雙向認證了。
方法二,用openssl建立伺服器公鑰金鑰並用CA簽發伺服器證書:
前面已經提到過,這種方式的好處是有利於抓包檢視伺服器與瀏覽器HTTPS互動裡的HTTP資訊。
其實,這種方法與《利用openssl建立一個簡單的CA》裡提到的製作個人數字證書方法很類似(請參考《利用openssl建立一個簡單的CA》的“三. 自己生成公鑰金鑰,並用測試CA簽發數字證書”章節)。
1. 製作伺服器證書(最終形成一個pkcs12檔案,包含伺服器金鑰、證書和CA的證書)
假設我們把伺服器相關的東西生成到CA的$HOME/testca/test/server目錄裡:
mkdir -p "$HOME/testca/test/server"
cd "$HOME/testca/test/server"
2.1 建立伺服器公鑰金鑰,並同時生成一個伺服器證書請求:
openssl req -newkey rsa:1024 -keyout serverkey.pem -keyform PEM -out serverreq.pem /
-outform PEM -subj "/O=ABCom/OU=servers/CN=servername"
執行命令過程中輸入金鑰保護密碼222222。
執行後可以用以下命令檢視請求內容:
openssl req -in serverreq.pem -text -noout
2.2 用測試CA簽署伺服器證書:
把serverreq.pem拷貝到CA的某目錄下,我們就可以按照《利用openssl建立一個簡單的CA》裡的“CA的日常操作”的“1. 根據證書申請請求籤發證書”章節進行證書籤發了:
openssl ca -in serverreq.pem -out servercert.pem -config "$HOME/testca/conf/testca.conf"
執行過程中需要輸入CA私鑰的保護密碼。
執行完後可以用以下命令檢視證書內容:
openssl x509 -in servercert.pem -text -noout
2.3 製作伺服器pkcs12檔案(包含伺服器金鑰、證書和CA的證書)
openssl pkcs12 -export -in servercert.pem -inkey serverkey.pem /
-out tomcat.p12 -name tomcat -CAfile "$HOME/testca/cacert.pem" /
-caname root -chain
執行過程中要輸入伺服器金鑰的保護密碼(serverkey.pem)和新生成的tomcat.p12的保護密碼,我們都輸入222222。
建立完成後,把pkcs12檔案拷貝到tomcat的conf目錄下。
3. 建立伺服器信任的客戶端CA證書庫:
同方法一的對應章節,這裡,我們假設客戶端個人證書(後續章節介紹如何生成客戶端個人證書)也是由測試CA簽發的,所以我們要把cacert.pem證書匯入信任證書庫:
keytool -keystore truststore.jks -keypass 222222 -storepass 222222 -alias ca -import -trustcacerts -file cacert.pem
可以用以下命令檢視信任證書庫內容:
keytool -keystore truststore.jks -keypass 222222 -storepass 222222 -list -v
4. 配置Tomcat支援HTTPS雙向認證(伺服器將認證客戶端證書):
修改tomcat的conf目錄裡的server.xml檔案($TOMCAT_HOME/conf/server.xml),找到類似下面內容的配置處,新增配置如下:
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="conf/tomcat.p12" keystorePass="222222" keystoreType="PKCS12"
truststoreFile="conf/truststore.jks" truststorePass="222222" truststoreType="JKS" />
注意:其中keystore的keystoreType與方法一的配置不同。經以上配置後,重啟tomcat,伺服器就支援HTTPS雙向認證了。
四. 建立用於客戶端(瀏覽器)測試的個人數字證書(pkcs12格式)
完全按照《利用openssl建立一個簡單的CA》裡提到的製作個人數字證書方法來進行,請參考《利用openssl建立一個簡單的CA》的“三. 自己生成公鑰金鑰,並用測試CA簽發數字證書”章節。
1. 建立個人金鑰和證書請求(證書請求裡包含了公鑰)
建立$HOME/testuser1目錄並執行命令:(證書等都放到這個目錄)
mkdir $HOME/testuser1
cd $HOME/testuser1
openssl req -newkey rsa:1024 -keyout testkey.pem -keyform PEM -out testreq.pem -outform PEM -subj "/O=TestCom/OU=TestOU/CN=testuser1"
執行過程中需要輸入私鑰的保護密碼,假設我們輸入密碼: 222222
執行完後,testkey.pem即為使用者的金鑰,而testreq.pem即為證書請求。
可以用openssl req -in testreq.pem -text -noout檢視證書請求的內容。
2. 用CA為testuser1簽發個人證書
同樣還在$HOME/testuser1目錄下執行命令:
openssl ca -in testreq.pem -out testcert.pem -config "$HOME/testca/conf/testca.conf"
執行過程中需要輸入CA的金鑰保護密碼(剛才設定的888888),並且最後詢問你是否要給該使用者簽發證書時要選y。
執行完後,testcert.pem即為證書,
可以用命令openssl x509 -in testcert.pem -text -noout檢視證書內容。
3. 製作PKCS12檔案(個人數字證書)
我們製作的這個PKCS#12檔案將包含金鑰、證書和頒發該證書的CA證書。
把前幾步生成的金鑰和證書製作成一個pkcs12檔案的方法執行命令:
openssl pkcs12 -export -in testcert.pem -inkey testkey.pem -out testuser1.p12 -name testuser1 -chain -CAfile "$HOME/testca/cacert.pem"
執行過程中需要輸入保護金鑰的密碼(222222),以及新的保護pkcs12檔案的密碼(222222)。
執行完後,若要檢視testuser1.p12的內容可以用命令openssl pkcs12 -in testuser1.p12
該testuser1.p12即為pkcs12檔案。你可以直接拷貝到windows下,作為個人數字證書,雙擊匯入IE後就可以使用了。
五. 用一個簡單webapp來測試HTTPS
伺服器證書和個人證書都準備好了,我們可以寫一個簡單的webapp,裡面只有一個jsp,用於檢視https客戶端驗證是否起效了。
1. 建立webapp用於測試https:
在$TOMCAT_HOME/webapps/裡建立一個目錄testhttps,並在testhttps目錄裡建立WEB-INF目錄,並在該目錄裡建立web.xml檔案如下:
檔案:$TOMCAT_HOME/webapps/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Test HTTPS App</display-name>
</web-app>
2. 建立一個顯示客戶端證書資訊的JSP:
在testhttps目錄建立檔案seecert.jsp
檔案:$TOMCAT_HOME/webapps/WEB-INF/seecert.jsp
<%@ page import="java.security.cert.X509Certificate" contentType="text/html; charset=GB2312" %>
<pre>
<%
java.security.cert.X509Certificate[] certs=null;
try{
certs=(X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");
if (certs == null) {
out.println("No certificates");
} else if (certs.length == 0) {
out.println("Certificates length is 0");
} else {
java.security.cert.X509Certificate cert = certs[0];
String dn = cert.getSubjectX500Principal().toString();
out.println("SubjectDN: " + dn);
out.println();
out.println("------------------certification detail--------------------");
out.println(cert);
out.println("----------------------------------------------------------");
}
} catch(Exception e){
out.println("Exception=" + e.getMessage());
}
%>
</pre>
3. 測試HTTPS並檢視客戶端證書資訊:
訪問URL:https://<tomcat>:8443/testhttps/seecert.jsp
在用瀏覽器訪問該URL時,瀏覽器可能會跳出視窗讓你選擇用哪個客戶端個人證書。
頁面開啟後,你將看到客戶端證書的資訊。
六. 使用wireshark(ethereal的新名稱)檢視HTTPS里加密的HTTP訊息:
用wireshark抓包後,你可以看到HTTPS伺服器與瀏覽器之間的HTTPS協商過程,但是HTTPS成功建立後,後續訊息是加密的,如何檢視加密的HTTP訊息呢?你需要藉助伺服器的金鑰(私鑰)明文。
假設需要從上面提到的openssl生成的伺服器證書tomcat.p12檔案裡匯出金鑰明文,生成金鑰明文檔案方法:
openssl pkcs12 -in tomcat.p12 -out clearserverkey.pem -nodes
然後在wireshark的協議定義裡找到SSL,並在“RSA keys list”裡配置如下內容:
<server_ip>,8443,http,<path_to_clearserverkey.pem>
其中:
<server_ip>——為HTTPS伺服器的IP;
8443——為HTTPS(SSL)的埠;
http——表示SSL里加密的是HTTP協議;
<path_to_clearserverkey.pem>——指上一步生成的clearserverkey.pem檔案的本地路徑。
這樣,你就能看到wireshark裡的SSL協議被解密,且裡面的HTTP訊息也看到了。
七. 客戶端證書在Tomcat裡的CRL檢查