背景
我们在使用 HTTPS 时经常接触到的是服务器证书认证(单向认证),很少用到客户端证书认证(双向认证),这是因为对于 web 网站来说,用户数目广泛,使用服务器证书认证就够了,无需在 SSL 层做用户身份验证,对于需要验证的页面再在应用逻辑层来做用户身份验证(比如常见的账号密码登录),使用客户端证书认证反而影响用户体验。但是对于一些特殊企业客户,在涉及到资金、股票等等金融业务交易时,考虑到其业务的安全性,他们在原有业务可能已经用了客户端证书认证,对于这类客户在接入 CDN 时,必然也要支持客户端证书认证。
简单来说,客户端证书认证就是在单向认证 SSL 握手流程中加入了两个流程:
- 服务器发送 Certificate Request 消息,表明本次 SSL 握手需要做客户端证书认证(如下图第3步)。
- 客户端收到服务器发送的 Certificate Request 消息后,需要将客户端证书通过 Certificate 消息发送给服务器(如下图第4步)。服务器收到该消息时会对客户端证书做合法性校验,包括:客户端证书是否过期,发行该证书的 CA 是否可信,用其 CA 公钥对该证书的签名做校验是否成功,该证书是否已经被吊销。若其合法性校验成功则继续后面的握手流程,否则服务器会握手失败并中断连接。
但是,如果客户端没有发送客户端证书,服务器也会握手成功,这就需要在应用程序中做判断是否有客户端证书,若客户端没有发送则要关闭连接并清除 session 缓存(代码请参考 Tengine 的 ngx_http_process_request 函数)。
实现
Tengine 是很容易配置客户端证书认证的,相关的几个指令如下:
ssl_verify_client on; #是否启用客户端证书认证
ssl_verify_depth 1; #对客户端证书证书链的认证深度
ssl_client_certificate /opt/tengine/conf/xxx.crt; #指定用于校验客户端证书的CA证书