证书这活儿:PEM/JKS/P12 怎么选、免费证书哪家强、自动轮换怎么搞
Posted on 二 24 3月 2026 in Journal
| Abstract | 证书这活儿:PEM/JKS/P12 怎么选、免费证书哪家强、自动轮换怎么搞 |
|---|---|
| Authors | Walter Fan |
| Category | learning note |
| Version | v1.0 |
| Updated | 2026-03-24 |
| License | CC-BY-NC-ND 4.0 |
短大纲
- 先讲一个我自己犯过的蠢错:个人网站证书忘了续,结果在公司里打不开
- 把 Root CA / Intermediate CA / Leaf Certificate 这条信任链掰开揉碎
- 再讲 PEM / JKS / PKCS#12 三种格式,到底谁装什么、谁给谁看
- 免费证书怎么选,自动轮换怎么做,最后给一张能照抄的检查清单
前几天我在公司里想打开自己的个人网站,结果浏览器直接给我甩了一张红脸:证书校验失败,页面打不开。
这事说起来挺丢人。平时给别人的服务配 TLS,讲起安全来头头是道,轮到自己的站,证书却悄悄过期了。家里电脑上偶尔还能点进去,一到公司网络环境里就不行,浏览器、代理、系统信任链,一层层都比我更较真。它们不像老朋友,不会跟你讲情面,只认日期、签名、链路和策略。
这件事又一次提醒我:证书管理这活儿,看着像“抄个配置,跑个命令”,其实更像值班。平时风平浪静,大家都想不起它;一旦到期、链条断了、格式塞错了,它就会在你最不想处理的时候跳出来,给你上一课。
所以这篇文章我不只想讲“怎么续期”。我想把证书这套东西里最容易糊涂的几层讲透,特别是下面这条链子:
Root CA → Intermediate CA → Leaf Certificate
别小看这三个名字。很多证书事故,表面看是“网站打不开”,骨子里其实就三类:要么 leaf 过期了,要么 intermediate 没带全,要么客户端根本不信任你上游那条根。把这三层搞清楚,再看 PEM、JKS、P12,脑子里就不会是一锅粥。
先搞清楚:证书到底是什么
不扯太多密码学术语,咱们先抓住最要紧的部分。
一张 TLS 证书,本质上是一个 X.509 结构化文档。它不是一张“截图”,也不是“一个公钥文件那么简单”,而是一份带签名的数据结构。里面至少有四类信息:
- 主体信息(Subject):这张证书是谁的。对网站来说,最重要的是域名
- 公钥(Public Key):客户端后面要拿它来协商密钥、验证签名
- 用途与约束:这张证书能干什么,能不能签别人,能不能做服务器认证
- 签发者签名(Issuer Signature):上一级 CA 用自己的私钥给它盖章
所以证书不是“加密算法”,它更像一张带防伪章的介绍信。公钥是介绍信里的联系方式,Subject 是名字,签名是公证处的钢印。
浏览器或操作系统拿到证书后,不是只看“锁有没有变绿”,它其实在背后做一串检查:
- 这张证书是不是发给当前访问的域名
- 现在时间是不是在有效期内
- 这张证书是不是被一个我信任的上级签发
- 上级自己是不是也被更上级签发
- 整条链有没有断,有没有人拿错证书冒名顶替
- 这张证书的用途是不是允许它拿来做 HTTPS 服务器
只要其中一项不对,浏览器就翻脸。它不会想“这个站长平时人还不错”。机器没有人情世故。
把那条链讲透:Root CA、Intermediate CA、Leaf Certificate 到底各管什么
很多人看到这几个词,脑子里有印象,可是一落到排障就开始晕。无他,这几个名字像,文件也像,打开之后还都是一坨 Base64,看久了确实容易眼花。咱们一个一个说。
1. Root CA:信任的起点,但不是天天拿出来抛头露面
Root CA 是整条证书链的祖师爷。
它有几个关键特征:
- 通常是自签名(self-signed):也就是
Subject == Issuer - 长期存在:有效期往往是十几年到几十年
- 不会轻易直接签网站证书
- 预装在系统、浏览器或企业信任仓库里
Root CA 证书里最关键的不是域名,而是“我有资格当 CA”。这通常体现在扩展字段里,比如:
Basic Constraints: CA:TRUEKey Usage: Certificate Sign, CRL Sign
它像银行总行的总印章。平时不拿出来盖每一份业务单据,因为一旦根私钥泄露,整个体系都得翻车。所以现实世界里,Root CA 很少直接签你的网站证书。
Root CA 常见的存在位置:
- 操作系统 trust store
- 浏览器自己的信任库
- Java cacerts
- 企业内网设备或代理注入的自定义根证书
这也解释了一个现象:为什么同一个站,在家里能开,在公司里不一定能开。公司网络里如果有 HTTPS inspection、中间代理、或者额外的企业根证书策略,验证路径和你家里的浏览器就不是同一套了。
2. Intermediate CA:真正天天干活的那层
Intermediate CA 是中间 CA,也叫中级 CA。它是 Root CA 签发出来的“代理掌门”。
它的职责是:
- 替 Root CA 干活,去签发大量业务证书
- 把 Root CA 私钥和日常签发操作隔离开
- 允许 CA 做更细的分层和权限控制
Intermediate CA 的证书长这样理解就行:
Subject: R3
Issuer: ISRG Root X1
Basic Constraints: CA:TRUE
Key Usage: Certificate Sign, CRL Sign
Authority Key Identifier: 指向 Root CA
Subject Key Identifier: 自己的 key 指纹
你会发现它和 Root CA 一样,也有 CA:TRUE,因为它也能签别人。区别在于:
- Root 是信任锚(trust anchor)
- Intermediate 是被 Root 授权出来干活的
这层非常关键。很多线上事故并不是 leaf 证书错了,而是部署的时候只放了 leaf,没把 intermediate 一起带上。浏览器有时靠缓存还能“脑补”出来,curl、移动端 SDK、某些企业代理就没那么好说话了,当场报错。
3. Leaf Certificate:真正挂在你网站门口那张牌子
Leaf Certificate 就是服务器证书,也叫终端证书、站点证书。它是给最终服务用的,不再拿去签别的证书。
它的典型特征:
Basic Constraints: CA:FALSEExtended Key Usage: TLS Web Server AuthenticationSubject Alternative Name (SAN)里放域名- 有较短有效期,比如 90 天
举个例子,我的个人网站证书如果是发给 www.fanyamin.com,那 Leaf Certificate 里你最关心的是:
Subject Alternative Name:
DNS:www.fanyamin.com
DNS:fanyamin.com
Issuer:
Let's Encrypt R3
Not Before / Not After:
2026-01-01 ... 2026-03-31
注意,现在浏览器对域名校验几乎都看 SAN,而不是老式 CN(Common Name)。所以如果证书里没把域名写进 SAN,哪怕别的字段都漂亮,也照样不认。
4. 这三层在文件里通常长什么样
很多人的第二个困惑是:名字懂了,可文件看着还是乱。
拿 PEM 来说,三层证书本质上都是同一种 X.509 证书,只是角色不同。它们在磁盘上经常长这样:
-----BEGIN CERTIFICATE-----
... Leaf Certificate ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... Intermediate CA ...
-----END CERTIFICATE-----
Root CA 往往不由服务器发送,因为客户端本地已经有了。服务器通常发的是:
- 自己的 leaf cert
- 一个或多个 intermediate cert
也就是大家常说的 fullchain.pem。如果你只部署 cert.pem,而没部署 fullchain.pem,浏览器就可能告诉你:“你这封介绍信像是真的,但你上面那个公证处我没接上。”
5. 客户端到底怎么验证这条链
这部分是最容易被“一句话带过”的,可真出了事,恰恰要靠它排查。
客户端在握手时,大体会按这个顺序检查:
第一步:拿到服务器送来的证书链
服务器通常会在 TLS 握手时送出:
- leaf certificate
- intermediate certificate(s)
如果只送 leaf,不送 intermediate,很多客户端就拼不出完整路径。
第二步:验证 leaf 的基本合法性
客户端先看 leaf 本身:
- 有效期是否包含当前时间
- SAN 里有没有当前访问域名
- 签名算法是否还能接受
- Key Usage / Extended Key Usage 是否允许做服务器证书
CA:FALSE是否合理
像我这次个人网站在公司里打不开,最直白的原因就是 leaf 过期了。到期这件事其实没什么讨论空间,Not After 一过,后面全免谈。
第三步:拿 leaf 的 Issuer 去找上一级 intermediate
Leaf 上会写:
IssuerAuthority Key Identifier
客户端会拿这些信息去跟服务器送来的 intermediate 对上,看是不是这位“师父”签的。
然后它会做数学验签:拿 intermediate 的公钥,验证 leaf 上的签名是不是对的。
这一步如果失败,常见原因有:
- 链接错 intermediate 了
- 证书文件顺序不对
- 证书被截断或损坏
第四步:继续往上追到 Root
Intermediate 也不是凭空可信。客户端还会继续验证:
- intermediate 是不是被某个 root 签的
- 这个 root 是不是在本机信任库里
到这里,链子才算闭环。要是走到头发现根不在 trust store 里,那浏览器照样不认。
所以“证书没问题”这句话,往往只说对了一半。你那张 leaf 没问题,不代表整条链就没问题。
第五步:检查约束条件是不是一路都合法
不是说签名对上就完事。客户端还要检查每一层的“权限”:
- 上级是不是
CA:TRUE - 上级是不是有
keyCertSign - path length constraint 有没有超
- leaf 有没有
serverAuth
这就像公司盖章流程。不是说印泥颜色对了就算通过,还得看这个人有没有审批权。
第六步:看吊销状态和额外安全策略
更严格的客户端还会继续看:
- CRL / OCSP 吊销状态
- CT Log / SCT
- 企业网关自己的安全策略
这一步也是为什么“在我电脑上能开”的经验,经常没有普适性。你家里浏览器和公司代理的策略,可能压根不是同一套。
6. 一张图把链路串起来
[Leaf Certificate: www.fanyamin.com]
signed by
[Intermediate CA: Let's Encrypt R3]
signed by
[Root CA: ISRG Root X1]
trusted by
[OS / Browser / Company Trust Store]
这条链只要有一段断掉,结果都一样:你的网站在用户眼里不是“有一点小毛病”,而是“不可信”。
7. 用 openssl 看这三层,最靠谱
纸上谈兵不如直接看证书。
# 看 leaf 证书内容
openssl x509 -in cert.pem -text -noout
# 看 fullchain 里到底塞了几张证书
openssl crl2pkcs7 -nocrl -certfile fullchain.pem | \
openssl pkcs7 -print_certs -text -noout
# 直接连线上服务,看看服务端到底送了什么链
openssl s_client -connect www.fanyamin.com:443 -servername www.fanyamin.com -showcerts
重点看这些字段:
SubjectIssuerNot Before / Not AfterBasic ConstraintsKey UsageExtended Key UsageSubject Alternative Name
你把这几个字段看顺了,证书问题起码能查明白七八成。
PEM / JKS / PKCS#12:三种格式到底差在哪
这是我见过最容易搞混的地方。很多人被 .pem、.crt、.key、.jks、.p12、.pfx 这些后缀搞得头大,其实理清楚只需要记住三个"容器"。
PEM:文本格式,Unix 世界的通用语
PEM 是 Privacy Enhanced Mail 的缩写,但跟邮件早没关系了。它就是 Base64 编码 + 头尾标记:
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhki...
-----END CERTIFICATE-----
特点:
- 纯文本,cat 就能看,diff 能比,git 能追踪
- 一个文件可以串联多张证书(certificate chain)
- 私钥单独存一个
.key文件(也是 PEM 格式) - Nginx、Apache、HAProxy、Go、Python、Node.js 原生支持
PEM 是最"透明"的格式。你几乎可以用记事本编辑它——虽然没人推荐这么干。
JKS:Java 生态的老朋友
JKS 是 Java KeyStore,Java 自己搞的一种二进制容器格式。
特点:
- 二进制,不能直接看内容,需要
keytool命令操作 - 可以在一个文件里同时存私钥、证书、信任链
- 用密码保护整个 keystore 以及里面每个条目
- 主要用于 Java 应用:Tomcat、Spring Boot、Kafka、Elasticsearch
JKS 的问题是只有 Java 生态认它。你想让 Nginx 读 JKS?不行。想用 Python 脚本解析?得先转格式。而且 JKS 用的加密算法比较老(早期版本用 SHA1),Oracle 从 Java 9 开始推荐用 PKCS#12 替代。
PKCS#12 (.p12 / .pfx):跨平台的二进制容器
PKCS#12 是一个 RSA 实验室定义的标准,文件后缀 .p12 或 .pfx(Windows 偏好叫 .pfx,内容一样)。
特点:
- 二进制,密码保护
- 能把私钥 + 证书 + 信任链全部打包在一起
- 跨平台:Java、.NET、Go、浏览器都能读
- 是 Java 9+ 默认推荐的 keystore 格式
PKCS#12 像一个"加密的压缩包",把你需要的东西都塞进一个文件。搬运方便,但调试不如 PEM 透明。
三者对比
| 特性 | PEM | JKS | PKCS#12 (.p12/.pfx) |
|---|---|---|---|
| 编码 | Base64 文本 | 二进制 | 二进制 |
| 可读性 | cat 可看 | 需要 keytool | 需要 openssl/keytool |
| 私钥+证书同文件 | 通常分开 | 可以 | 可以 |
| 密码保护 | 私钥可选加密 | 有 | 有 |
| 生态 | Nginx/Apache/Go/Python/Node | Java | 跨平台通用 |
| 信任链 | 文件内串联 | store 内多条目 | 包内多条目 |
| 标准化 | 事实标准 | Java 私有 | PKCS#12 (RFC 7292) |
一句话选型建议:Web 服务器和云原生场景用 PEM;纯 Java 栈用 PKCS#12(别再用 JKS 了);需要把私钥和证书打包给人的时候用 PKCS#12。
格式互转速查
工作中最常见的转换需求,openssl 和 keytool 基本能搞定:
# PEM → PKCS#12
openssl pkcs12 -export \
-in cert.pem -inkey key.pem -certfile chain.pem \
-out bundle.p12 -name myalias
# PKCS#12 → PEM
openssl pkcs12 -in bundle.p12 -out all.pem -nodes
# 只导出证书(不含私钥)
openssl pkcs12 -in bundle.p12 -nokeys -out cert.pem
# 只导出私钥
openssl pkcs12 -in bundle.p12 -nocerts -nodes -out key.pem
# PKCS#12 → JKS (Java 9+)
keytool -importkeystore \
-srckeystore bundle.p12 -srcstoretype PKCS12 \
-destkeystore app.jks -deststoretype JKS
# JKS → PKCS#12
keytool -importkeystore \
-srckeystore app.jks -srcstoretype JKS \
-destkeystore bundle.p12 -deststoretype PKCS12
转格式的时候最容易出的两个坑:
- 忘了带中间证书:只导了 leaf cert,没带 intermediate CA,客户端验不过信任链
- 密码搞丢了:PKCS#12 和 JKS 都有密码保护,转来转去密码对不上就完了。用密码管理器存好,别写在代码里
免费证书:不只是 Let's Encrypt
说到免费证书,大家第一反应是 Let's Encrypt。它确实改变了整个行业,但不是唯一选项。
Let's Encrypt
- 全球最大的免费 CA,ISRG 运营
- 支持 DV(Domain Validation)证书
- 有效期 90 天,逼着你做自动续期(这其实是好事)
- 通过 ACME 协议自动签发和续期
- 支持通配符证书(需要 DNS-01 验证)
- 速率限制:每个注册域名每周 50 张证书
ZeroSSL
- 免费 DV 证书,也支持 ACME
- 有 Web UI,适合不熟悉命令行的人
- 免费层每月 3 张 90 天证书
- 付费层有更多额度和 REST API
Google Trust Services
- Google 自己的公共 CA
- 支持 ACME 协议
- 证书有效期和速率限制政策较宽松
- 适合 GCP 生态里的服务
自签证书(Self-Signed)
- 不是"免费 CA"但确实免费
- 只适合开发、测试、内部服务
- 客户端不会自动信任,需要手动导入 CA 证书
- 工具:
openssl、mkcert(本地开发推荐)、cfssl
选型边界
| 场景 | 推荐 |
|---|---|
| 公网 Web 服务 | Let's Encrypt / ZeroSSL |
| 内部微服务 mTLS | 自签 CA 或 SPIFFE/SPIRE |
| 企业级需求(OV/EV证书) | 商业 CA(DigiCert、Sectigo 等) |
| 本地开发 | mkcert |
| Kubernetes Ingress | cert-manager + Let's Encrypt |
免费证书有一个共同限制:只做 DV(域名验证)。需要 OV(组织验证)或 EV(扩展验证)证书的,还是得找商业 CA 付钱。不过老实说,对绝大多数服务来说,DV 够了。
自动轮换:别再靠日历提醒了
证书过期是最蠢但最常见的生产事故之一。手工续期有两个问题:人会忘,流程会断。
自动轮换有两条主流路径。
路径一:ACME 客户端
ACME(Automatic Certificate Management Environment)是 Let's Encrypt 推广的标准协议(RFC 8555)。流程大致是:
@startuml
!theme plain
skinparam backgroundColor #FEFEFE
skinparam defaultFontSize 12
participant "ACME Client\n(certbot/acme.sh)" as C
participant "ACME Server\n(Let's Encrypt)" as S
participant "Web Server\n(Nginx)" as W
C -> S : 1. 请求签发 example.com
S --> C : 2. 返回验证挑战\n(HTTP-01 / DNS-01)
C -> W : 3. 放置验证文件\n/.well-known/acme-challenge/xxx
S -> W : 4. 回调验证
S --> C : 5. 验证通过,签发证书
C -> W : 6. 部署新证书,reload
note over C : 定时任务每天检查\n到期前 30 天自动续期
@enduml

常用 ACME 客户端:
- certbot:Let's Encrypt 官方推荐,Python 写的,插件丰富
- acme.sh:纯 Shell 脚本,零依赖,支持几十种 DNS 提供商
- lego:Go 写的,单二进制,适合容器环境
一个 acme.sh 的典型配置:
# 首次签发(DNS 验证,支持通配符)
acme.sh --issue --dns dns_cf -d example.com -d "*.example.com"
# 自动部署到 Nginx
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/key.pem \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--reloadcmd "systemctl reload nginx"
# acme.sh 会自动在 crontab 里加续期任务
路径二:Kubernetes cert-manager
如果你的服务跑在 Kubernetes 里,cert-manager 是事实标准。
@startuml
!theme plain
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
skinparam defaultFontSize 12
package "Kubernetes Cluster" {
[cert-manager\nController] as CM #LightGreen
[Issuer / ClusterIssuer\n(ACME config)] as ISS #LightYellow
[Certificate CR] as CERT #LightBlue
[Secret\n(tls.crt + tls.key)] as SEC #Plum
[Ingress / Gateway] as ING #LightSkyBlue
CERT --> CM : watch
CM --> ISS : 读取签发配置
CM --> SEC : 写入证书
ING --> SEC : 引用 TLS Secret
}
cloud "Let's Encrypt\nACME Server" as LE
CM --> LE : ACME 协议\n签发/续期
note bottom of CM
自动监控证书到期
提前 30 天续期
更新 Secret 后
Ingress 自动生效
end note
@enduml

一个最小可用配置:
# ClusterIssuer: 全集群可用的 Let's Encrypt 签发器
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
---
# Certificate: 声明式证书,cert-manager 自动签发和续期
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-tls
namespace: default
spec:
secretName: example-com-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- example.com
- www.example.com
renewBefore: 720h # 到期前 30 天续期
cert-manager 的好处是声明式:你只管定义"我要一张什么证书",它负责签发、续期、更新 Secret。Ingress Controller 监听到 Secret 变化,自动重载。整条链路不需要人介入。
两条路径怎么选
| 场景 | 推荐 |
|---|---|
| 传统 VM 上的 Nginx/Apache | certbot 或 acme.sh |
| Docker 单机部署 | acme.sh + cron |
| Kubernetes 集群 | cert-manager |
| 多云、混合部署 | acme.sh(通用性最好) |
| 内部 CA 签发 | cert-manager + 自定义 Issuer 或 Vault PKI |
几个实操层面容易踩的坑
坑一:只部署了 leaf cert,没带中间证书
浏览器可能缓存了中间 CA 所以能打开,但 curl、SDK、移动端就报错。解决办法:部署 fullchain 而不是单张证书。用 openssl s_client 验证:
openssl s_client -connect example.com:443 -showcerts
看输出里有没有完整的证书链。
坑二:证书和私钥不匹配
换证书的时候用了旧的私钥,或者反过来。快速检查:
# 两个 md5 必须一致
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
坑三:Let's Encrypt 速率限制撞了
测试时反复签发,把每周 50 张的额度用完了。生产签不了。测试环境永远用 staging endpoint:
https://acme-staging-v02.api.letsencrypt.org/directory
坑四:自动续期配了但没验证
cron job 加了,从来没跑过。或者跑了但 DNS 验证失败,续期静默失败,直到证书过期才发现。
建议:监控证书剩余天数。Prometheus 有 ssl_certificate_expiry_time_seconds 这个 metric,Blackbox Exporter 也能探测。低于 14 天就报警。
坑五:内部服务用公共 CA 证书
内部 api.internal.company.com 也去 Let's Encrypt 签一张?能行,但暴露了内部域名结构(CT Log 里全是公开的)。内部服务建议自建 CA 或者用 SPIFFE/SPIRE 做 mTLS。
证书管理的生命周期
把上面的点串起来,证书管理不是"签一张就完了",而是一个持续循环:
@startuml
!theme plain
skinparam backgroundColor #FEFEFE
skinparam activityFontSize 12
start
:评估需求\n公网/内部? DV/OV/EV?;
:选择 CA\n免费(Let's Encrypt) / 商业 / 自建;
:选择格式\nPEM(Web) / P12(Java/跨平台) / JKS(旧Java);
:签发证书\n手动 or ACME 自动化;
:部署证书\n配置 Web Server 或 K8s Secret;
:监控到期\nPrometheus / Blackbox Exporter;
if (到期前 30 天?) then (yes)
:自动续期\ncert-manager / acme.sh / certbot;
:部署新证书\nreload / Secret 更新;
else (no)
:继续监控;
endif
:审计与合规\nCT Log / 吊销检查 / 密钥轮换;
stop
@enduml

明天就能做的检查清单
- [ ] 盘点所有 TLS 证书:域名、格式、到期时间、存放位置
- [ ] 确认每个服务部署的是 fullchain 而不是单张 leaf cert
- [ ] 为公网服务配置 ACME 自动续期(certbot/acme.sh/cert-manager)
- [ ] 测试环境用 staging endpoint,别消耗生产速率限制
- [ ] 新 Java 项目用 PKCS#12 替代 JKS
- [ ] 监控证书剩余天数,低于 14 天触发告警
- [ ] 内部服务用自建 CA 或 mTLS,别把内部域名暴露到 CT Log
- [ ] 私钥文件权限收紧到
600,不要提交到 Git - [ ] 做一次"证书突然过期"的演练,看恢复流程要多久
总结
证书管理不是什么高深的活儿,但它有个讨厌的特点:平时不出事,出事就是凌晨三点。 大部分证书事故的根因都不是技术不行,而是流程断了——该续的没续,该监控的没监控,该自动化的还在靠人。
格式上,PEM 最透明,PKCS#12 最通用,JKS 正在退场。免费证书领域,Let's Encrypt 已经是基础设施级别的存在,90 天有效期逼着你做自动化,这是件好事。自动轮换,传统环境用 acme.sh 或 certbot,Kubernetes 用 cert-manager,都是成熟方案。
一句话:证书管理的最高境界,是你忘了它的存在。 因为该自动的都自动了,该告警的都告警了,你只需要在续签成功的日志里偶尔看到一行 Certificate renewed successfully,然后继续睡觉。
@startmindmap
!theme plain
skinparam backgroundColor #FEFEFE
* 证书管理最佳实践
** 格式选型
*** PEM: 文本, Web/云原生
*** PKCS#12: 二进制, 跨平台
*** JKS: Java 遗留, 建议迁移
** 免费证书
*** Let's Encrypt (90天, ACME)
*** ZeroSSL (Web UI)
*** Google Trust Services
*** 自签 (内部/开发)
** 自动轮换
*** ACME 客户端
**** certbot
**** acme.sh
**** lego
*** Kubernetes
**** cert-manager
**** ClusterIssuer + Certificate CR
** 常见坑
*** 缺中间证书
*** 证书私钥不匹配
*** 速率限制
*** 续期静默失败
*** 内部域名暴露 CT Log
** 监控告警
*** 剩余天数 < 14 天
*** Prometheus + Blackbox
*** 续期失败告警
@endmindmap

扩展阅读
- Let's Encrypt 官方文档: https://letsencrypt.org/docs/
- ACME 协议 RFC 8555: https://datatracker.ietf.org/doc/html/rfc8555
- cert-manager 文档: https://cert-manager.io/docs/
- acme.sh GitHub: https://github.com/acmesh-official/acme.sh
- mkcert - 本地开发零配置 HTTPS: https://github.com/FiloSottile/mkcert
- PKCS#12 RFC 7292: https://datatracker.ietf.org/doc/html/rfc7292
- SSL Labs 服务器测试: https://www.ssllabs.com/ssltest/
- ZeroSSL: https://zerossl.com/
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。