| 一、前言: 關於如何使用Tomcat伺服器實現雙向SSL認證的文章很早就有了, 比較實用的文章可以看看 IBM developerWorks 中國網站 2002年5月 配置Tomcat 4使用雙向SSL( http://www-900.ibm.com/developerWorks/cn/security/se-tcssl/index.shtml )以及 配置Tomcat 5使用雙向SSL (http://thinkbase.net/w/main/Wiki?Tomcat5SSL_ServerAndClient)。 關於原理方面不是本文的重點,故下面只講解實際操作的過程以及每個過程的說明: 二、需要的軟體包: Tomcat 5.0.25 用途:Web Server。 下載: http://jakarta.apache.org/builds/jakarta-tomcat-5.0/release/v5.0.25/bin/ JSSE 1.0,2 用途:用來產生Tocmcat使用的秘鑰對(keystore)。 在 JDK 1.4以上版本 中已經內建了這個工具, 因此, 只需要安裝 JDK1.4.x 即可 下載: http://java.sun.com/products/jsse/ Openssl 0.9.9.6 用途:用來產生CA認證、簽名並產生IE可匯入的PKCS#12格式私密金鑰。 下載: http://www.openssl.org/ 以上工具的安裝過程可以參考內建的協助,本文就不再詳細描述了。 三、認證環境設定: export CA_DN=/C=CN/ST=GuangDong/L=GuangZhou/O=boss ssl/OU=Boss CACenter/CN=Boss CA Root/emailAddress=winsonhrh@gmail.com
export SERVER_DNAME=CN=21cn.com,OU=Boss Server,O=boss ssl,L=GuangZhou,ST=GuangDong,C=CN export SERVER_KEYPASS=openssl export SERVER_STOREPASS=openssl export SERVER_ALIAS=boss-alias export SERVER_KEYSTORE=boss-alias.keystore
export CLIENT_DN_OU=W21cn-boss-client export CLIENT_DN_CN=Huronghua export CLIENT_DN=/C=CN/O=W21cn Boss/OU=$CLIENT_DN_OU/CN=$CLIENT_DN_CN export SSL_JAVA_HOME=/home/uud/software/jdk1.5.0_06 export JDK_KEYSTORE=$SSL_JAVA_HOME/jre/lib/security/cacerts #JSSE 信任的CA根憑證的儲存密碼, 似乎不能改變 export JDK_STOREPASS=changeit export OPENSSL_SRL_FILE=./ca-cert.srl export CA_ROOT_ALIAS=Boss-CA-ROOT 四、產生CA私密金鑰以及自簽名根憑證, 最後得到PKCS12格式的CA根憑證:
說明: 該命令產生CA私密金鑰以及自簽名根憑證, 最後得到PKCS12格式的CA根憑證(PKCS12格式的CA根憑證受密碼保護, 因此有比較好的安全性); 產生的PKCS12格式的CA根憑證儲存在 dist/ca-cert 目錄下, 在正式使用的系統中, 這個認證檔案(*.pfx)需要妥善儲存, 因為以後的伺服器憑證和用戶端認證都需要依賴這個認證, 尤其是用戶端認證, 如果丟失了CA根憑證, 就無法發布新的用戶端認證了, 而重建CA根憑證, 則需要重建伺服器憑證和用戶端認證, 在用戶端使用者較多的情況下, 重新發布所有的用戶端認證是有相當大的工作量; 在執行這個命令的過程中, 會提示輸入認證保護密碼, 請注意不要遺忘或者泄漏這個密碼, 否則根憑證的安全會受到威脅.
| mkdir ca mkdir dist cd dist mkdir ca-cert cd .. # genrsa [產生密鑰命令] -out[密鑰檔案輸出路徑] 1024 [密鑰位元] openssl genrsa -out ca/ca-key.pem 1024
# req[產生認證命令] -new[新產生] -out[認證檔案輸出路徑] -key[私密金鑰檔案路徑]
openssl req -new -out ca/ca-req.csr -key ca/ca-key.pem -subj $CA_DN # x509[簽發x509認證命令] -req[輸入待簽發認證] -in[輸入待簽發認證檔案路徑] -out[產生x509認證檔案輸出路徑] # -signkey[自簽發密鑰檔案路徑] -days[認證有效期間] openssl x509 -req -in ca/ca-req.csr -out ca/ca-cert.pem -signkey ca/ca-key.pem -days 365 # 產生CA認證: ca/ca-cert.pfx, 注意一定要記住匯出密碼:預設為 ssl # pkcs12[產生PKCS12格式認證命令] -export[匯出檔案] -clerts[僅匯出client認證] -password[匯出密碼] # -in[輸入的client認證檔案路徑] -inkey[client認證密鑰檔案路徑] -out[匯出PKS12格式檔案路徑] openssl pkcs12 -export -clcerts -in ca/ca-cert.pem -inkey ca/ca-key.pem -out ca/ca-cert.pfx # 將產生的認證儲存起來(以後需要用到) cp ca/ca-cert.pfx dist/ca-cert/ca-cert-200606.pfx |
五、從PKCS12格式的CA根憑證匯出 CA私密金鑰和未加密CA根憑證
說明: 該命令用於從PKCS12格式的CA根憑證中匯出CA私密金鑰和未加密CA根憑證; 由於安全原因, CA根憑證平時以密碼保護的PKCS12格式檔案(*.pfx)存放, 那麼如果需要使用CA根憑證來發行伺服器認證或者用戶端認證時, 首先需要執行這個命令得到CA根憑證的未加密形式; 在執行這個命令的過程中, 會出現兩次提示輸入根憑證的保護密碼, 如果密碼不正確, 這個命令將不能執行成功, 也就無法進行下面兩步的發布認證的操作了.
| mkdir decrypt_ca # 從PKCS12格式的CA認證匯出不加密的CA認證: ca/ca-cert.pem openssl pkcs12 -in dist/ca-cert/ca-cert-20060621.pfx -clcerts -nodes -nokeys -out decrypt_ca/ca-cert.pem # 將得到的認證檔案的開頭四行(Bag Attributes)去掉 # 因為step2-server.bat中keytool import時會認為含有Bag Attributes的檔案"不是一個 X.509 認證" vi ca-cert.pem 先按 4,再按 dd 最後按 x! 退出檔案編輯 # 從PKCS12格式的CA認證匯出CA私密金鑰: ca/ca-key.pem openssl pkcs12 -in dist/ca-cert/ca-cert-20060621.pfx -clcerts -nodes -out decrypt_ca/file.pem openssl rsa -in decrypt_ca/file.pem -out decrypt_ca/ca-key.pem |
六、產生伺服器端認證
說明: 該命令用於產生伺服器端認證(keystore檔案); 注意: CA根憑證同時也會被匯入到認證的keystore檔案中, 這樣在將這個認證應用到Tomcat Web伺服器上的時候就不需要將CA根憑證 匯入到JSSE的預設位置了; 這個命令必須在執行 (五、從PKCS12格式的CA根憑證匯出 CA私密金鑰和未加密CA根憑證) 之後才能正常運行.
| 產生KeyPair mkdir server # -genkey[產生金鑰組] -alias[金鑰組別名] -validity[密鑰有效期間] -keyalg[密鑰演算法參數] -keysize[密鑰位元] # -keypass[密鑰保護密碼]- storepass[儲存密碼] # -dname[別名相關附加資訊,其中cn是伺服器的名字一定要與WEB伺服器中設定的一樣] -keystore[金鑰儲存區檔案路徑]
keytool -genkey -alias $SERVER_ALIAS -validity 720 -keyalg RSA -keysize 1024 -keypass $SERVER_KEYPASS -storepass $SERVER_STOREPASS -dname $SERVER_DNAME -keystore server/$SERVER_KEYSTORE 產生待簽署憑證 server/server.csr # -certreq[產生待簽署憑證] -alias[認證別名] -sigalg[認證演算法參數] -file [產生檔案輸出路徑] # -keypass[密鑰保護密碼] -keystore[隱藏檔路徑] -storepass[儲存密碼]
keytool -certreq -alias $SERVER_ALIAS -sigalg MD5withRSA -file server/server.csr -keypass $SERVER_KEYPASS -keystore server/$SERVER_KEYSTORE -storepass $SERVER_STOREPASS 用CA私密金鑰進行簽名, 產生x509認證檔案
# x509[簽發x509認證命令] -req[輸入待簽發認證] -in[輸入待簽發認證檔案路徑] -out[產生x509認證檔案輸出路徑] # -CA[簽髮根認證] -CAkey[根憑證密鑰檔案] -days[認證有效期間] -CAserial[CA序號檔案] openssl x509 -req -in server/server.csr -out server/server-cert.pem -CA decrypt_ca/ca-cert.pem -CAkey decrypt_ca/ca-key.pem -days 365 -CAserial $OPENSSL_SRL_FILE -sha1 -trustout 匯入(替換)信任的CA根憑證到JSSE的預設位置(%JDK_KEYSTORE%)
# 匯入前首先刪除(如果原來已經匯入過) # JSSE 信任的CA根憑證的金鑰儲存區檔案路徑 keytool -delete -v -alias $CA_ROOT_ALIAS -storepass $JDK_STOREPASS -keystore $JDK_KEYSTORE
# -import[匯入命令] -v trustcacerts[匯入信任認證] -storepass[儲存密碼] -alias[CA根憑證的別名] # -file[認證檔案路徑] -keystore[匯入檔案路徑] -noprompt[不提示"信任這個認證。"] keytool -import -v -trustcacerts -storepass $JDK_STOREPASS -alias $CA_ROOT_ALIAS -file decrypt_ca/ca-cert.pem -keystore $JDK_KEYSTORE 把CA簽名後的server端認證匯入keystore: server/%SERVER_KEYSTORE% # -import[匯入命令] -v trustcacerts[匯入信任認證] -storepass[儲存密碼] -keypass[密鑰保護密碼] # -alias[伺服器憑證的別名] -file[認證檔案路徑] -keystore[匯入檔案路徑]
keytool -import -v -trustcacerts -storepass $SERVER_STOREPASS -keypass $SERVER_KEYPASS -alias $SERVER_ALIAS -file server/server-cert.pem -keystore server/$SERVER_KEYSTORE 把CA根憑證匯入keystore: server/%SERVER_KEYSTORE% # -import[匯入命令] -v trustcacerts[匯入信任認證] -storepass[儲存密碼] -keypass[密鑰保護密碼] # -alias[伺服器憑證的別名] -file[認證檔案路徑] -keystore[匯入檔案路徑] -noprompt[不提示"您仍然想要將它添加到自己的keystore 嗎。"] keytool -import -v -trustcacerts -storepass $SERVER_STOREPASS -keypass $SERVER_KEYPASS -alias $CA_ROOT_ALIAS -file decrypt_ca/ca-cert.pem -keystore server/$SERVER_KEYSTORE 查看JSSE CA根憑證 keytool -list -storepass $JDK_STOREPASS -keystore $JDK_KEYSTORE -alias $CA_ROOT_ALIAS -v
刪除匯入到JSSE的預設位置的CA根憑證(使JDK恢複原狀) keytool -delete -v -alias $CA_ROOT_ALIAS -storepass $JDK_STOREPASS -keystore $JDK_KEYSTORE 將產生的認證儲存起來(供伺服器使用) cd dist mkdir server cd .. cp server/$SERVER_KEYSTORE dist/server/$SERVER_KEYSTORE
查看server端認證 keytool -list -storepass $SERVER_STOREPASS -keystore dist/server/$SERVER_KEYSTORE -v |
七、發布用戶端認證:
說明: 這個命令用於發布用戶端認證; 為了便於批量產生用戶端認證, 這個命令支援命令列參數, 第1到3個參數依次為: 用戶端認證的名稱(Common Name) 用戶端認證所屬的組織(Organizational Unit Name) 產生的PKS12格式用戶端認證的匯入密碼, 這個密碼可以保護認證只能被知道密碼的使用者匯入到瀏覽器 這個命令必須在執行 (五、從PKCS12格式的CA根憑證匯出 CA私密金鑰和未加密CA根憑證) 之後才能正常運行.
| mkdir client cd dist mkdir client cd .. # 產生client私密金鑰: client/client-key.pem # genrsa [產生密鑰命令] -out[密鑰檔案輸出路徑] 1024 [密鑰位元] openssl genrsa -out client/client-key.pem 1024 產生待簽署憑證: client/client-req.csr
# req[產生認證命令] -new[新產生] -out[認證檔案輸出路徑] -key[私密金鑰檔案路徑] # -subj[request's subject, 這裡放置Distinguished Name(DN)資訊] openssl req -new -out client/client-req.csr -key client/client-key.pem -subj $CLIENT_DN 用CA私密金鑰進行簽名, 產生x509認證檔案: client/client.crt # x509[簽發x509認證命令] -req[輸入待簽發認證] -in[輸入待簽發認證檔案路徑] -out[產生x509認證檔案輸出路徑] # -signkey [密鑰檔案路徑] # -CA[簽髮根認證] -CAkey[根憑證密鑰檔案] -days[認證有效期間] -CAserial[CA序號檔案]
openssl x509 -req -in client/client-req.csr -out client/client.crt -signkey client/client-key.pem -CA decrypt_ca/ca-cert.pem -CAkey decrypt_ca/ca-key.pem -days 365 -CAserial $OPENSSL_SRL_FILE 產生client端的個人認證: client/$_CLIENT_P12_FILE # pkcs12[產生PKS12格式認證命令] -export[匯出檔案] -clerts[僅匯出client認證] -password[匯出密碼] # -in[輸入的client認證檔案路徑] -inkey[client認證密鑰檔案路徑] -out[匯出PKS12格式檔案路徑] # -name[好記的名字] 密碼是 clientssl openssl pkcs12 -export -clcerts -in client/client.crt -inkey client/client-key.pem -out client/$CLIENT_DN_OU-$CLIENT_DN_CN.p12 -name $CLIENT_DN_OU-$CLIENT_DN_CN 將產生的認證儲存起來(發布給使用者) cp client/$CLIENT_DN_OU-$CLIENT_DN_CN.p12 dist/client/$CLIENT_DN_OU-$CLIENT_DN_CN.p12 |
八、Tomcat 5 伺服器配置:
參考配置方法如下: 將 "六、產生伺服器端認證" 命令產生的 dist/server 目錄下的 keystore 檔案(如果使用預設配置, 這個檔案叫做"boss-alias.keystore")複製到 Tomcat 安裝目錄的 conf 目錄下; 修改 Tomcat 安裝目錄的 conf 目錄下的 "server.xml" 檔案, 修改 <Service name="Catalina"> 包含的 "Connector" 元素, 樣本如下(僅供參考):
| <Connector port="8443" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" URIEncoding="UTF-8" keystoreFile="%Tomcat%/conf/boss-alias.keystore" keystorePass="openssl" truststoreFile="%Tomcat%/conf/boss-alias.keystore" truststorePass="openssl"/> |
九、啟用雙向 SSL 時 Web 應用程式的配置 :
要啟用雙向 SSL 認證, 在 Web 應用程式的 web.xml 中需要如下增加一些配置: auth-method=CLIENT-CERT 說明是"以用戶端數位憑證來確認使用者的身份", transport-guarantee=CONFIDENTIAL 表示應用程式要求資料必須在一種"防止其他實體看到傳輸的內容的方式中傳送"..
| <login-config> <!-- Authorization setting for SSL --> <auth-method>CLIENT-CERT</auth-method> <realm-name>Client Cert Users-only Area</realm-name> </login-config> <security-constraint> <!-- Authorization setting for SSL --> <web-resource-collection > <web-resource-name >SSL</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> |
十、編寫 JSP 頁面顯示 認證的內容:
經過以上的配置之後, 那麼即使使用者是通過 http 訪問 Web 應用程式的, 瀏覽器也會自動切換到 https 方式並彈出選擇用戶端認證的對話方塊. 如何使用用戶端認證進行使用者驗證
在 web 應用程式中, 可以通過 java 代碼從 request 對象中獲得, 根據 Servlet Specifications, 使用request.getAttribute("javax.servlet.request.X509Certificate") 就可以得到 https 請求的用戶端憑證鏈結資訊, 其中第一個元素就是用戶端認證, 相應的範例程式碼如下:
| <%@ page import="java.security.cert.X509Certificate"%> <% //response.sendRedirect("jsp/vpay/input.jsp"); String certSubject = null; java.security.cert.X509Certificate[] certChain= (java.security.cert.X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); if (null!=certChain){ int len=certChain.length; if (len>0){ java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate)certChain[0]; java.security.Principal pSubject = cert.getSubjectDN(); certSubject = pSubject.getName(); } } %> Subject = <%=certSubject%> |
十一、匯入用戶端認證並訪問 HTTPS 應用:
匯入用戶端認證到瀏覽器中(雙擊用戶端認證檔案 "W21cn-boss-client-Huaronghu.p12" 即可匯入 IE)。 匯入完畢後, 啟動 Tomcat, 可以看到 http://localhost:8080/index.jsp 內容的訪問則會自動切換到 https://localhost:8443 上去了; 同時可以看到, 使用 HTTPS 方式訪問時, 用戶端認證的 Subject 可以被 jsp 頁面獲得。 試著在瀏覽器裡面把匯入的認證刪除, 你會發現 網站的內容已經不能訪問了: 十二、總結:
當Web伺服器開始正式運行以後, "四、產生CA私密金鑰以及自簽名根憑證, 最後得到PKCS12格式的CA根憑證" 命令是不能再次執行的, 如果需要重新發行者認證, 或者發布新的用戶端認證, 在執行 “六、產生伺服器端認證” 和 “七、產生客戶器端認證” 命令前, 可以通過 “五、從PKCS12格式的CA根憑證匯出 CA私密金鑰和未加密CA根憑證”重新從儲存的PKCS12格式CA根憑證中匯出CA私密金鑰和未加密CA根憑證. |