最近收到的几封读者邮件,都是询问为什么在 Nginx 中无法开启 OCSP Stapling。具体现象是在 Nginx 中明明配置了 ssl_stapling on
,但通过 SSL Labs 等工具查看,OCSP stapling 这一项并没有生效。本文就这个问题详细探讨下,如果你只关心结论,直接跳到最后一节即可。
我之前在《TLS 握手优化详解》这篇文章中介绍了 OCSP 是什么,为什么要开启 OCSP Stapling,这里再简单介绍下:
OCSP(Online Certificate Status Protocol,在线证书状态协议)是用来检验证书合法性的在线查询服务,一般由证书所属 CA 提供。某些客户端会在 TLS 握手阶段进一步协商时,实时查询 OCSP 接口,并在获得结果前阻塞后续流程。OCSP 查询本质是一次完整的 HTTP 请求 - 响应,这中间 DNS 查询、建立 TCP、服务端处理等环节都可能耗费很长时间,导致最终建立 TLS 连接时间变得更长。
而 OCSP Stapling(OCSP 封套),是指服务端主动获取 OCSP 查询结果并随着证书一起发送给客户端,从而让客户端跳过自己去验证的过程,提高 TLS 握手效率。
通过前面提到的 SSLLabs 这个强大的在线服务,可以轻松验证指定网站是否开启 OCSP Stapling。但这个网站功能太多,国内网站很可能要花十分钟才能完成全部检测项看到结果。
使用 Wireshark 这个神器,设置好抓包条件和过滤器后,也可以很方便地验证某个网站是否开启 OCSP Stapling,有兴趣的同学可以自己试一下。
本文介绍如何使用 openssl 这个命令行工具来完成同样的任务。
多说一句,Mac 系统自带的 openssl 版本太低了,建议通过 brew 装上新版:
$ brew install openssl $ brew link openssl --force
如果使用其它系统,也请通过 openssl version
检查一下 openssl 的版本,不要太旧了。
服务端启用 OCSP Stapling 之后,客户端还需要在建立 TLS 时,在 Client Hello 中启用 status_request 这个 TLS 扩展,告诉服务端自己希望得到 OCSP Reponse。目前主流浏览器都会带上 status_request,而在 openssl 中需要指定 -status
参数。完整命令如下:
$ openssl s_client -connect imququ.com:443 -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>&1 OCSP 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>&1 CONNECTED(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:1 depth=1 C = US, O = GeoTrust Inc., CN = RapidSSL SHA256 CA - G4 verify return:1 depth=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 Failure 140735166222416: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 Reponse 内容而报的错。这个错误可以通过加上 -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 Failure 140735166222416: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。
好了,本文先写到这里。本站近期开始接受捐赠,如果你认为本站文章对你有帮助,欢迎捐赠本站。详情请点击这里 »
本文链接: https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html , 参与评论 。
-- EOF --
发表于 2016-03-13 21:06:57 ,并被添加「OCSP、 HTTP 、 Nginx 、Certificate」标签。
本站所有文章均为本人原创,如果你认为我的文章对你有帮助,欢迎捐赠本站。详情请点这里 »