Let's Encrypt 的 ACME 协议到底干了什么?用抓包带你看一遍

用过 Let's Encrypt 或 certbot 的人都知道,跑一条命令就能拿到免费的 SSL 证书。但这个过程背后到底发生了什么?CA 怎么确认你拥有这个域名?证书是怎么签发出来的?

这些事情都由一个叫 ACME(Automatic Certificate Management Environment)的协议来完成。RFC 8555 定义了它的全部流程。

我用实际的抓包和请求记录,把整个流程走一遍。

ACME 协议要解决的核心问题

传统的 SSL 证书申请流程是这样的:你去 CA 的网站下单 → 选证书类型 → 提交域名 → 做验证(DNS 或邮箱)→ 等人工审核 → 下载证书文件。整个过程可能要几个小时到几天。

ACME 的目标是把这个流程自动化------让客户端程序(比如 certbot)通过 API 和 CA 交互,自动完成域名验证和证书签发。DV 证书的整个签发过程可以在几分钟内完成,不需要人工介入。

完整流程拆解

第一步:发现(Directory)

客户端先请求 CA 的 Directory URL,获取各个 API 端点的地址。

bash 复制代码
GET https://acme-v02.api.letsencrypt.org/directory

返回一个 JSON,包含了 newNonce、newAccount、newOrder 等端点的 URL。这一步相当于拿到一份 API 目录。

第二步:注册账号(Account)

客户端生成一对 RSA 或 ECDSA 密钥(Account Key),用私钥签名一个注册请求发到 newAccount 端点。

arduino 复制代码
POST /acme/new-acct
{
  "termsOfServiceAgreed": true,
  "contact": ["mailto:admin@example.com"]
}

CA 创建账号,返回一个 Account URL。后续所有请求都用这个 Account Key 签名来做身份认证。

这里有一个安全设计值得注意:ACME 的每个 POST 请求都带一个 nonce(防重放攻击),nonce 从 newNonce 端点获取或从上一个响应的 Replay-Nonce header 里拿。

第三步:下单(Order)

客户端发起一个证书申请,告诉 CA 我要给哪些域名签证书。

sql 复制代码
POST /acme/new-order
{
  "identifiers": [
    {"type": "dns", "value": "example.com"},
    {"type": "dns", "value": "*.example.com"}
  ]
}

CA 返回一个 Order 对象,里面包含了需要完成的 authorizations------每个域名对应一个 authorization,你需要逐个完成验证。

第四步:域名验证(Challenge)

这是整个流程的关键。CA 需要确认你真的控制这个域名。ACME 支持几种验证方式:

HTTP-01 验证: CA 给你一个 token,你需要在 http://yourdomain/.well-known/acme-challenge/{token} 放一个包含特定内容的文件。CA 的服务器会来访问这个 URL,如果能拿到正确内容,验证通过。

bash 复制代码
# CA 要求你在这个路径放文件
http://example.com/.well-known/acme-challenge/abc123...

# 文件内容 = token + "." + Account Key 的 Thumbprint
abc123....xyz789...

DNS-01 验证: 在域名的 DNS 里添加一条 TXT 记录。记录名是 _acme-challenge.yourdomain,值是 CA 给的 token 的 SHA-256 哈希值(Base64URL 编码)。

arduino 复制代码
_acme-challenge.example.com  TXT  "dGVzdC10b2tlbi1oYXNo..."

通配符证书必须用 DNS-01 验证,因为 HTTP-01 无法验证 * 域名的所有权。

验证通过后,客户端通知 CA:

bash 复制代码
POST /acme/challenge/{challenge-id}
{}  // 空 body,表示"我准备好了,你来验证"

CA 验证成功后把 authorization 的状态标记为 valid

第五步:签发证书(Finalize)

所有域名验证通过后,客户端生成一对新的密钥(Certificate Key,和 Account Key 不同),创建 CSR(Certificate Signing Request),发到 Order 的 finalize URL。

bash 复制代码
POST /acme/order/{id}/finalize
{
  "csr": "MIIBxTCCAWsCAQAwYjELMAkGA1UE..."  // Base64URL 编码的 CSR
}

CA 用自己的私钥签发证书,返回一个证书下载 URL。

第六步:下载证书

bash 复制代码
POST /acme/cert/{id}

返回 PEM 格式的证书链(你的证书 + 中间证书)。保存到文件,配到 Nginx 或其他 Web 服务器上就行了。

安全机制总结

ACME 协议在安全性上做了几个关键设计:

所有请求都用 JWS(JSON Web Signature)签名,CA 通过 Account Key 验证请求者身份。每个请求带 nonce 防重放。域名验证确保只有域名控制者才能申请证书。Account Key 和 Certificate Key 分离,降低单一密钥泄露的风险。

实际影响

理解 ACME 协议的意义不只是"知道原理":

当 certbot 报错时,你能根据错误发生在哪个阶段来定位问题。比如 DNS-01 验证失败,可能是 TXT 记录没生效(DNS 传播延迟);HTTP-01 失败,可能是服务器防火墙拦了 CA 的请求。

当你选择证书管理平台时,理解了 ACME 就知道平台实际上帮你做了什么------自动完成 DNS 验证(通过 DNS API 添加 TXT 记录)和证书签发,省掉了手动操作的环节。

延伸阅读

ACME 协议的完整规范在 RFC 8555,有兴趣可以读一下。Let's Encrypt 的文档里也有比较详细的流程说明。


这篇是纯协议解析,不涉及特定产品推荐。如果你对证书管理感兴趣,ssl.spug.cc 可以了解一下。

相关推荐
程序员多吃鸭eatmoreduck18 小时前
# 开源初体验,39 天 800 Star:我把项目发到了哪里,流量到底从哪来
后端
雪隐18 小时前
个人电脑玩AI-10让5060 Ti给你打工——我让 Claude Code 喝上了本地杂粮:Ternary-Bonsai-27B 部署历险记
前端·人工智能·后端
云上小朱18 小时前
Intel SGX相关软件部署
后端
Reart18 小时前
Leetcode 1143.最长公共子序列(720)
后端·算法
掘金者阿豪18 小时前
一条SQL能执行成功,不代表它真的安全:一次生产环境数据库迁移事故复盘
后端
Reart19 小时前
Leetcode 718.最长重复子数组(720)
后端
云上小朱19 小时前
软件更新-openssh和openssl-centos
后端
Conan在掘金19 小时前
鸿蒙 ArkUI 深水区:Navigation 多级路由,从「拼页面」到「搭应用」的分水岭
后端