1. 背景
因为云认证服务的商户认证体系是基于双向SSL
的,如果服务器端没有启用SSL
证书,则整个认证体系必须重构。目前的现状是申请SSL
证书费用比较高(一年2-3w),而且周期比较长,赶不上目前的测试进度。所以只能使用免费证书来支撑一下了。
免费证书服务提供商据我所知有几个:Let's Encrypt
, StartSSL
, StarCom
。但是StartSSL
, StarCom
前一阵子被Chrome
、Firefox
等设置为不可信(签名流程不规范,不符合安全监管要求之类的原因),因此目前可选的只有let's encrypt
。
Let's Encrypt
是国外一个公共的免费 SSL
项目,由 Linux
基金会托管,它的来头不小,由Mozilla
、思科、Akamai
、IdenTrust
和EFF
等组织发起,目的就是向网站自动签发和管理免费证书,以便加速互联网由 HTTP
过渡到 HTTPS
。
2. 自动化申请原理
Let's Encrypt
每次申请的证书有效期虽然只有90天,但是它支持自动化的申请:整个申请过程不像传统的CA有很长的审批过程,可以做到自动化的申请部署,所以完全能够满足使用的需求。Let's Encrypt
定义了规范ACME
(Automatic Certificate Management Environment)。只要按照规范定义,用户完全可以自己编写程序或脚本实现整个证书申请的过程,成为Agent
。官方有自己的实现:Certbot
。另外还有大量的开源实现,具体可以参考官网。
Let's Encrypt
和ACME
规范的设计目标是能够自动化申请证书,完全不用人工参与。这是通过一个符合ACME
规范Agent
来完成的。
2.1 域名验证
Let's Encrypt
是通过证书来识别一个域名管理者的。第一次运行Agent
的时候,Agent
会生成密钥对,并将公钥提交给Let's Encrypt
。这个过程类似于传统CA
中的在CA
创建商户的过程。
然后通过一些方法,Agent
向Let's Encrypt
证书这个证书的拥有者拥有对指定域名的控制权。这是通过挑战应答方式实现:针对指定域名Let's Encrypt
生成一个挑战值,然后ACME Client
将这个值部署到指定位置,Let's Encrypt
验证部署的值是否正确。ACME
规范中定义了几种验证方法,分别是:
http-01
将应答值保存成文件,并能够通过特定的URL访问到;dns-01
将应答值配置到TXT类型的DNS记录中;
在这个过程中,Agent会使用管理者的私钥对挑战值进行签名,并把签名值存放到指定的位置(文件或DNS记录中)。完成之后Agent通知Let’s Encrypt进行验证。验证之后就可以确认指定证书拥有对指定域名的控制权。这个证书和私钥被称为授权密钥对(authorized key pair)。
2.2 域名申请或吊销
有了授权密钥对之后,具体的申请过程就简单了,只要发送对应的消息给Let’s Encrypt即可。
证书申请时,Agent生成密钥对,并且生成一个PKCS#10 CSR,同时用授权密钥对中的私钥对CSR进行签名,以此证明这次申请是被授权的。Let’s Encrypt收到请求之后验证签名,如果没有问题,就签发证书:
证书吊销与上面类似。Agent对吊销请求进行签名,然后发送请求给Let’s Encrypt完成请求。
3. 申请过程
在实际使用中,我没有使用官方的客户端Certbot
,因为它集成度太高,会自动启动一个服务占用80端口,在实际的生产环境中很难做到这一点。我实际使用过的有两个:acme-tiny.py
和le-dns
。
3.1 acme-tiny.py
acme-tiny.py
是一个Python的脚本。它使用的是http-01
的验证方式,不需要占用端口,通过与http服务器(例如nginx、apache等)协同工作完成验证工作。具体用法可以参考其说明文件。简单说一下优缺点。
优点是脚本很小,完成的功能也足够单一,因此自由度比较高。当你的网站使用Apache或Nginx发布的时候,通过简单的配置就可以使用起来。但是如果你的网站架构比较复杂,例如分布在多台服务器上时,需要负载均衡时,使用acme-tiny就不同合适了。
3.2 le-dns
因为我的网站部署在多台服务器上,需要进行负载均衡,因此使用http-01的验证方法很难完成(需要部署到多台服务器上去)。因此比较合适的验证方法是使用dns-01验证。
要实现自动化的dns-01验证,就需要能够程序化的操控DNS记录,也就需要脚本能够访问DNS服务商的服务,这就需要根据不同的DNS服务商进行定制开发。我一直使用DNSPod的域名服务,而le-dns也支持DNSPod,所以最后选定了它。
3.2.1 安装
le-dns是基于letsencrypt.sh
的,是纯粹的bash脚本,没有其他依赖项。安装起来很简单:
1wget https://github.com/xdtianyu/scripts/raw/master/le-dns/le-dnspod.sh
2wget https://github.com/xdtianyu/scripts/raw/master/le-dns/dnspod.conf
3chmod +x le-dnspod.sh
3.2.2 修改配置
然后修改配置文件dnspod.conf:
1TOKEN="YOUR_TOKEN_ID,YOUR_API_TOKEN"
2RECORD_LINE="默认"
3DOMAIN="example.com"
4CERT_DOMAINS="example.com www.example.com im.example.com"
5#ECC=TRUE
其中的TOKEN是在DNSPod中申请到的API Token(https://www.dnspod.cn/console/user/security),注意格式上分为两部分。 DOMAIN为要申请证书的根域名;CERT_DOMAINS是要申请的域名,可以有多个。
3.2.3 获取证书
运行./le-dnspod.sh dnspod.conf
就可以生成证书了。生成的证书在certs目录下对应的域名目录中。
3.2.4 几点心得
3.2.4.1 证书长度
默认情况下,申请的证书密钥长度为4096,如果想修改成2048,可以修改le-dnspod.sh同目录下的letsencrypt.sh
中的KEYSIZE
参数。
3.2.4.2 在Nginx中的相关配置参考
1server {
2 listen 443;
3 ssl_certificate /etc/nginx/keys/cap.eveus.com/fullchain.pem;
4 ssl_certificate_key /etc/nginx/keys/cap.eveus.com/privkey.pem;
5 ...
6}
注意:ssl_certificate
使用的fullchain.pem。如果使用cert.pem,在某些平台上(例如手机上)会报证书不受信任。
3.2.4.3 授权密钥对
授权密钥对是可以重新生成的。如果以前的密钥对丢失了,可以重新通过工具生成,只要通过了域名控制权验证,那就成为新的授权密钥对了。