tomcat双向认证

数字证书相关的标准和协议

参考:https://en.wikipedia.org/wiki/PKCS

关键词 描述
PEM PKIX, PKCS, and CMS 结构的文本编码格式。PEM最早是基于“隐私增强邮件”的1993年IETF标准集的用于存储和发送加密密钥,证书和其他数据的事实上的文件格式。 原标准未被采用,但其定义的文本编码非常受欢迎,最终由IETF在RFC 7468中形式化。参考:https:tools.ietf.org/html/rfc7468
DER 可区分编码规则 (Distinguished Encoding Rules) 。可包含所有私钥、公钥和证书。它是大多数浏览器的缺省格式,并按 ASN1 DER 格式存储。它是无报头的(PEM 是用文本报头包围的 DER)。一般使用".cer"做文件后缀
JKS 通常可以将Apache/OpenSSL使用的“KEY文件 + CRT文件”格式”转换为标准的Java Key Store(JKS)文件。一般以".keystore"作为文件后缀。
CSR 证书签名请求(Certificate Signing Request)。发送给CA签名的请求内容标准,一般存储在一个".csr"后缀的文件中。
CRT 证书文件一般以".crt"作为文件后缀。
KEY PEM格式的私钥文件一般以".key"作为文件后缀。
CRL 证书吊销列表 (Certification Revocation List) 。
PKCS7 加密消息语法标准。(参考:https:www.ietf.org/rfc/rfc2315.txt)
PKCS12 个人信息交换语法标准。定义了一个通用的文件格式标准,用于存储附带公钥证书的私钥,并使用基于密码的对称密钥加密。前身是PFX。一般以".p12"作为文件后缀。
X.509 公钥证书标准

生成证书

使用java自带的keytool工具生成证书。如果文章中有哪些参数不明白的可以参考keytool官方文档

前期规划

涉及的名称比较多,先规划一下,后面会作说明(以下数据纯属测试,如有雷同,请知会)。

组织机构(公司名称):XTBaBa 组织机构部门:XTBaBa CA 根证书名称:XTBaBa Root CA 域名:a.xtbaba.com | b.xtbaba.com

服务端证书密钥库:tomcat.keystore 服务端信任证书列表密钥库:tomcat_trust.keystore 根证书密钥库:root.keystore 客户证书:xiaotian.p12

根证书在密钥库中的别名:root 所有密钥库&密钥的密码:123456

知识扫盲

  • keytool 是java自带的提供生成证书、数字签名等功能的小工具
  • keytool 生成的密钥库不能导出非对称算法的私钥
  • keytool 导入公钥证书时,如果别名(alias)相同,则认为导入的是CA签名后的证书
  • https 单向认证指客户端验证服务端身份,我们平常见的https站点,都属于单向认证
  • 双向认证时,浏览器会让用户选择一个证书传递给服务器,由服务器核验客户的证书

更多知识,请参考keytool官方文档

生成根证书

1
2
3
4
# 生成根证书密钥对并保存到tomcat.keystore密钥库中
keytool -genkeypair -keystore root.keystore -storepass 123456 -keyalg RSA -keypass 123456 -alias root -dname "CN=XTBaBa Root CA, OU=XTBaBa CA, O=XTBaBa, L=Hangzhou, S=Zhejiang, C=CN"
# 导出根证书
keytool -exportcert -keystore root.keystore -storepass 123456 -file root.crt -alias root -v

客户需要将root.crt导入到操作系统的受信任的根证书颁发机构证书分类中,否则 https 单向认证不通过

生成服务端证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 生成服务端证书密钥对
keytool -genkeypair -keystore tomcat.keystore -storepass 123456 -keyalg RSA -keypass 123456 -alias xtbaba_site -dname "CN=XTBaBa Customer Site, OU=XTBaBa RD, O=XTBaBa, L=Hangzhou, S=Zhejiang, C=CN"

# 生成服务端证书签名请求
keytool -certreq -keystore tomcat.keystore -storepass 123456 -alias xtbaba_site -file xtbaba_site.csr

# 使用根证书对服务端证书签名,签名时指定了两个域名
keytool -gencert -keystore tomcat.keystore -storepass 123456 -alias root -infile xtbaba_site.csr -outfile xtbaba_site.cer -ext "san=dns:a.xtbaba.com,dns:b.xtbaba.com" -ext eku="serverAuth,clientAuth"

# 将根证书临时导入到tomcat.keystore密钥库中
keytool -importkeystore -srckeystore root.keystore -srcstorepass 123456 -srcalias root -destkeystore tomcat.keystore -deststorepass 123456 -destalias root

# 将签名后的服务端证书导入到服务端密钥库
keytool -import -keystore tomcat.keystore -storepass 123456 -file xtbaba_site.cer -alias xtbaba_site

# 移除根证书,切记移除掉啊
keytool -delete -keystore tomcat.keystore -storepass 123456 -alias root

生成客户证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 生成客户证书密钥对,客户证书需要使用"PKCS12"格式
keytool -genkeypair -keystore xiaotian.p12 -storepass 123456 -storetype PKCS12 -keyalg RSA -keypass 123456 -alias xiaotian -dname "CN=Xiaotian, OU=XTBaBa User, O=XTBaBa, L=Hangzhou, S=Zhejiang, C=CN"

# 生成客户证书签名请求
keytool -certreq -keystore xiaotian.p12 -storepass 123456 -alias xiaotian -file xiaotian.csr

# 使用根证书对客户证书签名
keytool -gencert -keystore root.keystore -storepass 123456 -alias root -infile xiaotian.csr -outfile xiaotian.cer -ext eku="serverAuth,clientAuth"

# 导入签名后的客户证书到密钥库

