自己签发数字证书
为了解决网站 HTTPS/HTTP2 访问的问题,Let’s Encrypt 提供了免费数字证书,可以很方便地进行申请和部署。但有一些特殊情况仍然需要进行自签证书,比如:
- 在本地搭建开发测试环境(包含网站或 APP 签名等)
- 制作 HTTPS 网站的部分内容镜像(比如镜像 docs.python.org 网站的部分内容)。
自签证书的方法大致有两种。第一种方法是直接在服务器上签发证书。这种方法的优点是操作简单,缺点是不能为使用 HSTS 的网站制作镜像,必须为每个不同的证书单独导入客户端/浏览器。此外还可以在服务器上搭建 CA,然后通过用这个 CA 签发证书。这种方法的优点是功能没有被阉割,缺点是初始化操作繁琐。
本文介绍第二种方法,即先搭建 CA, 然后再由这个 CA 签发证书。操作系统环境是 Ubuntu 16.04 LTS。
0. 准备工作
证书存放在 /etc/ssl/demoCA
中。这个目录的结构如下:
demoCA/
|--private/ 存放 CA 密钥。安全原因,这个目录的权限为 0700
| \...
|--newcerts/ 由 CA 签发的证书的备份
| \...
|--index.txt 完成签发的证书记录
+--serial 每次签发证书时使用序列号,签发成功后会自动增加
首先,修改 /etc/ssl/openssl.cnf
...
[ CA_default ]
dir = /etc/ssl/demoCA
unique_subject = no
copy_extensions = copy
policy = policy_anything
...
[ req ]
req_extensions = v3_req
...
[ v3_req ]
subjectAltName = @alternate_names
[ alternate_names ]
# 手工增加此“节”。多个域名可以存放到此“节”,此后一个证书可以用于多个域名。
# DNS.0001 = host1.example.com
# DNS.0002 = host2.example.com
DNS.9000 = 127.0.0.1
DNS.9001 = localhost
DNS.9001 = localhost.localdomain
DNS.9003 = ::1
...
然后,创建目录结构,并初始化内容。
sudo mkdir -p /etc/ssl/demoCA/private \
/etc/ssl/demoCA/newcerts \
/etc/nginx/certs \
/etc/nginx/snippets
sudo chmod 0700 /etc/ssl/demoCA/private
sudo touch /etc/ssl/demoCA/index.txt
echo 01 | sudo tee /etc/ssl/demoCA/serial >/dev/null
1. 自建 CA 服务器
1.1. 生成自建 CA 密钥
sudo openssl genrsa -out /etc/ssl/demoCA/private/cakey.pem 2048
1.2. 生成自建 CA 证书
sudo openssl req -new -x509 -days 3652 \
-key /etc/ssl/demoCA/private/cakey.pem \
-out /etc/ssl/demoCA/cacert.pem \
-subj '/O=Leenware Inc/CN=Leenware Self-signed Authority'
参数 -subj
的内容使用 /
作为开始以及字段的间隔。O=
表示签发证书的单位名称,在生成 CA 证书时 CN=
用于表示证书的通用名称。当然还可以根据 RFC 4514 的定义增加其他字段。
可以使用命令 openssl x509 -in /etc/ssl/demoCA/cacert.pem -text
查看生成的根证书。
2. 导入 CA 证书
2.1. 向系统导入 CA 证书
cat /etc/ssl/demoCA/cacert.pem | sudo tee -a /etc/ssl/certs/ca-bundle.crt >/dev/null
在系统中导入自建 CA 后可以用 curl
直接访问 HTTPS,否则必须在 curl
中使用参数 --cacert /etc/ssl/demoCA/cacert.pem
。
2.2. 向 Firefox 导入 CA 证书
firefox --new-window /etc/ssl/demoCA/cacert.pem
运行上面的命令会打开一个新的 Firefox 窗口,请求确认导入 CA 证书。
3. 在 nginx 中部署 HTTPS/HTTP2
注意:除非特殊说明,以下命令在 Web 服务器上运行。
3.1. 生成 SSL 密钥
sudo openssl genrsa -out /etc/nginx/certs/self-signed.key 2048
3.2. 生成证书签发请求
sudo openssl req -new \
-key /etc/nginx/certs/self-signed.key \
-out /etc/nginx/certs/self-signed.csr \
-subj '/O=Leenware Inc/CN=localhost'
-subj
参见 生成自建 CA 证书。不同的地方是:生成请求时 CN
字段应填写域名。
可以使用命令 openssl req -in /etc/nginx/certs/self-signed.csr -text
查看生成的证书签发请求。
3.3. 自建 CA 根据请求签发证书
将生成的证书签发请求(文件 self-signed.csr
)复制或上传到 CA 服务器,然后在 CA 服务器运行以下命令:
sudo openssl ca -days 730 \
-in /etc/nginx/certs/self-signed.csr \
-out /etc/nginx/certs/self-signed.crt
可以使用命令 openssl x509 -in /etc/nginx/certs/self-signed.crt -text
查看已完成签发的证书。
将签发成功的证书(文件 self-signed.crt
)复制或下载到 Web 服务器,完成证书签发。
3.4. 创建 nginx 片段(可选)
cat <<EOF | sudo tee /etc/nginx/snippets/self-signed.conf >/dev/null
ssl_certificate certs/self-signed.crt;
ssl_certificate_key certs/self-signed.key;
EOF
使用 nginx 片段可以简化配置过程。在 /etc/nginx/sites-avaliable
目录下配置文件中通过 include snippets/self-signed.conf;
使用这个片段。
4. 重签、新签证书
重复 3. 在 nginx 中部署 HTTPS/HTTP2 的步骤即可。
5. 我后悔了,要清除刚才的操作
sudo python -c 'cacert = open("/etc/ssl/demoCA/cacert.pem", "r").read(); bundle = open("/etc/ssl/certs/ca-bundle.crt", "r").read(); open("/etc/ssl/certs/ca-bundle.crt", "w").write(bundle.replace(cacert, ""))'
sudo rm -rf /etc/ssl/demoCA
sudo rm -rf /etc/nginx/certs/self-signed.*
sudo rm -rf /etc/nginx/snippets/self-signed.conf
注意:Firefox 中导入的 CA 证书需要手工删除。
RFC 4514 支持的字段及含义
String | X.500 AttributeType |
---|---|
CN | commonName (2.5.4.3) |
L | localityName (2.5.4.7) |
ST | stateOrProvinceName (2.5.4.8) |
O | organizationName (2.5.4.10) |
OU | organizationalUnitName (2.5.4.11) |
C | countryName (2.5.4.6) |
STREET | streetAddress (2.5.4.9) |
DC | domainComponent (0.9.2342.19200300.100.1.25) |
UID | userId (0.9.2342.19200300.100.1.1) |