背景说明

在多数开放性的网络系统中,https的证书校验可能都只需要进行单向认证,即只需要客户端校验服务端证书,例如12306、支付宝等。
但是,在一些特定交互的系统中,并不完全开放的网络场景下,为了进一步提升交互双方的可靠性,客户端和服务端都需要校验对方的证书信息,即双向认证。
服务端和客户端这样一个网络架构中,一般是一对多的关系,即一个服务端对应着多个客户端。
因此在证书双向认证的过程中,也就意味着一个服务端证书需要对应着多个客户端证书。对于java程序来说,这个服务端可能是配置成https的tomcat,也可能是配置成https的nginx。
这个时候,客户端证书虽然是多个,但是实际上究竟是同一个的多个副本,还是都是唯一的原件,单纯从nginx或tomcat的https校验来说,是无法区分的。
那么这样一来,对于安全要求非常高、需要每个客户端证书都是唯一的场景,就无法得到满足。
因此,就还需要实际业务系统能够获取到证书信息,再结合具体客户端的业务绑定数据,来判断证书和客户端的一致性。
刚好,NGINX可以把解析后的证书信息赋值给特定的变量使用,例如设置到请求头中。这样,java代码收到请求时就可以获取请求头中的证书信息,然后再根据具体业务数据校验证书一致性。

NGINX配置

要实现上边说的效果,除开原本nginx的https相关的基本配置外,就还需要在nginx的location中加入header设置的一些配置,大概如下图:

java 请求如何使用证书_java 请求如何使用证书


nginx内部解析证书后,会把相关信息放到内置的变量中,我们可以使用$加内置变量名使用。如证书内容信息$ssl_client_cert,证书序列号$ssl_client_serial,证书subject信息$ssl_client_s_dn

以上变量的说明,可参考nginx官网:http://www.nginx.cn/doc/optional/ssl.html 拿到上边nginx内置变量后,就可以配置一下设置到请求头中,请求头变量名自定义,例如:

proxy_set_header X-SSL-Client-Cert $ssl_client_cert;proxy_set_header X-SSL-serial $ssl_client_serial; proxy_set_header cert-subject $ssl_client_s_dn;


其中,X-SSL-Client-Cert、X-SSL-serial、cert-subject为自定义变量名,供后边java代码获取header值使用。

JAVA获取header中的证书信息

有了上边的配置,那么对应的路由下的所有请求就会在请求头中携带nginx解析后的证书信息,Java代码也就可以获取到header中的证书相关的一些信息,例如:


public class CaController { @GetMapping("/test") public String caTest(@RequestHeader("X-SSL-serial") String serial, @RequestHeader("X-SSL-Client-Cert") String cert, @RequestHeader("cert-subject") String subject) { return serial + " \r" + cert + " \r" + subject; } }


浏览器访问就可以看到正确拿到客户端证书、序列号以及subject等:

java 请求如何使用证书_ca证书_02