# 1. 首先将根证书导入到客户证书密钥库中
keytool -importkeystore -srckeystore root.keystore -srcstorepass 123456 -srcalias root -destkeystore xiaotian.p12 -deststorepass 123456 -destalias root

# 2. 将签名后的客户证书导入到客户证书密钥库中。导入签名后的证书时,keytool要从密钥库中找到一个证书来验证该签名,所以必须将根证书临时导入到客户证书密钥库中。
keytool -import -keystore xiaotian.p12 -storepass 123456 -file xiaotian.cer -alias xiaotian

# 3. 从客户证书密钥库中移除根证书。千万别把根证书发给用户了
keytool -delete -keystore xiaotian.p12 -storepass 123456 -alias root

  1. 导入签名后的证书时,keytool要从密钥库中找到一个证书来验证该签名,所以必须将根证书临时导入到客户证书密钥库中
  2. 生成完毕后,将 xiaotian.p12 文件发送给客户,客户将该证书导入操作系统的证书库即可(一般导入在个人证书分类里)。
  3. 给客户生成完证书后,一定要从 xiaotian.p12 中删除根证书,千万别把根证书发给用户了

将客户证书添加到服务端的信任证书密钥库

1
2
3
4
5
# 导入根证书
keytool -importkeystore -srckeystore root.keystore -srcstorepass 123456 -srcalias root -destkeystore tomcat_trust.keystore -deststorepass 123456 -destalias root

# 导入客户证书
keytool -import -keystore tomcat_trust.keystore -storepass 123456 -file xiaotian.cer -alias xiaotian

必须将根证书导入到tomcat_trust.keystore密钥库中,tomcat将用此根证书来验证浏览器提交的根证书

keytool 其它常用命令

1
2
3
4
5
# 查看密钥库中的所有密钥
keytool -list -keystore tomcat.keystore -storepass 123456 -v

# 查看证书内容
keytool -printcert -file xiaotian.cer

配置tomcat

修改server.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!- 设置端口为80redirectPort 重定向到https端口 -->
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="443" />

<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="100"
scheme="https"
secure="true"
SSLEnabled="true"
keyAlias="xtbaba_site"
keystoreFile="D:/www/keys/tomcat.keystore"
keystorePass="123456"
truststoreFile="D:/www/keys/tomcat_trust.keystore"
truststorePass="123456"
clientAuth="true"
sslProtocol="TLS"
/>

xml文件配置说明:

  • protocol 必须配置为org.apache.coyote.http11.Http11NioProtocol
  • keyAlias 服务器证书别名
  • keystoreFile 服务器证书密钥库
  • keystorePass 服务器证书密钥库密码
  • truststoreFile 服务器信任证书列表密钥库
  • truststorePass 服务器信任证书列表密钥库密码
  • clientAuth 客户端认证:
    • true:客户端必须提供证书;
    • false:客户端不必提供证书;
    • want:客户端提不提供证书都可以
  • sslProtocol SSL协议

读取客户证书内容

maven依赖配置:

1
2
3
4
5
6
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-coyote</artifactId>
<version>7.0.8</version>
<scope>provided</scope>
</dependency>

通过HttpServletRequest提取证书内容时需要依赖tomcat-coyotejar包,该jar包由tomcat提供,必须指定scopeprovided。否则运行时会报错(报错内容为找不到SSLSupport)。java代码如下:

1
2
3
4
5
6
7
8
9
10
11
X509Certificate[] certificates = (X509Certificate[]) request.getAttribute(SSLSupport.CERTIFICATE_KEY);
if(certificates != null && certificates.length > 0){
X509Certificate certificate = certificates[0];
logger.debug("使用者唯一名称 = {}", certificate.getSubjectDN());
logger.debug("颁布者唯一名称 = {}", certificate.getIssuerDN());
logger.debug("证书序列号 = {}", certificate.getSerialNumber());
logger.debug("证书类型 = {}", certificate.getType());
logger.debug("证书有效期 : {}", DateFormatUtils.format(certificate.getNotBefore(), "yyyy-MM-dd HH:mm:ss.SSS"),
DateFormatUtils.format(certificate.getNotAfter(), "yyyy-MM-dd HH:mm:ss.SSS"));
logger.debug("证书签名 = {}", Hex.encodeHexString(certificate.getSignature()));
}

运行结果:

1
2
3
4
5
6
2017-07-04 18:14:29.480 HomeController - 使用者唯一名称 = CN=Xiaotian, OU=XTBaBa User, O=XTBaBa, L=Hangzhou, ST=Zhejiang, C=CN
2017-07-04 18:14:29.480 HomeController - 颁布者唯一名称 = CN=XTBaBa Root CA, OU=XTBaBa CA, O=XTBaBa, L=Hangzhou, ST=Zhejiang, C=CN
2017-07-04 18:14:29.480 HomeController - 证书序列号 = 1327706204
2017-07-04 18:14:29.480 HomeController - 证书类型 = X.509
2017-07-04 18:14:29.480 HomeController - 证书有效期 = 2017-07-04 15:46:18.000
2017-07-04 18:14:29.920 HomeController - 证书签名 = 9c2fc47626621378f0f47d62e2b337265d3042495a259b5230ea2336ec990e9e2073fa817f2bb3d72a64565601b5f0171c77685d3ca616df4fcc46b8d6c4aed0101c21e0eaa22fd747b311eea5a781f721d47d621b0543bc3de442830e1c34a272b7960a97766df23f7955c6bdbf13013cf76b9e4233270ad78514d3afc59d442e4298b4fb2c19ab656a38d41ace261f24f4ba63e14c9d368aa7393b808b91ca19115c6578903bfeb3feb3173d68ff

tomcat keytool 数字证书 双向认证