本文转载自:https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html html
最近收到的几封读者邮件,都是询问为何在 Nginx 中没法开启 OCSP Stapling。具体现象是在 Nginx 中明明配置了 ssl_stapling on
,但经过 SSL Labs 等工具查看,OCSP stapling 这一项并无生效。本文就这个问题详细探讨下,若是你只关心结论,直接跳到最后一节便可。git
我以前在《TLS 握手优化详解》这篇文章中介绍了 OCSP 是什么,为何要开启 OCSP Stapling,这里再简单介绍下:api
OCSP(Online Certificate Status Protocol,在线证书状态协议)是用来检验证书合法性的在线查询服务,通常由证书所属 CA 提供。某些客户端会在 TLS 握手阶段进一步协商时,实时查询 OCSP 接口,并在得到结果前阻塞后续流程。OCSP 查询本质是一次完整的 HTTP 请求 - 响应,这中间 DNS 查询、创建 TCP、服务端处理等环节均可能耗费很长时间,致使最终创建 TLS 链接时间变得更长。浏览器
而 OCSP Stapling(OCSP 封套),是指服务端主动获取 OCSP 查询结果并随着证书一块儿发送给客户端,从而让客户端跳过本身去验证的过程,提升 TLS 握手效率。bash
经过前面提到的 SSL Labs 这个强大的在线服务,能够轻松验证指定网站是否开启 OCSP Stapling。但这个网站功能太多,国内网站极可能要花十分钟才能完成所有检测项看到结果。服务器
使用 Wireshark 这个神器,设置好抓包条件和过滤器后,也能够很方便地验证某个网站是否开启 OCSP Stapling,有兴趣的同窗能够本身试一下。session
本文介绍如何使用 openssl 这个命令行工具来完成一样的任务。异步
多说一句,Mac 系统自带的 openssl 版本过低了,建议经过 brew 装上新版:ide
$ brew install openssl $ brew link openssl --force
若是使用其它系统,也请经过 openssl version
检查一下 openssl 的版本,不要太旧了。工具
服务端启用 OCSP Stapling 以后,客户端还须要在创建 TLS 时,在 Client Hello 中启用 status_request 这个 TLS 扩展,告诉服务端本身但愿获得 OCSP Response。目前主流浏览器都会带上 status_request,而在 openssl 中须要指定 -status
参数。完整命令以下:
$ openssl s_client -connect imququ.com:443 -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"
若是你的服务器上部署了多个 HTTPS 站点,可能还须要加上 -servername
参数启用 SNI:
$ openssl s_client -connect imququ.com:443 -servername imququ.com -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
了解如何经过 openssl 验证 OCSP Stapling 状态后,咱们再来看看 OCSP Response 的完整内容,去掉前面命令中的 grep 就能够看到。例如这是本站的:
$ openssl s_client -connect imququ.com:443 -status -tlsextdebug < /dev/null 2>&1OCSP response: ====================================== OCSP Response Data: OCSP Response Status: successful (0x0) Response Type: Basic OCSP Response Version: 1 (0x0) Responder Id: 87BAEBE8F7B12700EC9CD1A04EE0E123E57D809E Produced At: Mar 11 07:56:56 2016 GMT Responses: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 7C8E4E54532DB74C235073AAF1CDCF2C2423F86B Issuer Key Hash: F3B5560CC409B0B4CF1FAAF9DD2356F077E8A1F9 Serial Number: 5A26 Cert Status: good This Update: Mar 11 07:56:56 2016 GMT Next Update: Mar 18 07:56:56 2016 GMT Signature Algorithm: sha1WithRSAEncryption 8a:81:d6:a5:aa:8a:92:05:6f:39:97:f5:da:d0:bc:06:86:f2: ... ... Certificate: Data: Version: 3 (0x2) Serial Number: 8 (0x8) Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4 Validity Not Before: Jul 10 18:18:29 2015 GMT Not After : May 22 18:18:29 2016 GMT Subject: CN=RapidSSL SHA256 CA - G4 OCSP Responder Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:9d:e9:7b:75:81:1e:00:ab:b3:b4:cc:3f:a3:2d: ... ... Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9 OCSP No Check: X509v3 Subject Key Identifier: 87:BA:EB:E8:F7:B1:27:00:EC:9C:D1:A0:4E:E0:E1:23:E5:7D:80:9E X509v3 Extended Key Usage: OCSP Signing X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature X509v3 Subject Alternative Name: DirName:/CN=TGV-C-26 Signature Algorithm: sha256WithRSAEncryption bb:ac:c3:3e:8b:20:be:a0:a7:4d:bb:e1:d1:c3:98:17:8e:58: ... ... -----BEGIN CERTIFICATE----- MIIDnTCCAoWgAwIBAgIBCDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEW ... ... bmgyvaosG4GykSUnasMqfbA= -----END CERTIFICATE----- ======================================
能够看到 OCSP Response 由两部分组成:OCSP Response Data 和 Certificate。OCSP Response Data 是本站证书的验证信息;而 Certificate 则是用来验证 OCSP Response Data。本例中的 Certificate 的 Common Name 是 RapidSSL SHA256 CA - G4 OCSP Responder,能够看出它专属于 RapidSSL 的 OCSP 服务。后面咱们会发现,并非每一家 CA 的 OCSP Response 都会提供 Certificate 信息。
上面这段 OCSP Response 信息是经过服务端 OCSP Stapling 获取的。下面介绍如何经过 openssl 在本地获取证书 OCSP Response。
首先须要准备好待验证网站证书链上的全部证书。证书链通常由根证书、一个或多个中间证书、站点证书组成。根证书内置在操做系统或浏览器内(Firefox),能够直接导出,或者去各大 CA 官方下载;中间证书、站点证书在创建 TLS 链接时由服务端发送,能够这样获取:
$ openssl s_client -connect imququ.com:443 -showcerts < /dev/null 2>&1CONNECTED(00000003) depth=2 C = US, O = GeoTrust Inc., OU = (c) 2008 GeoTrust Inc. - For authorized use only, CN = GeoTrust Primary Certification Authority - G3 verify return:1depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA - G4 verify return:1depth=0 CN = www.imququ.com verify return:1--- Certificate chain 0 s:/CN=www.imququ.com i:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G4 -----BEGIN CERTIFICATE----- MIIFMDCCBBigAwIBAgICWiYwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCVVMx ... ... fBv5YysJ/pgFe75P9RVALMiPUPHvH2FGI47pxlvzs5+7Gt2p -----END CERTIFICATE----- 1 s:/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G4 i:/C=US/O=GeoTrust Inc./OU=(c) 2008 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G3 -----BEGIN CERTIFICATE----- MIIEpjCCA46gAwIBAgIQKByJKWYUQ4BCY1U6MkCuszANBgkqhkiG9w0BAQsFADCB ... ... nPvdJAq9WZFKQgM4EnEyiHagjny7Mu+IKhvUam9QuVJni6sw+h/94ySa -----END CERTIFICATE----- --- Server certificate subject=/CN=www.imququ.com issuer=/C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G4 --- No client certificate CA names sent Peer signing digest: SHA512 Server Temp Key: ECDH, P-256, 256 bits --- SSL handshake has read 3460 bytes and written 434 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256 Server public key is 4096 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES128-GCM-SHA256 Session-ID: B6A0F49F6DAD0BD8AFB63F87D134FFCBC2B1487CD81440C26D165B5738A5C3EC Session-ID-ctx: Master-Key: 72871B14BC37B08F51F818285264169C512B865D13839C9B824175115F008801781FBAC64D01FC76376BCAB85E6B8F84 Key-Arg : None PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 86400 (seconds) TLS session ticket: 0000 - 56 f8 0d dd 0e ea 7d 0b-09 70 0b dd 52 da b7 a8 V.....}..p..R... ... ... ... ... 00a0 - c2 25 af a9 46 69 64 73-69 16 ea 64 94 c7 f4 a4 .%..Fidsi..d.... Start Time: 1457861201 Timeout : 300 (sec) Verify return code: 0 (ok) --- DONE
以上内容中 Certificate Chain 这一节,编号为 0 的证书是站点证书;编号为 1 的证书是中间证书。个人证书链一共是三级,服务端只须要发送两个证书,对于四级证书链,服务端就须要发送三个证书了。总之,只有根证书无需发送。
将站点证书保存为 site.pem;中间证书保存为 intermediate.pem(若是有多个中间证书,按照子证书在上的顺序保存);再从系统中导出对应的根证书存为 root.pem。这样,证书链上的全部证书都搞定了。为了确保无误,建议再验证一下每一个证书的 Common Name:
$ openssl x509 -in site.pem -noout -subject subject= /CN=www.imququ.com $ openssl x509 -in intermediate.pem -noout -subject subject= /C=US/O=GeoTrust Inc./CN=RapidSSL SHA256 CA - G4 $ openssl x509 -in root.pem -noout -subject subject= /C=US/O=GeoTrust Inc./OU=(c) 2008 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G3
接着,获取站点证书的 OCSP 服务地址:
$ openssl x509 -in site.pem -noout -ocsp_uri http://gz.symcd.com
如今万事具有,使用如下命令便可得到站点证书的 OCSP Response:
$ openssl ocsp -issuer intermediate.pem -cert site.pem -no_nonce -text -url http://gz.symcd.com OCSP Request Data: Version: 1 (0x0) Requestor List: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 7C8E4E54532DB74C235073AAF1CDCF2C2423F86B Issuer Key Hash: F3B5560CC409B0B4CF1FAAF9DD2356F077E8A1F9 Serial Number: 5A26 OCSP Response Data: OCSP Response Status: successful (0x0) Response Type: Basic OCSP Response Version: 1 (0x0) Responder Id: 87BAEBE8F7B12700EC9CD1A04EE0E123E57D809E Produced At: Mar 11 07:56:56 2016 GMT Responses: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 7C8E4E54532DB74C235073AAF1CDCF2C2423F86B Issuer Key Hash: F3B5560CC409B0B4CF1FAAF9DD2356F077E8A1F9 Serial Number: 5A26 Cert Status: good This Update: Mar 11 07:56:56 2016 GMT Next Update: Mar 18 07:56:56 2016 GMT Signature Algorithm: sha1WithRSAEncryption 8a:81:d6:a5:aa:8a:92:05:6f:39:97:f5:da:d0:bc:06:86:f2: ... ... Certificate: Data: Version: 3 (0x2) Serial Number: 8 (0x8) Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=GeoTrust Inc., CN=RapidSSL SHA256 CA - G4 Validity Not Before: Jul 10 18:18:29 2015 GMT Not After : May 22 18:18:29 2016 GMT Subject: CN=RapidSSL SHA256 CA - G4 OCSP Responder Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:9d:e9:7b:75:81:1e:00:ab:b3:b4:cc:3f:a3:2d: ... ... Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:F3:B5:56:0C:C4:09:B0:B4:CF:1F:AA:F9:DD:23:56:F0:77:E8:A1:F9 OCSP No Check: X509v3 Subject Key Identifier: 87:BA:EB:E8:F7:B1:27:00:EC:9C:D1:A0:4E:E0:E1:23:E5:7D:80:9E X509v3 Extended Key Usage: OCSP Signing X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature X509v3 Subject Alternative Name: DirName:/CN=TGV-C-26 Signature Algorithm: sha256WithRSAEncryption ... ... -----BEGIN CERTIFICATE----- MIIDnTCCAoWgAwIBAgIBCDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQGEwJVUzEW ... ... bmgyvaosG4GykSUnasMqfbA= -----END CERTIFICATE----- Response Verify Failure140735166222416:error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:ocsp_vfy.c:138:Verify error:unable to get local issuer certificate site.pem: good This Update: Mar 11 07:56:56 2016 GMT Next Update: Mar 18 07:56:56 2016 GMT
能够看到,本身获取的 OCSP Response 和服务端发送的 OCSP Stapling 彻底一致,响应中的 Cert Status: good
表示证书合法。
可是,这段内容最后,多了这样的奇怪信息:
Response Verify Failure 140735166222416:error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:ocsp_vfy.c:138:Verify error:unable to get local issuer certificate
实际上,这是由于咱们没有告诉 openssl 应该信任哪些证书,openssl 没法验证 OCSP Response 内容而报的错。这个错误能够经过加上 -noverify
参数屏蔽,但更好的作法是经过 -CAfile
指定信任证书。
将根证书、所有中间证书按照子证书在上的顺序,保存为 chain.pem
。再次执行:
$ openssl ocsp -CAfile chain.pem -issuer intermediate.pem -cert site.pem -no_nonce -text -url http://gz.symcd.com
这下,最终的结果就是 Response verify OK 了。
Nginx 在实现 OCSP Stapling 这部分逻辑时,直接使用了 OpenSSL 库(这也解释了为何 Nginx + BoringSSL 不支持 OCSP Stapling)。咱们再来看看 Nginx 中与 OCSP Stapling 有关的三个重要配置项:
ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /your/path/to/chained.pem;
ssl_stapling
的做用天然不用说,ssl_trusted_certificate
至关于前面命令中的 -CAfile
。还记得前面说过「若是不指定 -CAfile
就得加上 -noverify
」么,换句话说,若是开启了 Nginx 的 ssl_stapling_verify
,但没有正确配置 ssl_trusted_certificate
,就会致使 OCSP Response 验证失败,OCSP Stapling 天然不会生效。
昨天给我写信的那位同窗就是栽在这里。实际上,检查 Nginx 的 error_log,应该有相似这样的错误:
[error] 5594#0: OCSP_basic_verify() failed (SSL: error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:Verify error:unable to get local issuer certificate) while requesting certificate status, responder: gz.symcd.com
能够看到,这个错误确实是 OpenSSL 库报的。
看到这里,有些同窗可能会说:不对,我明明开启了 ssl_stapling_verify
,也没有指定 ssl_trusted_certificate
,为何个人 OCSP Stapling 功能也是正常的呢?
昨天那位同窗的邮件中也问到这个奇怪的问题:
我 Nginx 目前的 OCSP stapling 配置为:
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 223.5.5.5 valid=300s;
resolver_timeout 5s;
有个奇怪的问题 :
我在使用 AlphaSSL 证书的域名测试 OCSP stapling 显示 NO,可是用配置了 COMODO ECC 证书的域名 OCSP stapling 就为 Yes。两个域名的 Nginx 配置彻底相同。
看来这个问题远远没这么简单,难道还跟证书有关?我找了一个一样使用 COMODO 证书的站点,使用前面讲过的方法得到了它的 OCSP Response:
$ openssl ocsp -CAfile root.pem -issuer intermediate.pem -cert site.pem -no_nonce -text -url http://ocsp.comodoca.com OCSP Request Data: Version: 1 (0x0) Requestor List: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 7AE13EE8A0C42A2CB428CBE7A605461940E2A1E9 Issuer Key Hash: 90AF6A3A945A0BD890EA125673DF43B43A28DAE7 Serial Number: 15F3C026B44BEEF870A7A496640BC484 OCSP Response Data: OCSP Response Status: successful (0x0) Response Type: Basic OCSP Response Version: 1 (0x0) Responder Id: 90AF6A3A945A0BD890EA125673DF43B43A28DAE7 Produced At: Mar 12 23:59:22 2016 GMT Responses: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 7AE13EE8A0C42A2CB428CBE7A605461940E2A1E9 Issuer Key Hash: 90AF6A3A945A0BD890EA125673DF43B43A28DAE7 Serial Number: 15F3C026B44BEEF870A7A496640BC484 Cert Status: good This Update: Mar 12 23:59:22 2016 GMT Next Update: Mar 16 23:59:22 2016 GMT Signature Algorithm: sha256WithRSAEncryption 29:a8:b4:a3:60:98:d9:c3:4f:56:4b:72:6c:9a:9e:7f:51:2d: ... ... Response Verify Failure140735166222416:error:27069076:OCSP routines:OCSP_basic_verify:signer certificate not found:ocsp_vfy.c:92: site.pem: good This Update: Mar 12 23:59:22 2016 GMT Next Update: Mar 16 23:59:22 2016 GMT
此次测试全部步骤跟以前同样,证书状态正常,也指定了 -CAfile
,但 OCSP Response 验证仍是失败:
Response Verify Failure 140735166222416:error:27069076:OCSP routines:OCSP_basic_verify:signer certificate not found:ocsp_vfy.c:92:
从错误信息中能够得知,COMODO 的 OCSP Response 没有提供 Certificate 信息,致使没法验证其内容。进一步测试发现,对于这种状况,Nginx 直接忽略了ssl_stapling_verify
参数,不管是否配置,都不执行 verify 操做。
通过测试,Let's Encrypt 的 OCSP 服务也没有返回 Certificate,因此使用 Let's Encrypt 证书,开启 OCSP Stapling 也无需配置 ssl_trusted_certificate
。
顺便说一下,获取 Let's Encrypt 证书的 OCSP Response 必定要指定 Host,就像这样:
openssl ocsp -CAfile chain.pem -issuer intermediate.pem -cert site.pem -no_nonce -text -url http://ocsp.int-x1.letsencrypt.org/ -header "HOST" "ocsp.int-x1.letsencrypt.org"
在 Nginx 中配置 ssl_stapling on
并 reload 后,Nginx 并不会立刻获取 OCSP Response,它要等第一个请求过来,再发起异步 OCSP 请求,因此刚开始几个响应,极可能不带 OCSP Stapling。另外,有时候因为 OCSP 域名没法解析,或者服务器没法访问形成 OCSP Response 获取失败,也会致使 OCSP Stapling 没法生效。这是首先须要排查的地方,通常在 Nginx 的 error_log 中会有这样的错误:
[error] 5225#0: xxx.com could not be resolved (110: Operation timed out) while requesting certificate status, responder: xxx.com
若是 OCSP Response 包含了 Certificate 信息,而且 Nginx 配置了 ssl_stapling_verify on
,那么须要确保正确配置了 ssl_trusted_certificate
参数,这个参数应该指向一个包含根证书、中间证书的文件(顺序是子证书在上、父证书在下),不然 OCSP Stapling 没法生效。这时候 Nginx 的 error_log 中会出现相似这样的错误:
[error] 4832#0: OCSP_basic_verify() failed (SSL: error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:Verify error:unable to get local issuer certificate) while requesting certificate status, responder: xxx.com
若是证书 OCSP Response 没有包含 Certificate 信息,例如 COMODO、Let's Encrypt 家的部分证书,那么 ssl_stapling_verify
和 ssl_trusted_certificate
两个配置能够忽略,彻底不影响开启 OCSP Stapling。
.