用过 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 可以了解一下。