本文將介紹如何利用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檢查
請參考《Tomcat裡配置CRL》(http://blog.csdn.net/jasonhwang/archive/2008/05/08/2413310.aspx)。