Nginx 开启 https 双证书指南

GerA39 3年前
   <p><img src="https://simg.open-open.com/show/10901150915b2367923274ded2f9a518.png"></p>    <h2>前言</h2>    <p>前几天某某在 telegram 里提起 nginx 最新的 mainline 版本已经支持了双证书, 想起自己 Aliyun 上的 nginx 已经有一段日子没更新了, 赶紧上 <a href="/misc/goto?guid=4958974879287255868" rel="nofollow,noindex">NG 官网</a> 看看有什么大新闻.</p>    <p>CHANGELOG 中截取的一段:</p>    <pre>      *) 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).  </pre>    <p>ECDSA (椭圆曲线数字签名算法) 就是我们所说的 ECC 证书了, 相比 RSA, ECC 证书具有安全性高, 处理速度更快的优点, 尤其适合在移动设备上使用. 但是其唯一的缺点就是兼容性问题, 古代的 XP 和 Android2.3 不支持这种加密方式.</p>    <p>于是双证书就出现了. 它可以在 TLS 握手的时候根据客户端支持的加密方法选择对应的证书, 以向下兼容古代客户端.</p>    <h2>升级 Nginx</h2>    <p>当前最新的 nginx 版本为 1.11.1 // 全是 1</p>    <p>由于 Chrome 在 51 版本之后对 HTTP/2 协议仅支持 ALPN, 该特性需要 nginx 在编译的时候 (with) 使用 openssl 1.0.2g 及以上的版本(推荐最新的 1.0.2h, 免受 CVE-2016-2107 的影响). 但是 openssl 作为 linux 中关键的一个组件, 随意升级会出现很多不确定因素, 所以我选择手动编译安装 nginx 并使用 --with-openssl 来指定特定版本的 openssl.</p>    <pre>  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    </pre>    <p>这里的 configure 配置仅供参考, 添加的 nginx-ct 模块是为开启Certificate Transperancy 策略做的准备.</p>    <h2>启用双证书</h2>    <p>在启用之前, 先要确保你有对应域名的两张证书, 一张 ECC 证书, 另一张 RSA 证书.</p>    <p>我的 ECC 证书是由 Let's Encrypt 签发的, 使用了最新的 X3 中间证书; RSA 证书则是 AlphaSSL 的泛解析证书.</p>    <p>nginx 配置如下</p>    <pre>  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;    </pre>    <p>ssl_ciphers 中同时添加了对 ECDSA 和 RSA 的支持</p>    <h2>其它策略</h2>    <h3>HPKP</h3>    <p>由于我的 RSA 证书签发者与 ECC 证书的不是同一个, 所以在 HPKP 头中, 需要同时指定两份证书的中间证书.</p>    <pre>  Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="amMeV6gb9QNx0Zf7FtJ19Wa/t2B7KpCF/1n2Js3UuSU="; max-age=2592000; includeSubDomains';    </pre>    <p>如果你的两份证书是由同一个签发者签名的, 只需要指定一次即可, 也就是说完全可以不用修改这项配置, 与单证书的时候保持一致肯定没错.</p>    <h3>HSTS</h3>    <p>恩, 这个不影响.</p>    <h3>CT(Certificate Transperancy)</h3>    <p>前面我编译 nginx 的时候添加了 nginx-ct 模块, 这个模块可以帮助你很快的开启 CT 策略, 具体开启的方法可以参考 qgy 大神的 <a href="http://www.open-open.com/lib/view/open1465269794000.html">这篇文章</a> , 唯一要注意的地方是, 如果你的服务器在天朝, 目前还是无法请求 googleapis 的 CT Logs 服务器的, 所以你可以使用一些特殊姿势来完成, 比如把证书传到国外 VPS 验证后, 再把 sct 文件传回来.</p>    <p>对于开启了双证书的网站, 你只需要保证两本证书的所有 sct 文件都放在一个目录下, 并在 nginx 中配置 ssl_ct_static_scts 为该路径即可, 浏览器会自动识别有效的 CT 信息.</p>    <pre>  ssl_ct on;    ssl_ct_static_scts ssl/sct;    </pre>    <h3>OCSP stapling</h3>    <p>这里其实有个坑, 详细可以看这篇文章: <a href="http://www.open-open.com/lib/view/open1465269623528.html">从无法开启 OCSP Stapling 说起</a></p>    <p> </p>    <p>我先来说结论:</p>    <p>Let's Encrypt 的 OCSP 服务也没有返回 Certificate,所以使用 Let's Encrypt 证书,开启 OCSP Stapling 也无需配置 ssl_trusted_certificate</p>    <p>如果 OCSP Response 包含了 Certificate 信息,并且 Nginx 配置了 ssl_stapling_verify on ,那么需要确保正确配置了  ssl_trusted_certificate 参数,这个参数应该指向一个包含根证书、中间证书的文件(顺序是子证书在上、父证书在下),否则 OCSP Stapling 无法生效。这时候 Nginx 的 error_log 中会出现类似这样的错误:</p>    <p>我的证书链中, ECC 的 let's encrypt 证书无需指定 ssl_trusted_certificate , 也就是说即使指定也不会生效; 而 RSA 的 AlphaSSL 证书需要指定 ssl_trusted_certificate 为一个包含根证书、中间证书的文件.</p>    <pre>  ssl_stapling             on;    ssl_stapling_verify      on;    ssl_trusted_certificate ssl/all.zeroling.com.server-middle-root.crt;    </pre>    <p>于是我的配置如下, 如果你的双证书都是 Let's Encrypt 签发的, 则 ssl_stapling_verify 和  ssl_trusted_certificate 两个配置可以忽略,完全不影响开启 OCSP Stapling。</p>    <h2>测试</h2>    <p>配置完成后, 赶紧到 ssllab 测试一下是否配置正确吧.</p>    <p>在 Handshake Simulation 中, 如果可以看到 EC 和 RSA 同时出现在不同的机器上, 就说明成功了.</p>    <p>这是 <a href="/misc/goto?guid=4959674259079881568" rel="nofollow,noindex">本站的测试结果</a> .</p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959674259171377537" rel="nofollow">http://www.zeroling.com/nginx-kai-qi-https-shuang-zheng-shu-zhi-nan/</a></p>    <p> </p>