2004 年,我使用 Linux 上的 OpenSSL 和 OpenVPN 提供的简单管理脚本建立了一个小型认证机构。根据当时找到的指南,我将根 CA 证书的有效期设置为 10 年。从那时起,我为 OpenVPN 隧道、网站和电子邮件服务器签署了许多证书,它们的有效期也均为 10 年(这可能是错误的,但我当时并不知道更好的做法)。
我发现了很多关于设置 CA 的指南,但关于其管理的信息却很少,特别是关于根 CA 证书到期时该做什么的信息,这将在 2014 年的某个时候发生。所以我有以下问题:
- 有效期在根 CA 证书到期后继续延长的证书,是否会在根 CA 证书到期后立即失效,还是会继续有效(因为它们是在 CA 证书有效期内签名的)?
- 需要执行哪些操作来更新根 CA 证书并确保其到期后顺利过渡?
- 我是否可以以某种方式以不同的有效期重新签署当前根 CA 证书,并将新签署的证书上传到客户端,以便客户端证书仍然有效?
- 或者我是否需要用新的根 CA 证书签名的新证书替换所有客户端证书?
- 根 CA 证书应何时更新?即将到期,还是到期前的合理时间?
- 如果根 CA 证书的更新成为一项主要工作,那么我现在可以做些什么来确保下次更新时能够更顺利地过渡(当然,除了将有效期设置为 100 年)?
情况稍微复杂一些,因为我只能通过使用当前 CA 证书签名的证书的 OpenVPN 隧道来访问某些客户端,所以如果我必须替换所有客户端证书,我将需要将新文件复制到客户端,重新启动隧道,祈祷它随后出现。
答案1
在您的根 CA 上保留相同的私钥可使所有证书继续针对新根证书成功验证;您所需要做的就是信任新的根证书。
证书签名关系基于私钥签名;在生成新的公共证书时保留相同的私钥(当然,公钥也一样),并根据需要更改新的有效期和任何其他新属性,这样就可以保持信任关系。CRL 也可以从旧证书延续到新证书,因为它们和证书一样,都是由私钥签名的。
那么,我们来验证一下吧!
创建根 CA:
openssl req -new -x509 -keyout root.key -out origroot.pem -days 3650 -nodes
从中生成子证书:
openssl genrsa -out cert.key 1024
openssl req -new -key cert.key -out cert.csr
签署子证书:
openssl x509 -req -in cert.csr -CA origroot.pem -CAkey root.key -create_serial -out cert.pem
rm cert.csr
一切就绪,证书关系正常。让我们验证一下信任:
# openssl verify -CAfile origroot.pem -verbose cert.pem
cert.pem: OK
好的,现在假设已经过去了 10 年。让我们从相同的根私钥生成一个新的公共证书。
openssl req -new -key root.key -out newcsr.csr
openssl x509 -req -days 3650 -in newcsr.csr -signkey root.key -out newroot.pem
rm newcsr.csr
而且..它有效吗?
# openssl verify -CAfile newroot.pem -verbose cert.pem
cert.pem: OK
但是..为什么?它们是不同的文件,对吧?
# sha1sum newroot.pem
62577e00309e5eacf210d0538cd79c3cdc834020 newroot.pem
# sha1sum origroot.pem
c1d65a6cdfa6fc0e0a800be5edd3ab3b603e1899 origroot.pem
是的,但这并不意味着新公钥在加密上与证书上的签名不匹配。不同的序列号,相同的模数:
# openssl x509 -noout -text -in origroot.pem
Serial Number:
c0:67:16:c0:8a:6b:59:1d
...
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
d7:a3:66:0a:45:bd:0e:cd:9d
# openssl x509 -noout -text -in newroot.pem
Serial Number:
9a:a4:7b:e9:2b:0e:2c:32
...
RSA Public Key: (1024 bit)
Modulus (1024 bit):
00:bd:56:b5:26:06:c1:f6:4c:f4:7c:14:2c:0d:dd:
3c:eb:8f:0a:c0:9d:d8:b4:8c:b5:d9:c7:87:4e:25:
8f:7c:92:4d:8f:b3:cc:e9:56:8d:db:f7:fd:d3:57:
1f:17:13:25:e7:3f:79:68:9f:b5:20:c9:ef:2f:3d:
4b:8d:23:fe:52:98:15:53:3a:91:e1:14:05:a7:7a:
9b:20:a9:b2:98:6e:67:36:04:dd:a6:cb:6c:3e:23:
6b:73:5b:f1:dd:9e:70:2b:f7:6e:bd:dc:d1:39:98:
1f:84:2a:ca:6c:ad:99:8a:fa:05:41:68:f8:e4:10:
d7:a3:66:0a:45:bd:0e:cd:9d
让我们进一步验证它是否在现实世界的证书验证中发挥作用。
启动一个 Apache 实例,让我们开始吧(debian 文件结构,根据需要调整):
# cp cert.pem /etc/ssl/certs/
# cp origroot.pem /etc/ssl/certs/
# cp newroot.pem /etc/ssl/certs/
# cp cert.key /etc/ssl/private/
我们将VirtualHost
在 443 上监听设置这些指令 - 请记住,根证书在生成和签名newroot.pem
时甚至不存在。cert.pem
SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/newroot.pem
我们来看看 openssl 是如何看待它的:
# openssl s_client -showcerts -CAfile newroot.pem -connect localhost:443
Certificate chain
0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIICHzCCAYgCCQCapHvpKw4sMjANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJB
...
-----END CERTIFICATE-----
(this should match the actual contents of newroot.pem)
...
Verify return code: 0 (ok)
好的,那么使用 MS 加密 API 的浏览器怎么样?首先必须信任根,然后一切就都好了,使用新根的序列号:
而且,我们仍然应该使用旧的根。切换 Apache 的配置:
SSLEngine on
SSLCertificateFile /etc/ssl/certs/cert.pem
SSLCertificateKeyFile /etc/ssl/private/cert.key
SSLCertificateChainFile /etc/ssl/certs/origroot.pem
在 Apache 上进行完全重启,重新加载将无法正确切换证书。
# openssl s_client -showcerts -CAfile origroot.pem -connect localhost:443
Certificate chain
0 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=server.lan
i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
i:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=root
-----BEGIN CERTIFICATE-----
MIIC3jCCAkegAwIBAgIJAMBnFsCKa1kdMA0GCSqGSIb3DQEBBQUAMFQxCzAJBgNV
...
-----END CERTIFICATE-----
(this should match the actual contents of origroot.pem)
...
Verify return code: 0 (ok)
而且,使用 MS crypto API 浏览器时,Apache 会显示旧根,但新根仍在计算机的受信任根存储中。它会自动找到它并根据受信任(新)根验证证书,尽管 Apache 显示的链(旧根)不同。从受信任根中剥离新根并添加原始根证书后,一切正常:
就这样!续订时保留相同的私钥,换入新的受信任根,一切就完成了刚刚起作用。 祝你好运!
答案2
我注意到原始 CA 密钥的更新证书中可能缺少 CA 扩展。这对我来说更合适(它创建了一个./renewedselfsignedca.conf
定义 v3 CA 扩展的地方,并且ca.key
和ca.crt
被假定为原始 CA 密钥和证书):
openssl x509 -x509toreq -in ca.crt -signkey ca.key -out \
renewedselfsignedca.csr
echo -e "[ v3_ca ]\nbasicConstraints= CA:TRUE\nsubjectKeyIdentifier=
hash\nauthorityKeyIdentifier= keyid:always,issuer:always\n" > \
renewedselfsignedca.conf
openssl x509 -req -days 1095 -in renewedselfsignedca.csr -signkey \
ca.key -out renewedselfsignedca.crt \
-extfile ./renewedselfsignedca.conf \
-extensions v3_ca
答案3
基本模式延长根有效期(需要公共 X.509 和相关私钥):
从公钥 X.509 和私钥生成 CSR:
openssl x509 -x509toreq -in XXX.crt -signkey XXX.key -out XXX.csr
使用私钥重新签署CSR:
openssl x509 -in XXX.csr -out XXX.crt -signkey XXX.key -req -days 365
答案4
当您的根证书过期时,您用它签名的证书也会过期。您必须生成新的根证书并用它签名新的证书。如果您不想每隔几年重复这个过程,唯一的选择就是将根证书的有效期延长 10 年或 20 年:我为自己使用的根证书设定了 20 年。
您无法“更新”根证书。您所能做的就是生成一个新的。
至少在旧证书到期前一两年生成新证书,这样如果出现问题,您就有时间进行切换,而不会陷入时间困境。这样,您可以随时暂时切换回旧证书,直到解决新证书的初期问题。
就 VPN 隧道而言,我会设置几个测试平台服务器进行实验,以便您在使用客户端机器执行操作之前准确了解要做什么。