0x00 一个问题
最近我们需要将某个站点从http切换到https。在测试的时候,我们发现一些使用IE浏览器的用户,首次打开https的页面需要20s左右的时间,但是使用Chrome的用户却没有这个情况。
0x10 OCSP简介
我们知道每一张SSL证书都有一个有效期,但是有些证书会因为种种原因,在有效期之内就被废除了。那么对于浏览器来说,当它从一个站点获取证书时就需要验证此证书是否已经被废除。在过去,为了达到这个目的,浏览器会定期更新证书吊销列表(CRL)。这个列表会列出所有已经失效的证书,如果获取的证书在这个列表中,那么对不起,你的这张证书就不被浏览器信任了。但是这种方式有着很明显的缺陷:一个是列表会越来越大,无论是保存还是查询都会变得越来越慢;另一个是更新实时性差,如果一个证书被吊销,只有等到下次更新CRL时浏览器才会知道,这段时间内就有安全问题。
为了解决CRL的这些不足,在线证书状态协议(OCSP,Online Certificate Status Protocol)出现了。通过OCSP,浏览器可以在线向CA证书颁发机构查询证书的有效性。这样,浏览器就无需在本地维护一张表,同时由于是在线查询,实时性得到保障。然而,这种方式也有自己的问题:客户端在任何协商前必须等待OCSP的请求返回!细心点的读者已经想到,如果此时浏览器无法访问到对应的CA证书颁发机构,那么就会出现文章开头的问题。没错,IE浏览器正是使用OCSP来对证书进行验证。由于我们的测试环境无法访问公网,所以IE在一开始就会因无法获取到CA的返回而保持等待状态,直至超时。而Chrome则默认关闭了OCSP验证,取而代之的是Google自己的实现方式(,一种类似CRL的实现),因此没有出现这个问题。
0x20 OCSP Stapling
OCSP Stapling则解决了上述的问题。服务端主动通过OCSP获得证书状态,并随着证书一起发送给客户端,这样客户端就可以跳过自己去验证的步骤,以提高SSL握手的效率。
0x30 验证OCSP Stapling状态
你可以用Wireshark抓包或者到来查询自己站点的OCSP Stapling是否启用,也可以用我下面介绍的openssl命令行方式来轻松完成任务。
由于CentOS 6自带的openssl是1.0.1版本,而我希望能使用最新版本的openssl,所以我们先去下载最新的源码包编译安装,当前我能下载到的最新版本是1.1.0d。
目前主流的浏览器在建立TLS时,会发送一个status_request扩展,告诉服务器想得到OCSP Response。在openssl命令行中,加入-status
参数有同样的效果。如果服务器启用了SNI,那么还需要加入-servername
参数指定你需要查询的域名:
bash$ openssl s_client connect xyz.cn:443 -servername www.xyz.cn -status -tlsextdebug < /dev/null 2&>1|grep -i "ocsp response"
如果结果是下面这样,那就是开启了OCSP Stapling:
OCSP response: OCSP Response Data: OCSP Response Status: successful (0x0) Response Type: Basic OCSP Response
而如果是这样,显然没开启:
OCSP response: no response sent
0x40 Nginx配置OCSP Stapling
Nginx中与OCSP Stapling相关的有以下几个配置项:
ssl_stapling on;ssl_stalping_verify on;ssl_trusted_certificate /path/to/chain.pem;
注意此功能需要Nginx版本1.3.7以上。
前两个都好理解,最后一个是什么证书呢?
其实这里应该是一个完整的证书链,按照从上到下的顺序应该包括站点证书、中间证书(1张或多张)、根证书。证书服务商在你购买证书时会告诉你站点证书和中间证书,或者如果你忘了,也可以通过openssl命令行轻松获取:
bash$ openssl s_client connect xyz.cn:443 -showcerts < /dev/null 2&>1
显示结果如下(省略部分内容):
CONNECTED(00000003)depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CAverify error:num=20:unable to get local issuer certificate---Certificate chain 0 s:/C=CN/ST=Jiangsu/L=Nanjing/O=XYZ Insurance Co.,Ltd./OU=R&D Dept./CN=*.xyz.cn i:/C=US/O=GeoTrust Inc./CN=GeoTrust SSL CA - G3-----BEGIN CERTIFICATE-----MIIE6zCCA9OgAwIBAgIQMnPsmcfMo25b3nxFKihPCzANBgkqhkiG9w0BAQsFADBE......qjmhhFFHbLiqCpP2HTvh-----END CERTIFICATE----- 1 s:/C=US/O=GeoTrust Inc./CN=GeoTrust SSL CA - G3 i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA-----BEGIN CERTIFICATE-----MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT......zNSS-----END CERTIFICATE----- 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority-----BEGIN CERTIFICATE-----MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT.....b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S-----END CERTIFICATE--------Server certificatesubject=/C=CN/ST=Jiangsu/L=Nanjing/O=XYZ Insurance Co.,Ltd./OU=R&D Dept./CN=*.xyz.cnissuer=/C=US/O=GeoTrust Inc./CN=GeoTrust SSL CA - G3---...... Start Time: 1487731515 Timeout : 7200 (sec) Verify return code: 20 (unable to get local issuer certificate) Extended master secret: no---DONE
这样我们就得到了站点证书(0)和中间证书(1、2)。从最后一张中间证书可以看到根证书的颁发机构为Equifax Secure Certificate Authority。此时我们可以到根证书颁发机构的下载到此根证书,也可以从操作系统中导出:
。
上面的返回内容中有一个报错信息:Verify return code: 20 (unable to get local issuer certificate)
,这是因为我们没有指定根证书。在得到根证书后,我们可以再次执行上面的命令,并加入-CAfile root.pem
选项:
bash$ openssl s_client connect xyz.cn:443 -showcerts < /dev/null 2&>1
将会显示:Verify return code: 0 (ok)
接下来,我们将站点证书保存为site.pem,中间证书保存为intermediate.pem,根证书保存为root.pem,然后再将这三种证书按序保存为chain.pem:
bash$ cat site.pem intermediate.pem root.pem > chain.pem
这样就能完成Nginx的OCSP Stapling配置了。
0x50 参考:
【1】
【2】