申请免费的https证书-Let's Encrypt详细教程
背景
近来,互联网由http
向https
推进的步伐越来越快,除了各大浏览器之外,搜索引擎也特别的优待https
,因此想着跟上步伐把自己的网站也弄成https
。
想要弄成https
,ssl证书
是个绕不过去的坎,各大CA机构的证书都价格不菲,要找个免费又受各大浏览器信任的证书着实不易。
本来考虑StartSSL
是个不错的选择,但是最近StartSSL
因为自身的不规范操作遭到了各大浏览器的封杀,只能抛弃。
通过筛选之后,发现Let's Encrypt
是最好的选择,唯一的缺点是证书的有效期只有3个月,不过这可以通过自动更新来解决。以下摘自网上关于Let's Encrypt
的介绍:
Let's Encrypt 是免费、自动化、开放的证书签发服务。它由 ISRG(Internet Security Research Group,互联网安全研究小组)提供服务,而 ISRG 是来自于美国加利福尼亚州的一个公益组织。Let's Encrypt 得到了 Mozilla、Cisco、Akamai、Electronic Frontier Foundation 和 Chrome 等众多公司和机构的支持,发展十分迅猛。
本人对于ssl证书
及Let's Encrypt
并没有什么研究,试用了不少号称自动化申请的脚本,但都失败,本文是在踩过坑后最终成功的步骤记录。
基本照搬于此文:https://imququ.com/post/letsencrypt-certificate.html
前提及准备工作
以下的所有操作最好都是在实际绑定域名指向的服务器上进行,因为这期间会通过域名指向进行域名的所有权验证,以及以后的证书自动更新也都会进行验证,因此这是最直接方便的选择。
操作系统
操作系统为Linux
,网上给的方案和脚本也都是针对linux
,本人用的CentOS
。
配置验证的http服务
本人使用nginx
,以nginx
为例,其它的原理都一样。
CA 在签发 DV(Domain Validation)证书时,需要验证域名所有权。Let's Encrypt
的验证方式是在你的服务器上生成一个随机的验证文件,在创建 CSR 时通过指定的域名进行访问,如果访问成功则表明你对这个域名有控制权。
具体访问的路径为:http://www.dexcoder.com/.well-known/acme-challenge/xxxxxxxxxx
最后的部分为随机生成的验证文件,因此需要把生成随机文件的目录映射成该路径:
server{ listen 80; server_name www.dexcoder.com; location ^~ /.well-known/acme-challenge/ { alias /opt/www/dexcoder/challenges/; } location / { return 301 https://$server_name$request_uri; }}
创建帐号
新建一个ssl目录,之后所有的操作及生成的证书文件等都在该目录。
进入这个目录,创建一个 RSA 私钥用于 Let's Encrypt 识别你的身份:
openssl genrsa 4096 > account.key
创建 CSR 文件
接着就可以生成 CSR(Certificate Signing Request,证书签名请求)文件了。在这之前,还需要创建域名私钥(一定不要使用上面的账户私钥),根据证书不同类型,域名私钥也可以选择 RSA 和 ECC 两种不同类型。以下两种方式请根据实际情况二选一。
1)创建 RSA 私钥(兼容性好):
openssl genrsa 4096 > domain.key
2)创建 ECC 私钥(部分老旧操作系统、浏览器不支持。优点是证书体积小):
#secp256r1openssl ecparam -genkey -name secp256r1 | openssl ec -out domain.key#secp384r1openssl ecparam -genkey -name secp384r1 | openssl ec -out domain.key
有了私钥文件,就可以生成 CSR 文件了。在 CSR 中推荐至少把域名带 www 和不带 www 的两种情况都加进去,其它子域可以根据需要添加(目前一张证书最多可以包含 100 个域名):
openssl req -new -sha256 -key domain.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:dexcoder.com,DNS:www.dexcoder.com")) > domain.csr
执行这一步时,如果提示找不到 /etc/ssl/openssl.cnf 文件,请看看 /usr/local/openssl/ssl/openssl.cnf 是否存在。
我在执行这一步时以上两处都没有该文件,但已经安装了openssl照理说应该存在才对,使用find命令搜索:
[root@iZ28siayqz6Z ~]# find / -name openssl.cnf/etc/pki/tls/openssl.cnf
终于发现 /etc/pki/tls/openssl.cnf ,如果还是不行,也可以使用交互方式创建 CSR(需要注意 Common Name 必须为你的域名):
openssl req -new -sha256 -key domain.key -out domain.csr
获取网站证书
下载acme-tiny脚本:
wget https://raw.githubusercontent.com/diafygi/acme-tiny/master/acme_tiny.py
执行脚本,指定账户私钥、CSR 以及验证目录(前面nginx配置的验证目录):
python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /opt/www/dexcoder/challenges/ > ./signed.crt
报出如下错误:
Traceback (most recent call last): File "acme_tiny.py", line 2, in <module> import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, loggingImportError: No module named argparse
这是因为centOS
本身的包都比较老旧,Python
还是2.6.x版本,acme-tiny
脚本需要2.7以上版本才行。
下载2.7.8,解压并编译安装:
# wget http://python.org/ftp/python/2.7.8/Python-2.7.8.tgz# tar xvf Python-2.7.8.tgz# cd Python-2.7.8# ./configure --prefix=/usr/local/python27# make && make install
这里安装到了/usr/local/python27
目录,我并没有也不想改变系统默认的python版本,因此显示指定python版本执行:
/usr/local/python27/python acme_tiny.py --account-key ./account.key --csr ./domain.csr --acme-dir /opt/www/dexcoder/challenges/ > ./signed.crt
又报出:
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/python27/lib/python2.7/urllib.py", line 86, in urlopen return opener.open(url) File "/usr/local/python27/lib/python2.7/urllib.py", line 204, in open return self.open_unknown(fullurl, data) File "/usr/local/python27/lib/python2.7/urllib.py", line 216, in open_unknown raise IOError, ('url error', 'unknown url type', type)IOError: [Errno url error] unknown url type: 'https'
这是因为在编译安装python时系统中没有openssl
库,安装:
sudo yum install openssl-devel
重新编译安装python:
./configure --prefix=/usr/local/python27# make && make install
再次执行脚本,成功。当前目录下生成一个 signed.crt,这就是申请好的证书文件。
如果你的域名在国外,DNS解析放在国内,这一步很可能会遇到类似这样的错误:
> ValueError: Wrote file to /home/xxx/www/challenges/oJbvpIhkwkBGBAQUklWJXyC8VbWAdQqlgpwUJkgC1Vg, but couldn't download http://www.yoursite.com/.well-known/acme-challenge/oJbvpIhkwkBGBAQUklWJXyC8VbWAdQqlgpwUJkgC1Vg
这是因为你的域名很可能在国外无法解析,可以找台国外 VPS 验证下。
搞定网站证书后,还要下载 Let's Encrypt 的中间证书。
在 Nginx 配置中,需要把中间证书和网站证书合在一起:
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pemcat signed.crt intermediate.pem > chained.pem
为了后续能顺利启用 OCSP Stapling,我们再把根证书和中间证书合在一起:
wget -O - https://letsencrypt.org/certs/isrgrootx1.pem > root.pemcat intermediate.pem root.pem > full_chained.pem
最终,修改 Nginx 中有关证书的配置并 reload 服务即可:
ssl_certificate /opt/www/dexcoder/ssl/chained.pem;ssl_certificate_key /opt/www/dexcoder/ssl/domain.key;
配置nginx http 301跳转到https
其实前面验证服务中已经给出配置了:
location / { return 301 https://$server_name$request_uri;}
证书自动更新脚本
Let's Encrypt 签发的证书只有 90 天有效期,推荐使用脚本定期更新。例如我就创建了一个 renew_cert.sh 并通过 chmod a+x renew_cert.sh 赋予执行权限。文件内容如下:
#!/bin/bashcd /home/xxx/www/ssl/python acme_tiny.py --account-key account.key --csr domain.csr --acme-dir /home/xxx/www/challenges/ > signed.crt || exitwget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pemcat signed.crt intermediate.pem > chained.pemservice nginx reload
crontab 中使用绝对路径比较保险,crontab -e 加入以下内容:
0 0 1 * * /home/xxx/shell/renew_cert.sh >/dev/null 2>&1
这样以后证书每个月都会自动更新,一劳永逸。实际上,Let's Encrypt 官方将证书有效期定为 90 天一方面是为了更安全,更重要的是鼓励用户采用自动化部署方案。
最后
实测 Let's Encrypt 的证书在各大系统及浏览器中都没发现任何问题,兼容性还是很不错的。