本章是HTTPS那些事儿的第四篇文章,其他相关文章请参见:前言
本篇主要介绍怎么使用jdk中的keytool工具模拟HTTPS证书颁发,通过该工具我们可以模拟CA证书的申请过程,CA证书的申请步骤可以参见本系列的第一篇文章HTTPS基础。
* 注意
本文纯手工打造,转载请注明出处。
首先,要模拟CA证书的颁发过程以及验证CA证书的有效性,需要定义三个主体对象:CA机构,服务器SERVER和客户端Client,后续我们将依次描述三个主体在该过程中所需要做的工作。
1. 模拟CA机构
作为一个CA机构,我们需要有自己的公私钥:ca.pub和ca.pri。有了公钥后,CA需要将公钥封装成证书(包含公钥和CA本身信息)并发布出去,所有信任该CA机构的客户端会将该证书安装到其信任证书列表中。
下面,我们通过A、B两步来生成CA机构的证书。
A). 生成CA公私钥文件
如果使用keytool生成证书,只会先生成一个同时包含公私钥以及CA本身信息的文件:MyCA.jks,但生成后可以导出CA机构的证书:MyCA.crt。此处的MyCA代表我们模拟的CA机构的名字。
keytool -genkeypair -keyalg RSA -keysize 2048 -keystore MyCA.jks -alias MyCA
该命令指定生成一个公私钥对,使用RAS算法,key长度为2048,生成到文件MyCA.jks中,文件中保存的公私钥对的名称为MyCA。使用该命令后会提示输入MyCA本身信息,如下:
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: myca
What is the name of your organizational unit?
[Unknown]: mallen's ca
What is the name of your organization?
[Unknown]: mallen
What is the name of your City or Locality?
[Unknown]: chengdu
What is the name of your State or Province?
[Unknown]: sichuan
What is the two-letter country code for this unit?
[Unknown]: CN
Is CN=myca, OU=mallen's ca, O=mallen, L=chengdu, ST=sichuan, C=CN correct?
[no]: y
Enter key password for <MyCA>
(RETURN if same as keystore password):
首先要求输入keystore文件,也就是MyCA.jks文件的访问密码。然后,输入first and last name。再之后输入单位、部门、城市、省份、国家编码,然后要求确认是否正确,最后再输入MyCA私钥的加密密钥,直接回车,使用keystore文件密钥即可。
密钥对文件生成后,可以通过如下命令查看文件内容:
keytool -list -v -keystore MyCA.jks
输入密码后,查看到的内容如下:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: myca
Creation date: Sep 6, 2018
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=myca, OU=mallen's ca, O=mallen, L=chengdu, ST=sichuan, C=CN
Issuer: CN=myca, OU=mallen's ca, O=mallen, L=chengdu, ST=sichuan, C=CN
Serial number: 4385a752
Valid from: Thu Sep 06 14:59:31 CST 2018 until: Wed Dec 05 14:59:31 CST 2018
Certificate fingerprints:
MD5: 6E:2F:7E:C0:FA:78:53:95:FD:1C:4D:5C:53:BF:92:30
SHA1: CD:4E:FF:AA:91:7E:68:AD:24:39:2E:9F:1E:70:85:54:03:63:F6:A2
SHA256: E8:85:FA:33:85:B7:E9:FE:96:BD:5E:80:CF:00:F6:B5:4A:EA:2D:F7:47:B7:81:3B:60:DF:79:1D:5E:9E:EA:A0
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 61 30 66 A2 7F B6 F1 B5 EA A1 D6 31 1B A5 DC 60 a0f........1...`
0010: 25 9E 04 28 %..(
]
]
*******************************************
*******************************************
B) 提取CA的公钥文件
拥有了CA公私钥文件之后,可以提取出CA的证书文件,该文件用户客户端校验服务器端证书时的密钥。后续会介绍怎么使用,提取命令如下:
keytool -exportcert -keystore MyCA.jks -alias MyCA -file MyCA.crt
该命令指定从MyCA.jks密钥对文件中获取名称为MyCA的密钥对的公钥,并将公钥写入到MyCA.crt中。
2. 模拟服务器SERVER
同样,每个Server也会有自己的公私钥:server.pub和server.pri。也需要首先生成公私钥对:
keytool -genkeypair -alias myserver -keyalg RSA -keysize 2048 -keystore MyServer.jks
该命令指定生成一个公私钥对,使用RAS算法,key长度为2048,生成到文件MyServer.jks中,公私钥对的名称为myserver。
后续的输入与生成MyCA.jks一致,但是需要注意的是在What is your first and last name?这一项,作为服务器应该填写服务器的域名,比如www.baidu.com或者*.baidu.com。jdk中,在验证服务器端证书时,首先会取该字段来与当前访问的地址做对比,如果一致才能进入真正的证书验证阶段。
作为测试,建议此处输入localhost,在后续访问时同样使用localhost访问。如果后续方式需要使用ip,此处请输入ip。
3. 证书申请请求
Server有了公私钥对后,便可发起证书申请。证书申请时,需要提交证书申请文件,该文件一般称为CSR文件。生成该文件的命令如下:
keytool -certreq -alias myserver -keystore MyServer.jks -file MyServer.csr
该命令根据MyServer.jks公私钥对生成证书申请文件MyServer.csr,该文件生成后,便可提交到CA机构进行证书的申请。
4. CA颁发证书
CA机构接收到MyServer的证书申请请求时,校验MyServer身份通过后,就可以使用自己的私钥对证书进行认证(签名),命令如下:
keytool -gencert -alias MyCA -keystore MyCA.jks -infile MyServer.csr -outfile MyServer.crt
该命令使用CA的公私钥对文件MyCA.jks对证书申请文件MyServer.csr进行签名,最终生成MyServer的CA证书文件MyServer.crt。
5. 导入Server证书
CA机构为MyServer颁发认证认证书MyServer.crt后,MyServer即可将证书文件导入到自己的证书库MyServer.jks中,导入命令如下:
keytool -import -alias myserver -file MyServer.crt -keystore MyServer.jks -trustcacerts
该命令会将MyServer.crt证书文件导入到MyServer.jks证书库中,需要注意的是,此处的alias必须与服务器公私钥对名称保持一致,也就是必须为myserver。
但在导入时,会报错:
keytool error: java.lang.Exception: Failed to establish chain from reply
该错误表示证书链不能建立,其原因是由于在导入MyServer.crt证书时会对其签名做验证,以便确认是由信任机构CA颁发的。但是,由于SERVER端当前并未将MyCA的证书导入到的信任证书列表中,导致抛出该错误。因此,需要将MyCA.crt导入到受信任的证书列表中。可以直接将MyCA.crt导入到$JAVA_HOME/jre/lib/security/cacerts文件中。命令为:
keytool -import -alias MyCA -file MyCA.crt -keystore $JAVA_HOME/jre/lib/security/cacerts
导入之后,再运行导入MyServer.crt证书的命令即可成功导入。导入时需要输入cacerts的密码,默认密码为changeit。
由该错误原因可知,哪怕对于权威的CA机构,如果CA结构的公开证书没有安装到本地,同样会报该错误。
6. 配置tomcat
其实前面5步就已经完成证书颁发过程了,但是为了验证证书的颁发过程没有问题,我们还需要将MyServer.crt使用起来。此处我们服务器端使用tomcat来提供HTTPS服务,需要进行如下配置:
打开tomcat的配置文件$CATALINA_HOME/conf/server.xml,打开https的配置,填入证书文件地址、密码和证书名称,修改后的内容如下:
修改完成,保存启动tomcat即可。
- TIPS:如果需要查看tomcat的SSL日志,可以通过配置项javax.net.debug来开启。只需要在bin目录下创建一个setenv.sh脚本,并写入:
JAVA_OPTS="$JAVA_OPTS -Djavax.net.debug=ssl"
当然该配置项还可以进行更细粒度的控制,请参考:https://access.redhat.com/solutions/973783。
7. 编写HTTPS客户端
有了HTTPS服务器端,我们需要验证客户端是否能正确校验通过我们颁发的MyServer.crt。首先,我们模拟HTTPS客户端的机器需要安装MyCA.crt到信任证书中:
keytool -import -alias MyCA -file MyCA.crt -keystore $JAVA_HOME/jre/lib/security/cacerts
证书安装成功后,编写访问代码:
这段代码会访问tomcat的examples项目中的页面,注意访问地址使用的域名是localhost,该域名与MyServer.jks中的域名保持一致。如果访问成功,会打印如下信息:
获取到该信息后,表示我们的HTTPS通信是正确的,服务器端的证书MyServer.crt是被信任的,因为我们本地信任了MyCA,而MyServer.crt是由MyCA颁发的。如果我们要验证MyServer.crt是不被信任的,我们可以卸载掉之前安装的MyCA.crt,再次访问就会出现如下错误:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
该错误的原因是服务器的证书不能被正确的验证,也就是通过JDK默认的信任证书不能验证MyServer的证书。
如果想校验不通过的情况,可以通过命令删除jdk中的MyCA.crt,命令如下:
keytool -delete -alias MyCA -keystore $JAVA_HOME/jre/lib/security/cacerts
总结
本文通过使用jdk自带的keytool工具,模拟了服务器端CA证书的颁发过程。如果在项目中暂时不能在受信任的CA机构申请到证书,但是却需要开启HTTPS功能,可以使用该文中介绍的方式来自己颁发CA证书。另外,通过这种方式,就可以在家自己进行HTTPS相关的技术研究了。
本文是HTTPS那些事儿系列的最后一篇文章,如需查看本系列其他文章,请转至前言。
本人程序猿一枚,语言能力有限,本系列的文章是在工作过程中积累而来的,原意仅供自己备忘。现公开出来,也仅是想帮助有需要的人,如有问题,可随时留言,如果我懂我会不定期回复。