文章目录
一、最终效果
HTTPS双向认证,不仅仅需要用户浏览器校验服务器数字证书,还需要服务器端验证用户是否是可信的,在诸如金融、军事等涉密领域,非常讲究系统的安全性,理应杜绝任何其他无关用户访问服务器。
对于HTTPS的基础知识在此不再赘述,在完成最终的HTTPS双向认证配置后,用户端访问服务器时,用户端浏览器会让用户选择自己被服务器信任的证书,来完成用户端的认证,如下图:
二、步骤
2.1 单向认证
先从单向认证入手,理清楚基本的概念和脉络。
单向认证指的是“服务器端认证”,指的是当客户访问服务器的时候,客户方去校验服务器是否是自己想去访问的服务器,我们一步步来。
2.1.1 创建SpringBoot项目
利用Intellij IEAD,我们可以轻松便捷的创建各种版本的Spring Boot项目,点击File—>New—>Project,切换到Spring Initializr。
在Dependencies中把Web的Spring Web Strarter选中,其余的配置默认即可,于是便创建了项目“com.example.demo",目录结构如下:
2.1.2 利用keytool生成密钥库(keystore)
Keytool是Java用于管理密钥和证书的工具,其功能包括:
- 创建并管理密钥
- 创建并管理证书
- 作为CA为证书授权(比如可以自己给自己的证书授权)
- 导入导出证书
密钥库指的是可以存放密钥的一个库,创建的密钥或者证书总需要一个地方存着吧,所以keytool决定在密钥库里面存放公钥,私钥或者数字签名,再配合着一套keytool的命令去进行密钥库的管理。
在CMD运行下面命令,在C盘的当前用户文件夹里(如:C:\Users\CharlesChen)会生成"server.p12"文件。
keytool -genkey -alias server -keyalg RSA -keystore server.p12 -validity 360 -storepass 123456 -storetype PKCS12 -keysize 2048 -dname "CN=localhost, OU=localhost, O=localhost, L=CD, ST=CD, C=CN"
简单解释下:
- -genkey:创建keystore和密钥对(看做创建一张SQL表,-genkeypair与此命令没有差别)
- -alias:和keystore关联的别名(为新纪录添加GUID)
- -keyalg:加密方式
- -validity:该密钥的有效期为 180天(默认为90天)
- -storepass:存取密码
- -storetype:密钥库类型
- -keysize:密钥长度
- -dname:证书个人信息(CN:commonName服务器域名、OU:organizationUnit、O:organizationName、L:localityName、S:stateName、C:country)
更多细节可以参考其他博客《证书及证书管理(keytool工具实例)》
2.1.3 对SpringBoot进行配置
首先把创建的"server.p12"文件放在resources同级目录下,然后在applicaiotn.preperties里写入如下配置:
# 服务器端证书检验
server.ssl.key-store=classpath:server.p12
server.ssl.key-store-password=123456
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=server
至此,我们便完成了服务端认证的配置。
2.1.4 测试
启动项目后,我们利用Chrome浏览器访问https://localhost:8080,如下图:
之所以会报错,是因为该服务器的证书是我们自己签发的,在浏览器信任的CA列表中并没有我们自己。
2.2 双向认证
基于单向认证,怎样才能实现“客户端认证”?
双向认证的流程大体是:
- 用户生成自己的数字证书(不是密钥库,不过数字证书可以从密钥库导出,可以理解为没有私钥的密钥库)。
- 用户端安装自己的数字证书。
- 服务器端在密钥库中添加对用户端数字证书的信任。
2.2.1 利用keytool生成用户端密钥库并安装
现在我们转换角色成为一个需要证明自己是自己的客户,同样,客户需要生成他自己的密钥库,命令如下:
keytool -genkey -alias client -keyalg RSA -keystore client.p12 -validity 360 -storepass 123456 -storetype PKCS12 -keysize 2048 -dname "CN=client, OU=client, O=client, L=CD, ST=CD, C=CN"
生成密钥库后,双击并安装自己的证书,相当于你出门要把身份证带在身上,光有身份证还不行,如下图:
2.2.2 导出用户端数字证书
运行下面命令,可在C盘的当前用户文件夹里会生成"client.cer"文件。
keytool -keystore client.p12 -export -alias clent -file client.cer
简单解释下:
- -keystore:选中的密钥库
- -file:导出格式
2.2.3 服务器端信任用户端数字证书
运行下面的命令,把导出的client.cer数字签名证书加入到服务器密钥库里面,让服务器信任用户证书。
keytool -import -file client.cer -keystore server.p12
我们输入下面命令可以查看最终服务器密钥库的密钥列表:
keytool -list -keystore server.p12 -storepass 123456 -storetype PKCS12
在下图中我们可以看到,在server.p12中有一个包含了私钥的实体,还有个来自客户端的信任实体。
2.2.4 配置Spring Boot
在application.properties里面添加下面的配置,启用客户端校验,并说明密钥库位置。
# 客户端证书校验
server.ssl.trust-store=classpath:server.p12
server.ssl.trust-store-password=123456
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN
server.ssl.client-auth=need
2.2.5 测试
利用Chrome登录后我们发现会让用户选择自己的证书,这样会让服务器在自己的信任库里面搜索,如下图:
项目代码已上传到码云Gitee:https://gitee.com/blackbuttoncc/two-way-HTTPS-demo
三、思考
如何证明你是你自己,每次想起就头疼!
值得一提的是,上述步骤只是配置了HTTPS,但是没有禁止HTTP连接,所以后续优化步骤可以是强制所有的连接强制使用HTTPS,可参考文章《springboot–http跳转https》,在此不再赘述。
最后,如果真的决定在生产环境中配置在应用中验证双向HTTPS验证,推荐分离服务器端的原始密钥库和信任库。例如,在本文中,是在服务器密钥库配置的信任实体,最好可以分离为server.p12和trust.p12两个库,这样可以很方便的维护。
四、参考文献
- 《使用keytool 生成证书》,作者:美码师
- 《证书及证书管理(keytool工具实例)》,作者:南极山
- 《Keytool工具生成SSL证书以及在Java中实现SSL》,作者:Obito_uchiha
- 《Springboot的SSL双向认证》,作者:得谷养人
- 《springboot–http跳转https》,作者:乐兰特