Let's Encrypt免费证书的使用

1. 背景

因为云认证服务的商户认证体系是基于双向SSL的,如果服务器端没有启用SSL证书,则整个认证体系必须重构。目前的现状是申请SSL证书费用比较高(一年2-3w),而且周期比较长,赶不上目前的测试进度。所以只能使用免费证书来支撑一下了。

免费证书服务提供商据我所知有几个:let’s encrypt, startssl, starcom。但是startssl, starcom前一阵子被Google、Mozilla等设置为不可信(签名流程不规范,不符合安全监管要求),因此目前可选的只有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.pyle-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脚本,没有其他依赖项。安装起来很简单:

1
2
3
wget https://github.com/xdtianyu/scripts/raw/master/le-dns/le-dnspod.sh
wget https://github.com/xdtianyu/scripts/raw/master/le-dns/dnspod.conf
chmod +x le-dnspod.sh

3.2.2 修改配置

然后修改配置文件dnspod.conf:

1
2
3
4
5
TOKEN="YOUR_TOKEN_ID,YOUR_API_TOKEN"
RECORD_LINE="默认"
DOMAIN="example.com"
CERT_DOMAINS="example.com www.example.com im.example.com"
#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中的相关配置参考

1
2
3
4
5
6
server {
listen 443;
ssl_certificate /etc/nginx/keys/cap.eveus.com/fullchain.pem;
ssl_certificate_key /etc/nginx/keys/cap.eveus.com/privkey.pem;
...
}

注意:ssl_certificate使用的fullchain.pem。如果使用cert.pem,在某些平台上(例如手机上)会报证书不受信任。

3.2.4.3 授权密钥对

授权密钥对是可以重新生成的。如果以前的密钥对丢失了,可以重新通过工具生成,只要通过了域名控制权验证,那就成为新的授权密钥对了。

附录. 参考资料

热评文章