第 38 章 / 共 51 章
卷五 · 扩展、运维与实战

xacme:证书自动化签发

HTTPS 的续命术:ACME(RFC 8555)客户端把账户、订单、dns-01 挑战与证书获取装进一个函数调用——配合磁盘存储实现到期自动续。

本章导读

站位:ch12(证书管理)的自动化续篇——手工 openssl 换成 API 驱动;与 ch42 部署的"证书台账"衔接。前置:build.bat xacme 变体。生产联测需真实域名与 DNS 控制权。

38.1 ACME 流程

RFC 8555 的核心循环:向 CA 的 directory 注册账户(ES256 密钥 + JOSE 签名)→ 下单域名(可含通配符)→ 完成 dns-01 挑战(在 DNS 上发布 CA 给的 TXT 记录)→ finalize 用 CSR 生成证书 → 下载链。xacmeClientInit 建客户端(解析 directory、注册/复用账户),xacmeClientIssue 跑完整个循环返回 PEM 链。CA 中立:LE / ZeroSSL / Google / Buypass 预设常量,换 CA 只是换 URL。

38.2 DNS provider

dns-01 需要 DNS 写权限:provider 接口是纯结构(id / Add / Remove / 可选 Propagate 回调),内建阿里云(dns_ali,V3 签名);其它厂商自填结构即可接入,零注册零全局状态。库自带 TXT 传播探测——向权威 NS 发 UDP 查询确认记录可見后再通知 CA 验证。

38.3 签发与续期

一站式续签(IssueStored)
xacmeclient* pClient;
xstrview tDomain = xrtStrView("example.com");
bool bRenewed;

pClient = ...;  /* xacmeClientInit(引擎, CA pem, 目录URL, 账户密钥) */
char* sChain = xacmeClientIssueStored(pClient, &tDomain, 1,
    &tDnsProvider, sStoreRoot, 30, &bRenewed);
if ( sChain != NULL && bRenewed ) {
    install_cert(sChain);   /* 热替换(ch47 演练过) */
}
xrtFree(sChain);

IssueStored 的语义:本地证书剩余寿命 ≥ 阈值天数就直接返回现有链(pbRenewed=false);不足则走完整签发并落盘。剩余天数判断基于 x509 解析(xrt),不依赖外部进程。

38.4 存储与恢复

store 是目录约定:accounts/ 存账户密钥(PKCS#8 PEM,Init 可复用免重复注册)、certs/<域名>/ 存全链与 CA 溯源,原子写入(xrtFileWriteAtomic)。续期判断、证书回读、CA 链回读各有独立函数——不用 IssueStored 也能拼自己的续期策略。

38.5 契约

关注要点
所有权客户端/Issue 返回的链均库分配;Destroy/xrtFree 释放
网络自带同步 HTTPS 传输(可借引擎或自建);调用量集中在签发窗口
凭据账户密钥与 DNS 密钥走 store/环境(ch44 分层),绝不入源码
失败线程错误带域 "xrt.acme.flow" 的稳定错误码,可分类重试

常见错误

拿生产 CA 当试验场
症状
频繁重签触发 Let's Encrypt 速率限制,域名被锁数小时。
原因
调试期应使用 LE staging 预设(不占正式配额);IssueStored 的续期阈值别设成"每次都续"。
账户密钥每次新生成
症状
重启后账户对不上,CA 视为新注册,订单历史丢失。
原因
账户密钥要经 store 持久化并在 Init 传入复用——IssueStored 已代劳,绕开它就要自己管。

编程练习

基础离线检查

staging 目录 + 账户配置 + provider 注册表(Count/Id)跑通;NeedRenew 对一份近到期证书返回真。

验收:注册表含 ali;剩余天数判断与 openssl 对账。
进阶staging 签发

真实域名 + LE staging 完成一次 dns-01 签发并落盘。

验收:证书链可被 xrt TLS 加载;重复跑触发"未到期直接返回"。
挑战续期机器人

ch41 定时器每日检查,不足 30 天自动 IssueStored + 热替换(ch47 的零断连演练)。

验收:替换期间零断连;失败有分类告警而非静默。

小结

要点结论
流程directory→账户→订单→dns-01→finalize→下载,一函数封装
DNSprovider 纯结构接口,内建阿里云,自带 TXT 探测
续期IssueStored 按剩余天数决定签/不签,原子落盘
存储账户密钥复用 + 全链溯源,目录约定清晰
纪律staging 调试;密钥持久化;凭据不入源码
xrt 延伸阅读