一、前言:
關(guān)于如何使用Tomcat服務(wù)器實現(xiàn)雙向SSL認證的文章很早就有了, 比較實用的文章可以看看 IBM developerWorks 中國網(wǎng)站 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
)。
??????關(guān)于原理方面不是本文的重點,故下面只講解實際操作的過程以及每個過程的說明:
二、需要的軟件包:
???????Tomcat 5.0.25
???????用途:Web Server。
??????下載: http://jakarta.apache.org/builds/jakarta-tomcat-5.0/release/v5.0.25/bin/
???????JSSE 1.0,2
???????用途:用來產(chǎn)生Tocmcat使用的秘鑰對(keystore)。 在 JDK 1.4以上版本 中已經(jīng)自帶了這個工具, 因此, 只需要安裝 JDK1.4.x 即可
???????下載: http://java.sun.com/products/jsse/
???????Openssl 0.9.9.6
???????用途:用來產(chǎn)生CA證書、簽名并生成IE可導(dǎo)入的PKCS#12格式私鑰。
???????下載: http://www.openssl.org/
???????以上工具的安裝過程可以參考自帶的幫助,本文就不再詳細描述了。
三、證書環(huán)境設(shè)置:
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 目錄下, 在正式使用的系統(tǒng)中, 這個證書文件(*.pfx)需要妥善保存,
因為以后的服務(wù)器證書和客戶端證書都需要依賴這個證書, 尤其是客戶端證書, 如果丟失了CA根證書,
就無法發(fā)布新的客戶端證書了, 而重新生成CA根證書, 則需要重新生成服務(wù)器證書和客戶端證書,
在客戶端用戶較多的情況下, 重新發(fā)布所有的客戶端證書是有相當大的工作量;
在執(zhí)行這個命令的過程中, 會提示輸入證書保護密碼, 請注意不要遺忘或者泄漏這個密碼, 否則根證書的安全會受到威脅.
mkdir ca
mkdir dist
cd dist
mkdir ca-cert
cd ..
# genrsa [產(chǎn)生密鑰命令] -out[密鑰文件輸出路徑] 1024 [密鑰位數(shù)]
openssl genrsa -out ca/ca-key.pem 1024
# req[產(chǎn)生證書命令] -new[新生成] -out[證書文件輸出路徑] -key[私鑰文件路徑]
openssl req -new -out ca/ca-req.csr -key ca/ca-key.pem -subj $CA_DN
# x509[簽發(fā)x509證書命令] -req[輸入待簽發(fā)證書] -in[輸入待簽發(fā)證書文件路徑] -out[產(chǎn)生x509證書文件輸出路徑]
# -signkey[自簽發(fā)密鑰文件路徑] -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, 注意一定要記住導(dǎo)出密碼:
默認為 ssl
# pkcs12[生成PKCS12格式證書命令] -export[導(dǎo)出文件] -clerts[僅導(dǎo)出client證書] -password[導(dǎo)出密碼]
# -in[輸入的client證書文件路徑] -inkey[client證書密鑰文件路徑] -out[導(dǎo)出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根證書導(dǎo)出 CA私鑰和未加密CA根證書
說明:
該命令用于從PKCS12格式的CA根證書中導(dǎo)出CA私鑰和未加密CA根證書;
由于安全原因, CA根證書平時以密碼保護的PKCS12格式文件(*.pfx)存放,
那么如果需要使用CA根證書來發(fā)布服務(wù)器證書或者客戶端證書時, 首先需要執(zhí)行這個命令得到CA根證書的未加密形式;
在執(zhí)行這個命令的過程中, 會出現(xiàn)兩次提示輸入根證書的保護密碼, 如果密碼不正確, 這個命令將不能執(zhí)行成功,
也就無法進行下面兩步的發(fā)布證書的操作了.
mkdir decrypt_ca
# 從PKCS12格式的CA證書導(dǎo)出不加密的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證書導(dǎo)出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
|
???
六、生成服務(wù)器端證書
說明:
該命令用于生成服務(wù)器端證書(keystore文件);
注意: CA根證書同時也會被導(dǎo)入到證書的keystore文件中, 這樣在將這個證書應(yīng)用到Tomcat Web服務(wù)器上的時候就不需要將CA根證書
導(dǎo)入到JSSE的默認位置了;
這個命令必須在執(zhí)行 (
五、從PKCS12格式的CA根證書導(dǎo)出 CA私鑰和未加密CA根證書
) 之后才能正常運行.
生成KeyPair
mkdir server
# -genkey[產(chǎn)生密鑰對] -alias[密鑰對別名] -validity[密鑰有效期] -keyalg[密鑰算法參數(shù)] -keysize[密鑰位數(shù)]
# -keypass[密鑰保護密碼]- storepass[存儲密碼]
# -dname[別名相關(guān)附加信息,其中cn是服務(wù)器的名字一定要與WEB服務(wù)器中設(shè)置的一樣] -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[產(chǎn)生待簽名證書] -alias[證書別名] -sigalg[證書算法參數(shù)] -file [產(chǎn)生文件輸出路徑]
# -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私鑰進行簽名, 產(chǎn)生x509證書文件
# x509[簽發(fā)x509證書命令] -req[輸入待簽發(fā)證書] -in[輸入待簽發(fā)證書文件路徑] -out[產(chǎn)生x509證書文件輸出路徑]
# -CA[簽發(fā)根證書] -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
導(dǎo)入(替換)信任的CA根證書到JSSE的默認位置(%JDK_KEYSTORE%)
# 導(dǎo)入前首先刪除(如果原來已經(jīng)導(dǎo)入過)
# JSSE 信任的CA根證書的密鑰存儲文件路徑
keytool -delete -v -alias $CA_ROOT_ALIAS -storepass $JDK_STOREPASS -keystore $JDK_KEYSTORE
# -import[導(dǎo)入命令] -v trustcacerts[導(dǎo)入信任證書] -storepass[存儲密碼] -alias[CA根證書的別名]
# -file[證書文件路徑] -keystore[導(dǎo)入文件路徑] -noprompt[不提示"信任這個認證?"]
keytool -import -v -trustcacerts -storepass $JDK_STOREPASS -alias $CA_ROOT_ALIAS -file decrypt_ca/ca-cert.pem -keystore $JDK_KEYSTORE
把CA簽名后的server端證書導(dǎo)入keystore: server/%SERVER_KEYSTORE%
# -import[導(dǎo)入命令] -v trustcacerts[導(dǎo)入信任證書] -storepass[存儲密碼] -keypass[密鑰保護密碼]
# -alias[服務(wù)器證書的別名] -file[證書文件路徑] -keystore[導(dǎo)入文件路徑]
keytool -import -v -trustcacerts -storepass $SERVER_STOREPASS -keypass $SERVER_KEYPASS -alias $SERVER_ALIAS -file server/server-cert.pem -keystore server/$SERVER_KEYSTORE
把CA根證書導(dǎo)入keystore: server/%SERVER_KEYSTORE%
# -import[導(dǎo)入命令] -v trustcacerts[導(dǎo)入信任證書] -storepass[存儲密碼] -keypass[密鑰保護密碼]
# -alias[服務(wù)器證書的別名] -file[證書文件路徑] -keystore[導(dǎo)入文件路徑] -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
刪除導(dǎo)入到JSSE的默認位置的CA根證書(使JDK恢復(fù)原狀)
keytool -delete -v -alias $CA_ROOT_ALIAS -storepass $JDK_STOREPASS -keystore $JDK_KEYSTORE
將生成的證書保存起來(供服務(wù)器使用)
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
|
?
七、發(fā)布客戶端證書:
說明:
這個命令用于發(fā)布客戶端證書;
為了便于批量生成客戶端證書, 這個命令支持命令行參數(shù), 第1到3個參數(shù)依次為:
客戶端證書的名稱(Common Name)
客戶端證書所屬的組織(Organizational Unit Name)
產(chǎn)生的PKS12格式客戶端證書的導(dǎo)入密碼, 這個密碼可以保護證書只能被知道密碼的用戶導(dǎo)入到瀏覽器
這個命令必須在執(zhí)行 (
五、從PKCS12格式的CA根證書導(dǎo)出 CA私鑰和未加密CA根證書
) 之后才能正常運行.
mkdir client
cd dist
mkdir client
cd ..
# 生成client私鑰: client/client-key.pem
# genrsa [產(chǎn)生密鑰命令] -out[密鑰文件輸出路徑] 1024 [密鑰位數(shù)]
openssl genrsa -out client/client-key.pem 1024
生成待簽名證書: client/client-req.csr
# req[產(chǎn)生證書命令] -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私鑰進行簽名, 產(chǎn)生x509證書文件: client/client.crt
# x509[簽發(fā)x509證書命令] -req[輸入待簽發(fā)證書] -in[輸入待簽發(fā)證書文件路徑] -out[產(chǎn)生x509證書文件輸出路徑]
# -signkey [密鑰文件路徑]
# -CA[簽發(fā)根證書] -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[導(dǎo)出文件] -clerts[僅導(dǎo)出client證書] -password[導(dǎo)出密碼]
# -in[輸入的client證書文件路徑] -inkey[client證書密鑰文件路徑] -out[導(dǎo)出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
將生成的證書保存起來(發(fā)布給用戶)
cp client/$CLIENT_DN_OU-$CLIENT_DN_CN.p12 dist/client/$CLIENT_DN_OU-$CLIENT_DN_CN.p12
|
八、Tomcat 5 服務(wù)器配置:
參考配置方法如下:
將 "
六、生成服務(wù)器端證書
" 命令產(chǎn)生的 dist\server 目錄下的 keystore 文件(如果使用默認配置, 這個文件叫做"boss-alias.keystore")復(fù)制到 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 應(yīng)用程序的配置 :
???????要啟用雙向 SSL 認證, 在 Web 應(yīng)用程序的 web.xml 中需要如下增加一些配置: auth-method=CLIENT-CERT 說明是"以客戶端數(shù)字證書來確認用戶的身份", transport-guarantee=CONFIDENTIAL 表示應(yīng)用程序要求數(shù)據(jù)必須在一種"防止其他實體看到傳輸?shù)膬?nèi)容的方式中傳送"..
<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 頁面顯示 證書的內(nèi)容:
??????經(jīng)過以上的配置之后, 那么即使用戶是通過 http 訪問 Web 應(yīng)用程序的, 瀏覽器也會自動切換到 https 方式并彈出選擇客戶端證書的對話框.
如何使用客戶端證書進行用戶驗證
????????在 web 應(yīng)用程序中, 可以通過 java 代碼從 request 對象中獲得, 根據(jù) Servlet Specifications, 使用request.getAttribute("javax.servlet.request.X509Certificate") 就可以得到 https 請求的客戶端證書鏈信息, 其中第一個元素就是客戶端證書, 相應(yīng)的示例代碼如下:
<%@ 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%>
|
?
十一、導(dǎo)入客戶端證書并訪問 HTTPS 應(yīng)用:
????? ?導(dǎo)入客戶端證書到瀏覽器中(雙擊客戶端證書文件 "W21cn-boss-client-Huaronghu.p12" 即可導(dǎo)入 IE)。
????? 導(dǎo)入完畢后, 啟動 Tomcat, 可以看到 http://localhost:8080/index.jsp 內(nèi)容的訪問則會自動切換到 https://localhost:8443 上去了; 同時可以看到, 使用 HTTPS 方式訪問時, 客戶端證書的 Subject 可以被 jsp 頁面獲得。
????????試著在瀏覽器里面把導(dǎo)入的證書刪除, 你會發(fā)現(xiàn) 網(wǎng)站的內(nèi)容已經(jīng)不能訪問了:
十二、總結(jié):
??????當Web服務(wù)器開始正式運行以后, "
四
、生成CA私鑰以及自簽名根證書, 最后得到PKCS12格式的CA根證書
" 命令是不能再次執(zhí)行的,
如果需要重新發(fā)布服務(wù)器證書, 或者發(fā)布新的客戶端證書,
在執(zhí)行 “
六、生成服務(wù)器端證書
” 和 “
七、生成客戶器端證書
” 命令前,
可以通過 “
五、從PKCS12格式的CA根證書導(dǎo)出 CA私鑰和未加密CA根證書
”重新從保存的PKCS12格式CA根證書中導(dǎo)出CA私鑰和未加密CA根證書.
|