4、ACME协议详解:ACME v2协议流程、挑战类型与证书签发续期

说到自动化证书管理,ACME协议是绕不开的核心。说白了,它就是让客户端和CA(证书颁发机构)之间对话的一套标准流程。我最早接触ACME v1的时候,感觉还挺新鲜的,后来v2出来,流程更清晰,安全性也更高。今天咱们就把它掰开揉碎了讲清楚。

4.1 ACME v2协议流程:一次完整的握手

ACME v2的流程,我习惯把它分成三个阶段:注册、验证、签发。咱们一步步看。

核心思想:CA要证明你确实拥有某个域名,才会给你发证书。ACME就是这套“证明”的标准化流程。

4.1.1 注册阶段

客户端先跟CA打个招呼,说“我想申请证书”。CA会要求客户端生成一个账户密钥对,然后注册一个账户。这一步其实挺简单的,就是建立信任关系。

4.1.2 验证阶段

这是最关键的环节。客户端告诉CA:“我想给 example.com 申请证书。” CA会返回一个挑战(Challenge),要求客户端证明对域名的控制权。客户端完成挑战后,CA会验证结果。验证通过,CA才会进入签发阶段。

4.1.3 签发与续期阶段

验证通过后,客户端生成证书签名请求(CSR),发给CA。CA签发证书,返回给客户端。续期其实就是重复验证和签发流程,只不过账户信息不用重新注册。

我画了一张流程图,把这三个阶段串起来,你一看就明白了。

ACME v2 协议核心流程 客户端 CA 服务器 ① 注册账户 ② 返回挑战列表 挑战类型 HTTP-01 | DNS-01 | TLS-ALPN-01 客户端选择一种完成验证 ③ 完成挑战并提交 ④ 验证通过 ⑤ 签发 / 续期证书 客户端生成CSR → CA签发 → 下载证书 ⑥ 返回证书

4.2 三种挑战类型:你该选哪个?

ACME v2 定义了三种挑战类型。每种都有自己的适用场景。我根据项目经验,给你总结一下。

挑战类型 验证方式 适用场景 优点 缺点
HTTP-01 在 Web 服务器的 /.well-known/acme-challenge/ 路径下放置指定文件 有公网 Web 服务器,且 80 端口可访问 配置简单,无需修改 DNS 依赖 80 端口;不支持通配符证书
DNS-01 在域名的 TXT 记录中添加指定值 需要通配符证书;或无法开放 80/443 端口 支持通配符证书;不依赖 Web 服务 需要 DNS API 权限;生效有延迟
TLS-ALPN-01 在 443 端口通过 TLS 握手时返回特定证书 仅支持 443 端口,且需要 TLS 服务 无需修改文件系统;适合反向代理场景 依赖 443 端口;配置稍复杂

4.2.1 HTTP-01:最常用的方式

这个挑战,说白了就是CA去你网站的一个特定路径下,看有没有一个特定内容的文件。有,就证明你控制这个网站。

我个人习惯在开发环境或小规模站点上用这个。配置起来确实方便,几行 Nginx 配置就搞定了。但要注意,它不支持通配符证书。你想想看,如果你有几十个子域名,每个都要单独验证,那得多麻烦?

小技巧:用 HTTP-01 时,确保你的 Web 服务器没有把 /.well-known/ 路径重定向到其他地方。我曾经遇到过因为做了全站 HTTPS 重定向,导致验证文件被跳转,挑战一直失败的情况。

4.2.2 DNS-01:通配符证书的救星

这个挑战,是让你在域名的 DNS 解析记录里加一条 TXT 记录。CA 去查这条记录,有就证明你控制这个域名。

我最喜欢它的地方是:支持通配符证书。比如你给 *.example.com 申请证书,一次验证,所有子域名都能用。我在管理一个大型微服务集群时,全靠 DNS-01 批量签发通配符证书,省了太多事。

但有个坑:DNS 记录生效有延迟。有些 DNS 服务商更新记录后,要等几分钟甚至更久才能全球生效。所以,建议用支持 TTL 设置的 DNS API,把 TTL 设小一点,比如 60 秒。

避坑指南:我曾经因为 DNS 服务商的 API 限流,导致批量续期时部分域名验证失败。后来我加了重试机制,每次失败后等待 30 秒再重试,问题就解决了。

4.2.3 TLS-ALPN-01:最“安静”的方式

这个挑战比较特殊。它利用 TLS 握手阶段的 ALPN(应用层协议协商)扩展,在握手过程中就完成验证。客户端不需要修改文件系统,也不需要动 DNS。

嗯,这里要注意:它要求你的服务器必须在 443 端口上运行,并且支持 TLS-ALPN 扩展。我一般在反向代理或负载均衡器上用这个,比如 Nginx 或 HAProxy。配置起来稍微复杂一点,但一旦配好,后续的续期完全静默,用户体验很好。

4.3 证书签发与续期:自动化才是王道

理解了挑战类型,签发和续期就顺理成章了。ACME v2 的续期,其实就是重新走一遍验证流程。但有个关键点:证书有效期。

现在主流 CA(比如 Let's Encrypt)签发的证书有效期是 90 天。为什么这么短?说白了,就是为了推动自动化。你想想看,如果证书有效期是一年,你可能会手动去续;但如果是 90 天,手动续就太累了,逼着你用自动化工具。

我的建议:不要等到证书过期前一周才续期。我一般设置一个定时任务,在证书到期前 30 天就开始尝试续期。如果失败,还有充足的时间排查问题。

4.3.1 签发流程示例(使用 acme.sh)

我用 acme.sh 举个例子,它是我用得最多的 ACME 客户端。签发一个证书,命令很简单:

# 使用 DNS-01 挑战,签发通配符证书
acme.sh --issue --dns dns_cf -d *.example.com

# 使用 HTTP-01 挑战,签发单域名证书
acme.sh --issue -d example.com -w /var/www/html

续期更简单:

# 手动续期所有证书
acme.sh --renew-all

# 或者设置 cron 任务自动续期
acme.sh --install-cronjob

4.3.2 续期策略:我踩过的坑

自动续期不是配好就万事大吉了。我遇到过几次翻车,总结了几条经验:

  • 重试机制必须有:网络抖动、DNS 延迟都可能导致验证失败。我一般设置最多重试 5 次,每次间隔 30 秒。
  • 续期后要重启服务:证书文件更新了,但 Web 服务器或反向代理可能还在用旧证书。续期脚本里一定要加上重启服务的命令。
  • 监控不能少:我习惯在续期脚本最后加一个通知,比如发到钉钉或 Slack。如果续期失败,我能第一时间知道。

避坑指南:我曾经因为续期脚本里忘了重启 Nginx,导致证书明明续期成功了,但线上服务用的还是旧证书,直到用户反馈“证书过期”我才发现。从那以后,我每次续期脚本都会加上 nginx -s reload 或 systemctl reload nginx。

4.4 总结一下

ACME v2 协议,说白了就是一套“证明你拥有域名”的标准流程。三种挑战类型各有优劣,选哪个取决于你的场景。我个人建议:

  • 单域名、有公网 Web 服务 → HTTP-01
  • 通配符证书、或无法开放 80 端口 → DNS-01
  • 反向代理、负载均衡场景 → TLS-ALPN-01

证书续期一定要自动化,而且要有重试和监控。别等到证书过期了才手忙脚乱地去处理。嗯,这些经验都是我用真金白银的线上事故换来的,希望对你有帮助。