自签名证书与自签名CA

以域名 domain.com为例

生成CA

创建 Root Key

可以使用 genrsa 命令创建 Root key,如下创建一个4096位的RSA私钥,并用aes256加密,保存为root/ca.key.pem文件

1
2
mkdir root
openssl genrsa -aes256 -out root/ca.key.pem 4096

创建 Root Cert

这一步就是创建根证书了,需要通过 req 子命令来创建,而且需要一个 Root CA 的 openssl.cnf 配置文件:

1
2
3
4
5
6
7
8
9
openssl req -config root/openssl.cnf \
-key root/ca.key.pem \
-new \
-x509 \
-days 7300 \
-sha256 \
-extensions v3_ca \
-out root/ca.cert.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=AnyName/OU=AnyName/CN=AnyNameCA

验证证书

可以通过x509命令来验证证书的有效性

1
openssl x509 -noout -text -in root/ca.cert.pem

创建中间证书

前面我们已经拥有了 Root Pair,事实上已经可以用于证书的发放了,但是由于根证书很干净,特别容易被污染,所以我们需要创建中间 Pair 作为 Root Pair 的代理,生成过程同上,只是细节略微不一样。

创建一个 intermediate 目录用来存放中间 Pair 相关的文件:

1
mkdir intermediate

创建 Intermediate Key

继续使用 genrsa 命令创建 Intermediate Key,即中间CA的私钥

1
openssl genrsa -aes256 -out intermediate/intermediate.key.pem 4096

创建 Intermediate Cert

同样使用 req 子命令来创建 Intermediate Cert,即中间CA证书,这里需要特别注意的一点是 Common Name(subj 参数的 CN 字段) 不要与 Root Pair 的一样,而且也需要一个 Intermediate CA 的 openssl.cnf 配置文件:

1
2
3
4
openssl req -config intermediate/openssl.cnf -new -sha256 \
-key intermediate/intermediate.key.pem \
-out intermediate/intermediate.csr.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=Chenxuan/OU=Chenxuan/CN=ChenxuanInCA

使用 Root CA 给 Intermediate Cert 签名

会依赖两个文件,我们需要先创建一下,然后进行签名:

1
2
3
4
5
6
7
8
9
touch root/index.txt
echo 1000 > root/serial
openssl ca -config root/openssl.cnf \
-extensions v3_intermediate_ca \
-days 7200 \
-notext \
-md sha256 \
-in intermediate/intermediate.csr.pem \
-out intermediate/intermediate.cert.pem

intermediate/intermediate.cert.pem 就是创建的中间CA证书了。

此时 root/index.txt 中将会多出这么一条记录:

1
V	320428133311Z		1000	unknown	...

验证 Intermediate Cert

同样可以通过x509命令来验证证书的有效性,也可以使用根证书来验证中间证书有效性:

1
2
openssl x509 -noout -text -in intermediate/intermediate.cert.pem
openssl verify -CAfile root/ca.cert.pem intermediate/intermediate.cert.pem

创建证书链

浏览器在验证中间证书的时候,同时也会去验证它的上一级证书是否靠谱,创建证书链,将 Root Cert 和 Intermediate Cert 合并到一起,可以让浏览器一并验证:

1
cat intermediate/intermediate.cert.pem root/ca.cert.pem > intermediate/ca-chain.cert.pem

此时两个证书会合并到 intermediate/ca-chain.cert.pem 文件中。

创建服务器/客户端证书

这一步是生成我们服务器上需要部署的内容,Root Pair 和 Intermediate Pair 使用的都是 4096 位的加密方式,一般情况下服务器/客户端证书的过期时间为一年,所以可以安全地使用 2048 位的加密方式。

这里为域名 domain.com 生成证书,先创建一个 domain.com 的文件夹:

1
mkdir domain.com

创建域名私钥

继续使用 genrsa 命令创建 domain.com 这个域名的私钥:

1
openssl genrsa -out domain.com/domain.com.key.pem 2048

创建域名证书

1
2
3
4
5
openssl req -config intermediate/openssl.cnf \
-key domain.com/domain.com.key.pem \
-new -sha256 \
-out domain.com/domain.com.csr.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=AnyName/OU=AnyName/CN=domain.com

使用中间证书签证域名证书

同样会依赖两个文件,需要先创建一下,然后进行签证:

1
2
3
4
5
6
7
8
9
touch intermediate/index.txt
echo 1000 > intermediate/serial
openssl ca -config intermediate/openssl.cnf \
-extensions server_cert \
-days 375 \
-notext \
-md sha256 \
-in domain.com/domain.com.csr.pem \
-out domain.com/domain.com.cert.pem

此时 intermediate/index.txt 中也会多出这么一条记录:

1
V	230511135530Z		1000	unknown	...

验证域名证书

使用 x509 和证书链来验证域名证书:

1
2
openssl x509 -noout -text -in domain.com/domain.com.cert.pem
openssl verify -CAfile intermediate/ca-chain.cert.pem domain.com/domain.com.cert.pem

