Nginx实现ECC RSA双证书

ECC证书具有密钥短、体积小、加密强度高的优点。384位ECC密钥即可达到RSA 7680位的效果。但是,强大的加密功能,带来的缺点就是兼容性的损害。由于ECC的推出很晚,Windows XP上只有Firefox才能访问使用ECC的网站(ff的SSL是自己实现,不依赖系统),而Android更是只有4+才能访问。如果网站还有这些系统的用户,上ECC就是得不偿失了。(PS:其实对于我而言,主要是学校电脑基本都是XP,万一要用自己网站上的东西,能被坑死……

然而,国内证书商沃通(Wosign)却鼓吹他家的ECC“支持XP及Android 2”,这是为什么呢?其实,他的证书使用的技术手段,也是目前ECC兼容低版本系统的方法——双证书(Dual-Certifcates)。

双证书,即在Web Server中同时存在两条证书链。在TLS握手时,根据Client Hello来决定发送哪条证书链所包含的证书。因此,双证书实际上不是使ECC“兼容”了低版本,而是对于低版本系统自动发送RSA证书。

之前,双证书只能被Apache支持。不过,在nginx的最新版——1.11.0中,内置了双证书功能。

编译nginx-1.11.0

编译请参照这篇文章

在配置文件中启用双证书

如何配置双证书呢?首先,我们需要两条证书链,即必须分别拥有一个RSA证书和一个ECC证书。个人站点推荐let’s encrypt,中间证书已经被XP信任。以我个人配置为例,我写了两个自动更新脚本:

#!/bin/bash

cd /root/ssl
python acme_tiny.py --account-key account.key --csr n4l.pw.csr --acme-dir /home/wwwroot/challenges/ > signed.crt || exit
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > n4l.pw.chained.pem
cd /root/ssl/ct-submit-1.0.0
./ct-submit-1.0.0 ct.googleapis.com/aviator </root/ssl/n4l.pw.chained.pem > /root/ssl/scts/n4l.pw.ecc.sct
service nginx reload

#!/bin/bash

cd /root/ssl/rsas
python acme_tiny.py --account-key account.key --csr n4l.pw.rsa.csr --acme-dir /home/wwwroot/challenges/ > signed.crt || exit
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > n4l.pw.rsa.chained.pem
cd /root/ssl/ct-submit-1.0.0
./ct-submit-1.0.0 ct.googleapis.com/aviator </root/ssl/rsas/n4l.pw.rsa.chained.pem > /root/ssl/scts/n4l.pw.rsa.sct
service nginx reload

注意两个和CT有关的行。如果你不使用Certficate transperancy策略,则无须使用。关于这个策略对双证书的兼容性,下一节涉及。

Nginx-1.11.0的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).
也就是说,用于指定证书及私钥的两条指令,可以使用多次,从而指定多个证书链。

那么,在站点配置文件中,只需如此设置:

ssl_certificate /path/to/ecc;
ssl_certificate_key /path/to/ecc_key;
ssl_certificate /path/to/rsa;
ssl_certificate_key /path/to/rsa_key;

并且,需要注意的是,Cipher Suites的配置中需要包含ECDSA和RSA的Cipher Suites,否则RSA证书将不能正常打开网站(握手会失败)。

一些其他策略对于双证书的支持

1.Certificate Transperancy策略需要额外配置(nginx-ct模块开启方式)
2016-8-17更新

最新的nginx-ct模块支持对每个证书单独发送sct信息。要支持这个功能,需要使用git clone得到最新的nginx-ct模块源码,然后静态编译至nginx即可。使用时可以这样:

ssl_ct on;

ssl_certificate /path/to/rsa.pem;
ssl_certificate_key /path/to/rsa.key;
ssl_ct_static_scts /path/to/rsa/scts;

ssl_certificate /path/to/ecdsa.pem;
ssl_certificate_key /path/to/ecdsa.key;
ssl_ct_static_scts /path/to/ecdsa/scts;

参考:https://github.com/grahamedgecombe/nginx-ct
2.HPKP策略

本站的HPKP头中,使用的pin-sha256为le中间证书指纹。而且我的双证书皆为le签发。如果你的情况和我一样(HPKP使用中间证书指纹,双证书都在同一签发者签发),则HPKP不受任何影响)

如果你的双证书在不同签发商签发,则需要如此修改HPKP头:

Public-Key-Pins 'pin-sha256="ECC intermediate sha256";pin-sha256="RSA intermediate sha256";max-age=2592000; includeSubDomains'

我们知道,HPKP头本身至少需要2个sha-256指纹,分别作为主、备使用。所以,至少需要将备用证书指纹换成RSA证书签发者证书的指纹。你也可以指定更多的指纹备用。

 

至此,双证书配置完成。快找一台XP电脑试试效果吧~(注:这么做只能保证在密钥交换这一环节支持XP,如果你还使用了一些新特性,例如SNI扩展,那么在XP上的古代浏览器(IE8等),也是无法打开的)