4. 自签名证书
说实话,很多团队在开发环境里用自签名证书,图的就是快。但你真去生产环境也这么干?那我就要劝你慎重了。不过话说回来,理解自签名证书的完整流程,能帮你把TLS的信任链看得更透。
我自己带项目的时候,要求每个新人都亲手走一遍这个流程。为什么?因为只有亲手创建过根CA、签发过服务器证书,你才能真正理解“信任”二字在TLS里是怎么传递的。
整体思路:先有根,再有枝
自签名证书体系,说白了就是一个迷你CA。你既是CA又是证书使用者。流程分三步:
- 创建根CA证书 – 这是信任的起点,自己签名自己。
- 用根CA签发服务器证书 – 生成CSR,让根CA给它签名。
- 把根CA证书导入浏览器 – 浏览器信任根CA,顺便也信任它签发的服务器证书。
嗯,核心逻辑就这些。下面咱们一步一步来。
第一步:创建根CA证书
根CA证书是信任的锚点。我习惯用OpenSSL来做,因为它几乎无处不在。先准备一个配置文件,帮我们组织好证书信息。
以下是我常用的步骤:
- 生成RSA私钥(4096位,别省)
- 创建自签名根证书 – 签发者就是自己
核心命令:
# 1. 生成根CA私钥
openssl genrsa -out ca.key 4096
# 2. 使用私钥自签名根证书(有效期10年)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyLab/CN=My Root CA" \
-out ca.crt
你看,这里用了 -x509 标志,意思是直接生成自签名证书,而不是CSR。CN(通用名称)我写了“My Root CA”,你可以改成任何标识。但记住——浏览器最终会根据这个CN来判断是否匹配,所以你最好写一个自己能记住的名字。
个人习惯:我把根CA的有效期设成10年。生产环境我建议3-5年就够了,毕竟密钥泄露风险。但我自己测试环境懒,10年省事。你想想看,开发机上的证书过期了,又得重新签一堆,麻烦。
第二步:用根CA签发服务器证书
现在我们有了根CA,接下来要给具体的服务器(比如你的本地Web站点)签发证书。这一步需要先生成一个CSR(证书签名请求),然后交给根CA去签名。
2.1 生成服务器私钥和CSR
# 生成服务器私钥(2048位够用,你也可以用4096)
openssl genrsa -out server.key 2048
# 生成CSR
openssl req -new -key server.key -sha256 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyLab/CN=localhost" \
-out server.csr
这里有个坑——CN必须和你访问服务器时用的域名或IP一致。比如你用 https://localhost 访问,CN就写 localhost;如果你用 https://192.168.1.10,CN就写 192.168.1.10。我曾经有个同事,CN写了公司域名,本地测试时一直报证书不匹配,查了半天才发现是CN写成了 example.com,但实际访问的是 localhost。嗯,教训就是:别偷懒,写对。
2.2 用根CA给CSR签名
这里我们需要一个扩展配置文件,用来指定证书用途。自签名证书如果不加扩展,浏览器可能会报“证书用途无效”。
创建扩展文件(v3.ext):
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = 127.0.0.1
IP.1 = 127.0.0.1
然后执行签名:
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 \
-extfile v3.ext
执行完你会得到 server.crt(服务器证书)和 ca.srl(序列号文件,CA内部管理用)。到此,你的服务器证书已经由根CA签发了。
第三步:在浏览器中信任自签名证书
浏览器可不会自动信任你的根CA。你需要手动把 ca.crt 导入到“受信任的根证书颁发机构”中。不同浏览器其实都依赖操作系统的证书存储,所以操作一次就能全局生效(除Firefox外,它有自己的证书库)。
3.1 Windows系统
- 双击
ca.crt→ 选择“安装证书”。 - 选择“本地计算机”(需要管理员权限)。
- 选择“将所有证书放入下列存储” → 浏览 → 选择“受信任的根证书颁发机构”。
- 完成。重启浏览器。
3.2 macOS系统
- 打开“钥匙串访问” → 将
ca.crt拖入“系统”钥匙串。 - 双击该证书 → 展开“信任” → 将“使用此证书时”改为“始终信任”。
- 关闭窗口,输入密码。完成。
3.3 Linux系统(以Ubuntu为例)
sudo cp ca.crt /usr/local/share/ca-certificates/my-root-ca.crt
sudo update-ca-certificates
完事后,重启浏览器,访问你的HTTPS站点,应该能看到小锁标志了。
注意:千万不要把自签名证书的生产根CA私钥泄露给任何人。一旦泄露,攻击者可以签发任何域名的证书。我在项目中见过一次内部工具泄密,不得不紧急吊销整个自签名CA。所以要像保护银行卡密码一样保护你的根CA私钥。
验证证书链
证书安装完后,可以用下面的命令验证服务器证书是否被根CA正确签署:
openssl verify -CAfile ca.crt server.crt
如果输出 server.crt: OK,说明链是完整的。如果报错,八成是SAN(主题备用名称)没写对,或者根证书没导入。
常见证书参数对比
| 参数 | 根CA证书 | 服务器证书 |
|---|---|---|
| 签发者 | 自己(自签名) | 根CA |
| 有效期 | 3-10年(推荐5年) | 1-2年 |
| 密钥长度 | 4096位 | 2048位(也可4096) |
| 用途扩展 | CA:TRUE |
CA:FALSE + SAN |
| 浏览器信任 | 需要手动导入 | 自动信任(根已导入) |
为什么服务器证书有效期短?说白了,私钥泄露风险。服务器证书经常暴露在公网上,可能被提取。根CA的私钥你锁在机器里,泄露概率低很多。所以长有效期给根CA,短有效期给服务器证书,这是安全实践。
避坑指南:我曾经踩过的几个坑
- 漏写SAN:Chrome从58版本开始,必须要有SAN扩展,否则直接报“NET::ERR_CERT_COMMON_NAME_INVALID”。我见过有人调试了一下午,最后发现就是v3.ext里忘了加SAN段。
- 私钥权限:生成私钥后,记得
chmod 600 *.key,不然启动Web服务器时提示“权限太开放”。 - 浏览器缓存:导入根证书后如果还是报错,先清除SSL状态或者重启浏览器。我记得有一次折腾半天,最后发现是浏览器缓存了旧的证书状态。
- Firefox不信:Firefox不使用系统证书存储,需要单独导入。路径:偏好设置 → 隐私与安全 → 证书 → 查看证书 → 导入。
小结一下
自签名证书的流程,说穿了就是:创建根CA → 签发服务器证书 → 信任根CA。你手里的 ca.crt 和 ca.key 是整个信任链的根基,务必保管好。
这套方法在开发测试、内网服务加密、CI/CD环境中非常实用。我自己的实验室里,所有内部服务都跑着自签名证书,省去了申请商业证书的麻烦。当然,线上环境千万别这么干——用户浏览器不会信任你的根CA,你得买正经的CA签发的证书。