可以看到以下内容说明证书创建成功:

1
domain.com/domain.com.cert.pem: OK

CA 根证书:ca-chain.cert.pem

网站私钥:domain.com.key.pem

网站公钥:domain.com.cert.pem

脚本生成ssl

指定域名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
domain_name=domain.com
mkdir -p $domain_name
openssl genrsa -out ${domain_name}/${domain_name}.key.pem 2048
openssl req -config <(cat ./intermediate/openssl.cnf \
<(printf "[ alt_names ]\nDNS.1 = ${domain_name}")) \
-key ${domain_name}/${domain_name}.key.pem \
-new -sha256 \
-out ${domain_name}/${domain_name}.csr.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=AnyName/OU=AnyName/CN=${domain_name}

openssl ca -config intermediate/openssl.cnf \
-days 375 \
-notext \
-md sha256 \
-in ${domain_name}/${domain_name}.csr.pem \
-out ${domain_name}/${domain_name}.cert.pem
泛域名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
domain_name=domain.com
mkdir -p _.$domain_name
openssl genrsa -out _.${domain_name}/_.${domain_name}.key.pem 2048
openssl req -config <(cat ./intermediate/openssl.cnf \
<(printf "[ alt_names ]\nDNS.1 = ${domain_name}\nDNS.2 = *.${domain_name}")) \
-key _.${domain_name}/_.${domain_name}.key.pem \
-new -sha256 \
-out _.${domain_name}/_.${domain_name}.csr.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=AnyName/OU=AnyName/CN=${domain_name}

openssl ca -config intermediate/openssl.cnf \
-days 375 \
-notext \
-md sha256 \
-in _.${domain_name}/_.${domain_name}.csr.pem \
-out _.${domain_name}/_.${domain_name}.cert.pem

长期有效证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
domain_name=domain.com
mkdir -p _.$domain_name
openssl genrsa -out _.${domain_name}/_.${domain_name}.key.pem 2048
openssl req -config <(cat ./intermediate/openssl.cnf \
<(printf "[ alt_names ]\nDNS.1 = ${domain_name}\nDNS.2 = *.${domain_name}")) \
-key _.${domain_name}/_.${domain_name}.key.pem \
-new -sha256 \
-out _.${domain_name}/_.${domain_name}.csr.pem \
-subj /C=CN/ST=Shanghai/L=Shanghai/O=AnyName/OU=AnyName/CN=${domain_name}

openssl ca -config intermediate/openssl.cnf \
-days 7300 \
-notext \
-md sha256 \
-in _.${domain_name}/_.${domain_name}.csr.pem \
-out _.${domain_name}/_.${domain_name}.cert.pem

Windows下证书的添加方式

Windows 需要添加 CA根证书 与 中间证书 到系统里面

如果要访问 自签名证书 需要安装 ca.cert.pem 这个证书

双击CA根证书->存储位置选择本地->指定证书位置(受信任的根证书颁发机构)

如果要保证兼容性 需要一并安装 intermediate.cert.pem 这个证书

双击CA根证书->存储位置选择本地->指定证书位置(中间证书颁发机构)

证书吊销的支持

  1. 创建 crlnumber 文件并初始化

进入你的 中间 CA 的目录(即配置文件 intermediate/openssl.cnf 所在的目录),执行:

1
2
cd /path/to/intermediate
echo "00" > crlnumber
  1. 生成CRL文件
1
2
# 使用openssl命令生成CRL文件,并输出到Web服务器的目录
openssl ca -config /path/to/your/openssl.cnf -gencrl -out /var/www/html/crl/crl.pem
  1. 更新签发使用的配置文件

需要在签发证书时使用的OpenSSL配置文件(通常是openssl.cnf)中,找到或添加 [ v3_ca ] 或 [ usr_cert ] 等相关段落,并加入以下配置:

1
2
[ v3_ca ]
crlDistributionPoints = URI:http://your-server.com/crl/crl.pem

将 http://your-server.com/crl/crl.pem 替换为你的CRL文件的实际访问URL。

  • 微软官方文档明确建议:CRL 分发点(CDP)应使用 HTTP 协议,不要使用 HTTPS
  • RFC 5280(X.509 标准)也指出,虽然 LDAP 和 HTTP 都支持,但为了兼容性和避免循环依赖,HTTP 是广泛使用的标准方案。

默认有效期配置

1
2
3
[ CA_default ]
# ... 其他配置 ...
default_crl_days = 7 # 默认有效期 7 天(也可以写 default_crl_hours)
  1. 通过nginx映射crl文件

想办法让 实际访问URL 可以访问就行。

证书使用

Windows 的证书存储机制和浏览器(Chrome/Edge)的证书处理逻辑有一个关键差异:Windows 严格区分“根证书存储区”和“中间证书存储区”,而浏览器通常会自行构建更灵活的缓存链。

如果把根证书和中间证书合并后再全部导入“受信任的根证书颁发机构”,会导致部分程序在校验时“认不出”完整的证书链。

参考