
关于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
服务端接口代码,非常简单
@RestController
public 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上找到