前几天某某在 telegram 里提起 nginx 最新的 mainline 版本已经支持了双证书, 想起自己 Aliyun 上的 nginx 已经有一段日子没更新了, 赶紧上 NG 官网 看看有什么大新闻.
CHANGELOG 中截取的一段:
*) Feature: the "ssl_certificate" and "ssl_certificate_key" directives can be specified multiple times to load certificates of different types (for example, RSA and ECDSA).
ECDSA (椭圆曲线数字签名算法) 就是我们所说的 ECC 证书了, 相比 RSA, ECC 证书具有安全性高, 处理速度更快的优点, 尤其适合在移动设备上使用. 但是其唯一的缺点就是兼容性问题, 古代的 XP 和 Android2.3 不支持这种加密方式.
于是双证书就出现了. 它可以在 TLS 握手的时候根据客户端支持的加密方法选择对应的证书, 以向下兼容古代客户端.
当前最新的 nginx 版本为 1.11.1 // 全是 1
由于 Chrome 在 51 版本之后对 HTTP/2 协议仅支持 ALPN, 该特性需要 nginx 在编译的时候 (with) 使用 openssl 1.0.2g 及以上的版本(推荐最新的 1.0.2h, 免受 CVE-2016-2107 的影响). 但是 openssl 作为 linux 中关键的一个组件, 随意升级会出现很多不确定因素, 所以我选择手动编译安装 nginx 并使用 --with-openssl
来指定特定版本的 openssl.
wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_2h.tar.gz tar xzvf OpenSSL_1_0_2h.tar.gz wget http://nginx.org/download/nginx-1.11.1.tar.gz tar xzvf nginx-1.11.1.tar.gz cd nginx-1.11.1/ nginx -V ./configure --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --add-module=../nginx-ct-1.2.0 --with-openssl=../OpenSSL_1_0_2h --with-http_v2_module --with-http_ssl_module make make install service nginx restart
这里的 configure 配置仅供参考, 添加的 nginx-ct
模块是为开启Certificate Transperancy 策略做的准备.
在启用之前, 先要确保你有对应域名的两张证书, 一张 ECC 证书, 另一张 RSA 证书.
我的 ECC 证书是由 Let's Encrypt 签发的, 使用了最新的 X3 中间证书; RSA 证书则是 AlphaSSL 的泛解析证书.
nginx 配置如下
ssl_certificate ssl/letsencrypt-ecc/chained.pem; ssl_certificate_key ssl/letsencrypt-ecc/domain.key; ssl_certificate ssl/all.zeroling.com.crt; ssl_certificate_key ssl/all.zeroling.com.key; ssl_prefer_server_ciphers on; ssl_ciphers EECDH+CHACHA20:EECDH+ECDSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_ciphers 中同时添加了对 ECDSA 和 RSA 的支持
由于我的 RSA 证书签发者与 ECC 证书的不是同一个, 所以在 HPKP 头中, 需要同时指定两份证书的中间证书.
Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="amMeV6gb9QNx0Zf7FtJ19Wa/t2B7KpCF/1n2Js3UuSU="; max-age=2592000; includeSubDomains';
如果你的两份证书是由同一个签发者签名的, 只需要指定一次即可, 也就是说完全可以不用修改这项配置, 与单证书的时候保持一致肯定没错.
恩, 这个不影响.
前面我编译 nginx 的时候添加了 nginx-ct
模块, 这个模块可以帮助你很快的开启 CT 策略, 具体开启的方法可以参考 qgy 大神的 这篇文章 , 唯一要注意的地方是, 如果你的服务器在天朝, 目前还是无法请求 googleapis 的 CT Logs 服务器的, 所以你可以使用一些特殊姿势来完成, 比如把证书传到国外 VPS 验证后, 再把 sct 文件传回来.
对于开启了双证书的网站, 你只需要保证两本证书的所有 sct 文件都放在一个目录下, 并在 nginx 中配置 ssl_ct_static_scts
为该路径即可, 浏览器会自动识别有效的 CT 信息.
ssl_ct on; ssl_ct_static_scts ssl/sct;
这里其实有个坑, 详细可以看这篇文章:
https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html
我先来说结论:
Let's Encrypt 的 OCSP 服务也没有返回 Certificate,所以使用 Let's Encrypt 证书,开启 OCSP Stapling 也无需配置 ssl_trusted_certificate
如果 OCSP Response 包含了 Certificate 信息,并且 Nginx 配置了 ssl_stapling_verify on
,那么需要确保正确配置了 ssl_trusted_certificate
参数,这个参数应该指向一个包含根证书、中间证书的文件(顺序是子证书在上、父证书在下),否则 OCSP Stapling 无法生效。这时候 Nginx 的 error_log 中会出现类似这样的错误:
我的证书链中, ECC 的 let's encrypt 证书无需指定 ssl_trusted_certificate
, 也就是说即使指定也不会生效; 而 RSA 的 AlphaSSL 证书需要指定 ssl_trusted_certificate
为一个包含根证书、中间证书的文件.
ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate ssl/all.zeroling.com.server-middle-root.crt;
于是我的配置如下, 如果你的双证书都是 Let's Encrypt 签发的, 则 ssl_stapling_verify
和 ssl_trusted_certificate
两个配置可以忽略,完全不影响开启 OCSP Stapling。
配置完成后, 赶紧到 ssllab 测试一下是否配置正确吧.
在 Handshake Simulation 中, 如果可以看到 EC 和 RSA 同时出现在不同的机器上, 就说明成功了.
这是 本站的测试结果 .