一、前言:
关于如何使用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根证书.
|