跳到主要内容

自己创建 CA,并用 CA 为各服务签发证书

这篇文章是基于 常用 OpenSSL 命令 的一个补充,主要是介绍如何自己创建 CA,并用 CA 为各服务签发证书。

生成 CA 私钥

openssl ecparam -name prime256v1 -genkey -out ca.key

生成 CA 证书

为了让根证书的信息看起来漂亮一点,参考了几个有名的根证书,使用了 OrganizationOrganizational UnitCommon Name 这几个字段。 把这几个字段写进一个配置文件(这里命名为 ca.conf),后面生成 CA 证书时会用到。

[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = v3_req

[ req_distinguished_name ]
OU = My Service, Inc.
O = My Service, Inc.
CN = My Service Root CA

[ v3_req ]
keyUsage = keyCertSign, cRLSign
basicConstraints = critical,CA:true
  • 因为私钥是ECDSA算法生成的,所以[req]段下面的default_bits = 2048就不起作用了,可以不写。
  • keyUsage:keyUsage 扩展定义了证书的公钥可以用于哪些操作。对于根证书和中间CA证书,以下两个值通常是必须的:
    • keyCertSign:证明证书的公钥可以用来签署其他证书。这是CA证书的关键功能,因为CA的主要作用就是签发证书。
    • cRLSign:证明证书的公钥可以用来签署证书撤销列表(CRL)。CRL是由CA发布的列表,包含了被撤销(不再有效)的证书序列号。
  • basicConstraints:basicConstraints 是一个关键扩展,它指示证书是否是CA证书以及是否有权签发其他证书。
    • CA:true:指示证书是CA证书,并且可以用来签发其他证书。
    • critical:这个关键字表示这个扩展是“关键的”,意味着如果证书使用者或者验证证书链的软件不能理解这个扩展,那么应该拒绝这个证书。对于 basicConstraints,这是非常重要的,因为它定义了证书的基本信任结构。

然后用这个配置文件生成 CA 证书。

openssl req -new -x509 -key ca.key -out ca.crt -days 3650 -sha256 -config ca.conf
  • -sha256:指定签名时使用的摘要算法(新版 OpenSSL 默认就是 SHA-256,写上是为了明确)。
  • 网上的例子里常见的 -nodes(不加密私钥)在这里不需要:它只在让 openssl req 顺便生成私钥(-newkey)时才有意义,这里私钥已经单独生成好了。

生成服务私钥

openssl ecparam -name prime256v1 -genkey -out server.key

生成服务证书请求(CSR)

openssl req -new -key server.key -out server.csr

不加 -config 时,OpenSSL 会以交互方式询问 Common Name 等信息。

注意:Chrome 等现代浏览器校验证书时只认 SAN(Subject Alternative Name),不再看 Common Name。所以只要证书是给浏览器访问的服务用的,就应该配置 SAN,而不只是「多个域名」的场景才需要。SAN 配置文件(san.cnf)的写法参考 常用 OpenSSL 命令,然后在生成 CSR 时加上 -config 选项。

openssl req -new -key server.key -out server.csr -config san.cnf

用自己的 CA 为服务签发证书

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256

需要 SAN 时有一个很容易踩的坑:openssl x509 -req 默认不会把 CSR 里的扩展复制进证书。也就是说即使上一步的 CSR 里已经带了 SAN,直接用上面的命令签出来的证书里也没有 SAN。签发时必须用 -extfile-extensions 把扩展再指定一遍(req_extsan.cnf 里扩展段的名字)。

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile san.cnf -extensions req_ext
  • -CAcreateserial:序列号文件(ca.srl)不存在时自动创建。CA 用它给每张签出的证书分配唯一的序列号。
  • OpenSSL 3.0 起也可以用 -copy_extensions copy 直接复制 CSR 里的扩展,代替 -extfile/-extensions

签完可以验证一下证书链和 SAN 是否正确:

openssl verify -CAfile ca.crt server.crt
openssl x509 -in server.crt -noout -text | grep -A 1 "Subject Alternative Name"

关于 398 天有效期限制

从 2020 年 9 月 1 日起,Apple 和 Google 等厂商不再信任有效期超过 398 天(大约 13 个月)的新签发 SSL/TLS 证书,超过会触发 NET::ERR_CERT_VALIDITY_TOO_LONG 错误。

不过这个限制只针对系统预装(公共信任)的根 CA 签发的证书。像本文这样自己创建、再手动添加到信任列表的 CA 不受影响——Apple 的官方说明里明确写了 "This change will not affect certificates issued from user-added or administrator-added Root CAs"。所以上面给服务证书设置 -days 3650 是没有问题的。