OCSP Stapling 是通过服务端客户端主动获取 OCSP 查询结果并随着证书一起发送给客户端,转而提高 TLS 握手效率。

国内使用 Let’s Encrypt 证书的网站或者服务非常多,毕竟不要钱。如果你不启用OCSP Stapling 的话,客户端访问服务端的时候,需要去验证证书。而在国内因为一些特殊原因Let’s Encrypt 的验证服务器连接过程会异常缓慢,过程可能持续十几秒,并且在iOS上这个问题特别严重。当然是用别的证书开启OCSP Stapling的话也会大大提高效率。

检查 OCSP Stapling 状态

最简单的办法是通过第三方查询网站查询https状态,即可验证OCSP Stapling状态。但这类网站功能十分强大,查询完毕经常需要几十秒。

使用 openssl 这个命令验证 OCSP Stapling 状态;

完整命令如下:

$ openssl s_client -connect wangshuashua.com:443 -status -tlsextdebug < /dev/null 2>&1 | grep -i "OCSP response"

如果你的服务器上部署了多个 HTTPS 站点,可能还需要加上 -servername 参数启用 SNI:

$ openssl s_client -connect wangshuashua.com:443 -servername wangshuashua.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

配置 OCSP Stapling

为 Nginx 配置 OCSP Stapling ,如果您的证书是 Let’s Encrypt 家证书的可以忽略 ssl_trusted_certificate 参数;

因为 Let’s Encrypt 的 OCSP 服务不会返回 Certificate,所以使用 Let’s Encrypt 证书,开启 OCSP Stapling 无需配置 ssl_trusted_certificate

Let’s Encrypt 证书配置如下参数即可。

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 223.5.5.5 valid=300s ipv6=off;
resolver_timeout 5s;

其他证书配置需要附带 OCSP Stapling验证文件啊,及 ssl_trusted_certificate 参数。

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate "/path/to/site.pem";

下面介绍如何通过 openssl 在本地获取证书 OCSP Response,生成 ssl_trusted_certificate 所需站点证书。

首先需要准备好待验证网站证书链上的所有证书。证书链一般由根证书、一个或多个中间证书、站点证书组成。根证书内置在操作系统或浏览器内(Firefox),可以直接导出,或者去各大 CA 官方下载;中间证书、站点证书在建立 TLS 连接时由服务端发送,可以这样获取:

$ openssl s_client -connect wangshuashua.com:443 -showcerts < /dev/null 2>&1

CONNECTED(00000003)
depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify return:1
depth=0 C = CN, ST = ZheJiang, L = HangZhou, O = "Alibaba (China) Technology Co., Ltd.", CN = *.alicdn.com
verify return:1
---
Certificate chain
 0 s:/C=CN/ST=ZheJiang/L=HangZhou/O=Alibaba (China) Technology Co., Ltd./CN=*.alicdn.com
   i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
-----BEGIN CERTIFICATE-----
MIIG+DCCBeCgAwIBAgIMS62MvWfwokD9DEmpMA0GCSqGSIb3DQEBCwUAMGYxCzAJ
...
-----END CERTIFICATE-----
 1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
...
-----END CERTIFICATE-----
---
Server certificate
subject=/C=CN/ST=ZheJiang/L=HangZhou/O=Alibaba (China) Technology Co., Ltd./CN=*.alicdn.com
issuer=/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
---
No client certificate CA names sent
Peer signing digest: SHA256
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3578 bytes and written 415 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 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: 9B0E1E0095F9AF922AF4BA051FD03A3359DE6424EB7545F8B15DE7E3B6B...
    Session-ID-ctx: 
    Master-Key: B967E727C4C2C04E661B758669CA3360A810C0BB6FC17E995D9384C2C5FC...
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    TLS session ticket lifetime hint: 900 (seconds)
    TLS session ticket:
    0000 - c7 f3 15 16 ca 6f 94 ed-9e 9e 8e b7 f6 f1 53 b1   .....o........S.
    ....

    Start Time: 1608743940
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
DONE

以上内容中 Certificate Chain 这一节,编号为 0 的证书是站点证书;编号为 1 的证书是中间证书。

将站点证书保存为 site.pem;中间证书保存为 intermediate.pem(如果有多个中间证书,按照子证书在上的顺序保存);再从系统中导出对应的根证书存为 root.pem。

通过站点获取证书的 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 表示证书合法。

将根证书、全部中间证书按照子证书在上的顺序,保存为 chain.pem。这些就是我们需要在Nginx 配置文件里面需要的 ssl_trusted_certificate 文件了,可以通过以下代码验证获取的 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 中配置 ssl_stapling on 并 reload 后,Nginx 并不会马上获取 OCSP Response,它要等第一个请求过来,再发起异步 OCSP 请求,所以刚开始几个响应,很可能不带 OCSP Stapling。另外,有时候由于 OCSP 域名无法解析,或者服务器无法访问造成 OCSP Response 获取失败,也会导致 OCSP Stapling 无法生效。这是首先需要排查的地方,一般在 Nginx 的 error_log 中会有这样的错误:

[error] 5225#0: example.com could not be resolved (110: Operation timed out) while requesting certificate status, responder: example.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: example.com

如果证书 OCSP Response 没有包含 Certificate 信息,例如 COMODO、Let’s Encrypt 家的部分证书,那么 ssl_stapling_verify 和 ssl_trusted_certificate 两个配置可以忽略,完全不影响开启 OCSP Stapling。

扩充阅读:

Nginx常用命令