关于ssl(https)客户端配置
最近接触到银行的接口,对于安全方面非常严格,发到银行的报文需要加签、加密,接口也是ssl双向认证,只接受OVSSL以上的证书,这里记录下作为客户端ssl认证的两种方式。
银行会给一个他们的ssl公钥,这个公钥是用来验证他们返回报文的
因为银行测试环境还没有申请下来,所以也写了个本地的https服务端作为认证测试
客户端ssl证书生成
傻瓜式界面操作,需要软件KeyStore Explorer。
下载后新建-选择JKS(其他也可以)
Tools->Generate Key Pair,生成密匙对
选择RSA(2048),点击Name输入网站信息
这里需要注意,Common Name 要对应接口域名或者ip。
接下去就是下一步下一步,遇到密码就输入密码,然后保存到本地一个文件,比如127.0.0.1-ssl-client.jks。这里会有两次输入密码,第一次是密钥的密码,第二次是jks文件的密码,一般用一样的就行。
导出公钥:KeyStoreExplorer里面双击密钥对,点击pem,export,导出公钥pem文件,比如命名为127.0.0.1-ssl-client-pub.pem
然后把这个127.0.0.1-ssl-client-pub.pem文件给银行,银行加入到他们的信任列表里面,我们就可以调用他们的接口了。(当然这里只是为了测试,真实情况需要把证书发给CA认证才行)
客户端把银行的公钥加入信任列表
银行给的公钥是pem格式的,假设叫citi-ssl-pub.pem,需要把这个公钥加入信任密钥对(KeyStore)。
打开KeyStore Explorer,新建->选择JKS->Tools->Import Trusted Certificate,选择银行的公钥citi-ssl-pub.pem
保存,填写密码,这个也是jks文件的密码,保存文件可以叫127.0.0.1-ssl-client-trust.jks
客户端代码直接请求https接口
上面我们有了客户端ssl证书、也把公钥给了银行、银行把公钥给我们,我们也生成了信任jks。可以开始写代码了。这里用hutool的httpUtil工具类请求银行接口,直接上代码
这里代码写好了,当然运行肯定回报错,银行的测试环境没有,我们这里再模拟写一个https的服务,作为测试。
https服务端ssl证书生成
同客户端一模一样,也需要导出公钥,假设叫127.0.0.1-ssl-server-pub.pem。把这个也加入到127.0.0.1-client-trust.jks作为信任证书,方法同加入citi-ssl-pub.pem一样。这样127.0.0.1-client-trust.jks里面就有两个信任证书了
https服务端把客户端ssl证书加入信任列表
这里的步骤也和客户端一样,生成一个jks密钥对,加入客户端的ssl公钥证书(127.0.0.1-ssl-client-pub.pem)。这里是模拟银行把我们的公钥拿过去后的操作。服务端的信任jks文件名:127.0.0.1-ssl-server-trust.jks
整理下所有的测试证书文件
ssl ssl文件夹|---client 客户端ssl文件夹|---|---127.0.0.1-ssl-client.jks 客户端ssl密钥对,包含私钥|---|---127.0.0.1-ssl-client-pub.pem 客户端ssl公钥证书|---|---127.0.0.1-ssl-client-trust.jks 客户端信任证书(包含两个公钥证书)|---server 服务端ssl文件夹|---|---127.0.0.1-ssl-server.jks 服务端ssl密钥对,包含密钥|---|---127.0.0.1-ssl-server-pub.pem 服务端ssl公钥证书|---|---127.0.0.1-ssl-server-trust.jks 服务端信任证书列表|---|---citi-ssl-pub.pem 银行ssl公钥证书
本地模拟https服务端代码
springboot 项目,在配置文件中增加ssl
server: port: 4000 ssl: key-store-type: JKS key-store: classpath:ssl/server/127.0.0.1-ssl-server.jks key-store-password: 127 key-password: 127 trust-store-type: JKS trust-store: classpath:ssl/server/127.0.0.1-ssl-server-trust.jks trust-store-password: 127 client-auth: need
服务端接口代码,非常简单
@RestControllerpublic class SslServer { @RequestMapping("/ssl") public String hello(@RequestParam("host") String host) { return "hello " + host; }}
启动服务端项目
测试客户端直接请求服务端
运行客户端main方法,直接请求,正常返回
服务端没有加入客户端信任证书测试
修改服务端的信任证书列表,将127.0.0.1-ssl-client-pub.pem证书删掉
增加citi-ssl-pub.pem(不能为空),再次请求
如果没有把客户端证书加入到服务端信任列表,请求就会报错。还原服务端的信任证书列表
把客户端的信任列表去掉
客户端代码改为
再次请求,发现还是能正常访问,那么客户端的信任列表有什么用呢?我的理解是为了验证服务端的响应是否为银行,防止返回的报文被他人篡改
客户端http请求nginx,nginx转发银行
这种情况是为了应付如果项目部署服务机器不能访问外网,可以通过nginx转发。如果是这种方式代码就不需要配置任何东西,请求nginx的端口就行这种情况nginx配置文件
http{ server{ listen 80; server_name localhost; location /bank { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; proxy_ssl_certificate 127.0.0.1-ssl-client-pub.pem; proxy_ssl_certificate_key 127.0.0.1-ssl-client-pri.pem; proxy_ssl_trusted_certificate 127.0.0.1-ssl-server-pub.pem; proxy_ssl_verify on; proxy_ssl_server_name on; proxy_ssl_verify_depth 2; proxy_pass https://127.0.0.1:4000/ssl; } }}
这里的127.0.0.1-ssl-client-pub.pem 和127.0.0.1-ssl-server-pub.pem 和原来的一样,但是需要一个客户端ssl证书密钥,也可以通过keyStore Explorer导出
右键选择密钥对->View Details->Private Key Details->输入密码->点击pem->Export
还存在一个疑问没解决:127.0.0.1-ssl-server-pub.pem 这个公钥如果Common Name不是域名,nginx转发会报错,upstream SSL certificate does not match "127.0.0.1" while SSL handshaking to upstream
代码可以在Github上找到