跳到主要内容

RFC 8555 - 自动证书管理环境 (Automatic Certificate Management Environment, ACME)

  • 状态: Proposed Standard
  • 发布日期: 2019 年 3 月
  • Stream: IETF
  • 勘误: 无勘误

摘要 (Abstract)​

使用 X.509 的公钥基础设施 (Public Key Infrastructure using X.509, PKIX) 证书可用于多种目的, 其中最重要的是域名认证. 因此, Web PKI 中的证书颁发机构 (Certification Authorities, CAs) 被信任来验证证书申请者是否能合法代表证书中的域名. 截至本文撰写时, 这种验证通过一组临时机制完成. 本文档描述一种协议, CA 和申请者可以使用该协议自动化验证和证书签发过程. 该协议还为其他证书管理功能提供设施, 例如证书吊销.


本备忘录状态 (Status of This Memo)​

本文档是 Internet 标准路线文档.

本文档是 Internet Engineering Task Force (IETF) 的产物. 它代表 IETF 社区的共识. 它已经接受公开审查, 并获 Internet Engineering Steering Group (IESG) 批准发布.


关键特性 (Key Features)​

ACME 协议收益:

  • 完全自动化 (Fully Automated): 从请求到续期无需人工干预
  • 频繁更新 (Frequent Updates): 支持短期证书 (Let's Encrypt 默认 90 天)
  • 降低成本 (Cost Reduction): 消除人工流程成本
  • 增强安全性 (Enhanced Security): 短期证书降低暴露风险

典型 ACME 工作流:

Client (ACME Client)                    ACME Server (CA)
| |
| 1. Create Account |
|--------------------------------------->|
| <-- Account URL |
| |
| 2. Submit Certificate Order |
|--------------------------------------->|
| <-- Order Object + Auth Challenges |
| |
| 3. Complete Domain Validation |
| (HTTP-01 or DNS-01) |
|--------------------------------------->|
| <-- Validation Success |
| |
| 4. Finalize Order (Submit CSR) |
|--------------------------------------->|
| <-- Certificate URL |
| |
| 5. Download Certificate |
|--------------------------------------->|
| <-- PEM Format Certificate Chain |

核心组件 (Core Components)​

资源类型 (Resource Types)​

  1. Directory: 服务器 API 端点目录
  2. Account: 客户端账户信息
  3. Order: 证书订单
  4. Authorization: 域名授权
  5. Challenge: 验证挑战
  6. Certificate: 已签发证书

验证方法 (Validation Methods)​

  • HTTP-01 Challenge: 在特定 HTTP 路径提供文件
  • DNS-01 Challenge: 提供特定 DNS TXT 记录

  • Certbot (EFF Official)
  • acme.sh (Shell Script)
  • Lego (Go Language)
  • win-acme (Windows)

  • RFC 7515 - JSON Web Signature
  • RFC 5280 - X.509 Certificates
  • RFC 6797 - HSTS
  • RFC 7807 - Problem Details for HTTP APIs

参考资源 (References)​


有关详细技术规范, 请参考官方 RFC 8555 文档.


1. Introduction (简介)​

Web PKI 中的证书 [RFC5280] 最常用于认证域名 (Domain Names).因此, Web PKI 中的证书颁发机构 (Certification Authorities, CAs) 被信任验证证书申请人合法代表证书中的域名.

不同类型的证书反映了 CA 对证书主体信息的不同验证级别."域名验证" (Domain Validation, DV) 证书是迄今为止最常见的类型.在 DV 证书颁发过程中, CA 唯一需要执行的验证是确认请求者有效控制该域名 [CABFBR].CA 不需要尝试验证请求者的真实身份.(这与 "组织验证" (Organization Validation, OV) 和 "扩展验证" (Extended Validation, EV) 证书不同, 后者的流程还旨在验证请求者的真实身份.)

现有的 Web PKI 证书颁发机构倾向于使用一组临时协议 (Ad Hoc Protocols) 进行证书颁发和身份验证.对于 DV 证书, 典型的用户体验如下:

  • 生成 PKCS#10 [RFC2986] 证书签名请求 (Certificate Signing Request, CSR).

  • 将 CSR 复制粘贴到 CA 的网页中.

  • 通过以下方法之一证明对 CSR 中域名的所有权:

    • 在 Web 服务器的特定位置放置 CA 提供的挑战 (Challenge).

    • 在与目标域名对应的 DNS 记录中放置 CA 提供的挑战.

    • 在与域名对应的 (希望是) 管理员控制的电子邮件地址接收 CA 提供的挑战, 然后在 CA 的网页上响应.

  • 下载颁发的证书并将其安装在用户的 Web 服务器上.

除了 CSR 本身和颁发的证书外, 这些都是完全临时的程序, 通过让人类用户遵循 CA 的交互式自然语言指令来完成, 而不是通过机器实现的已发布协议.在许多情况下, 这些指令难以遵循, 并导致严重的挫折和困惑.作者进行的非正式可用性测试表明, 网站管理员通常需要 1-3 小时才能为域名获取和安装证书.即使在最好的情况下, 缺乏已发布的标准化机制也会阻碍 HTTPS 和其他依赖 PKIX 的系统的广泛部署, 因为它抑制了与证书颁发、部署和吊销相关任务的机械化.

本文档描述了一个可扩展框架, 用于自动化证书颁发和域名验证过程, 从而允许服务器和基础设施软件在无需用户交互的情况下获取证书.使用此协议应该能够极大地简化 HTTPS 的部署, 以及基于传输层安全 (Transport Layer Security, TLS) [RFC8446] 的其他协议中基于 PKIX 的身份验证的实用性.

应当注意的是, 虽然本文档的重点是验证域名以在 Web PKI 中颁发证书, 但 ACME 支持在其他 PKI 上下文中使用其他标识符的扩展.例如, 在撰写本文时, 正在进行使用 ACME 颁发证明 IP 地址的 Web PKI 证书 [ACME-IP] 和证明电话号码的安全电话身份重访 (Secure Telephone Identity Revisited, STIR) 证书 [ACME-TELEPHONE] 的工作.

ACME 还可以用于自动化证书管理的某些方面, 即使在仍然需要非自动化流程的情况下也是如此.例如, 外部账户绑定 (External Account Binding) 功能 (参见第 7.3.4 节) 可以允许 ACME 账户使用已授予外部非 ACME 账户的授权.这使得 ACME 能够处理尚无法完全自动化的颁发场景, 例如 "扩展验证" 证书的颁发.



2. Deployment Model and Operator Experience (部署模型和操作体验)​

ACME 的指导性用例是为网站获取证书 (HTTPS [RFC2818]).在这种情况下, Web 服务器旨在代表一个或多个域名, 证书颁发过程旨在验证该 Web 服务器确实代表这些域名.

DV 证书验证通常检查与域名控制相关的属性声明——这些属性可以由证书颁发者在纯在线进行的交互过程中观察到.这意味着在典型情况下, 请求、验证和颁发过程中的所有步骤都可以通过互联网协议表示和执行, 无需带外人工干预.

在 ACME 之前, 当部署 HTTPS 服务器时, 服务器操作员通常会收到生成自签名证书 (Self-Signed Certificate) 的提示.如果操作员改为使用 ACME 部署 HTTPS 服务器, 体验将如下所示:

  • 操作员的 ACME 客户端提示操作员输入 Web 服务器要代表的预期域名.

  • ACME 客户端向操作员呈现可以从中获取证书的 CA 列表.(此列表将根据 CA 的能力和 ACME 配置的更新而随时间变化.) ACME 客户端可能会在此时提示操作员提供付款信息.

  • 操作员选择一个 CA.

  • 在后台, ACME 客户端联系 CA 并请求其为预期域名颁发证书.

  • CA 通过让 ACME 客户端执行某些只能在控制域名的情况下才能完成的操作来验证客户端控制所请求的域名.例如, CA 可能要求请求 example.com 的客户端在 example.com 下配置 DNS 记录或在 http://example.com 下配置 HTTP 资源.

  • 一旦 CA 满意, 它就会颁发证书, ACME 客户端会自动下载并安装它, 可能通过电子邮件、短信等方式通知操作员.

  • ACME 客户端定期联系 CA 以获取更新的证书、装订的在线证书状态协议 (Online Certificate Status Protocol, OCSP) 响应 [RFC6960], 或保持 Web 服务器功能正常及其凭据最新所需的任何其他内容.

通过这种方式, 使用 CA 颁发的证书进行部署几乎与使用自签名证书一样容易.此外, 维护该 CA 颁发的证书将需要最少的手动干预.ACME 与 HTTPS 服务器的这种紧密集成允许在证书颁发时立即自动部署, 使人类管理员免于前一节中描述的大部分耗时工作.



3. Terminology (术语表)​

本文档中的关键词 "MUST" (必须), "MUST NOT" (禁止), "REQUIRED" (必需), "SHALL" (应), "SHALL NOT" (不应), "SHOULD" (应该), "SHOULD NOT" (不应该), "RECOMMENDED" (推荐), "NOT RECOMMENDED" (不推荐), "MAY" (可以), 和 "OPTIONAL" (可选) 应按照 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释, 当且仅当它们以全大写形式出现时, 如此处所示.

ACME 中的两个主要角色是 "客户端" (Client) 和 "服务器" (Server).ACME 客户端使用该协议请求证书管理操作, 例如颁发或吊销.ACME 客户端可以运行在 Web 服务器、邮件服务器或其他需要有效 X.509 证书的服务器系统上.或者, 它可以运行在不使用证书但被授权响应 CA 提供的挑战的单独服务器上.ACME 服务器运行在证书颁发机构, 并响应客户端请求, 如果客户端被授权则执行所请求的操作.

ACME 客户端通过 "账户密钥对" (Account Key Pair) 向服务器进行身份验证.客户端使用此密钥对的私钥对发送到服务器的所有消息进行签名.服务器使用公钥验证来自客户端的消息的真实性和完整性.



5. Character Encoding (字符编码)​

ACME 客户端、ACME 服务器和验证服务器通过 HTTP 发送的所有请求和响应, 以及摘要计算的任何输入, 必须 (MUST) 使用 UTF-8 字符集 [RFC3629] 进行编码.请注意, 证书中出现的标识符可能有其自己的编码考虑 (例如, 包含非 ASCII 字符的 DNS 名称表示为 A-labels 而不是 U-labels).任何此类编码考虑都应在前述 UTF-8 编码之前应用.



6. Message Transport (消息传输)​

ACME 客户端和 ACME 服务器之间的通信通过 HTTPS 进行, 使用 JSON Web 签名 (JSON Web Signature, JWS) [RFC7515] 为从客户端发送到服务器的消息提供一些额外的安全属性.HTTPS 提供服务器身份验证和机密性.通过一些 ACME 特定的扩展, JWS 提供客户端请求有效载荷的身份验证、防重放保护以及 HTTPS 请求 URL 的完整性.

6.1. HTTPS Requests (HTTPS 请求)​

每个 ACME 功能都是通过客户端向服务器 [RFC2818] 发送一系列 HTTPS 请求来完成的, 这些请求携带 JSON 消息 [RFC8259].必须 (REQUIRED) 使用 HTTPS.下面第 7 节的每个小节描述了该功能使用的消息格式以及消息发送的顺序.

在 ACME 使用的大多数 HTTPS 事务中, ACME 客户端是 HTTPS 客户端, ACME 服务器是 HTTPS 服务器.ACME 服务器在验证挑战时充当客户端: 验证 'http-01' 挑战时是 HTTP 客户端, 验证 'dns-01' 时是 DNS 客户端, 等等.

ACME 服务器在配置其 TLS 实现时应该 (SHOULD) 遵循 [RFC7525] 的建议.支持 TLS 1.3 的 ACME 服务器可以 (MAY) 允许客户端发送早期数据 (0-RTT).这是安全的, 因为 ACME 协议本身在所有需要的情况下都包含防重放保护 (参见第 6.5 节).因此, 对于可以在 0-RTT 中携带哪些 ACME 数据没有限制.

ACME 客户端必须 (MUST) 根据 [RFC7231] 发送 User-Agent 头字段.除了底层 HTTP 客户端软件的名称和版本外, 此头字段应该 (SHOULD) 包括 ACME 软件的名称和版本.

ACME 客户端应该 (SHOULD) 根据 [RFC7231] 发送 Accept-Language 头字段, 以启用错误消息的本地化.

旨在普遍可访问的 ACME 服务器需要使用跨源资源共享 (Cross-Origin Resource Sharing, CORS), 以便可以从基于浏览器的客户端访问 [W3C.REC-cors-20140116].此类服务器应该 (SHOULD) 将 Access-Control-Allow-Origin 头字段设置为值 "*".

ACME 使用的 JSON 对象中的二进制字段使用 [RFC4648] 第 5 节中描述的 base64url 编码进行编码, 根据 [RFC7515] 第 2 节中 JSON Web 签名中指定的配置文件.此编码使用 URL 安全字符集.必须 (MUST) 去除尾随的 '=' 字符.包含尾随 '=' 字符的编码值必须 (MUST) 被拒绝为编码不当.

6.2. Request Authentication (请求认证)​

所有具有非空主体的 ACME 请求必须 (MUST) 将其有效载荷封装在 JSON Web 签名 (JSON Web Signature, JWS) [RFC7515] 对象中, 使用账户的私钥签名, 除非另有说明.服务器必须 (MUST) 在处理请求之前验证 JWS.将请求主体封装在 JWS 中提供了请求的身份验证.

作为 ACME 请求主体发送的 JWS 对象必须 (MUST) 满足以下附加标准:

  • JWS 必须 (MUST) 采用扁平化 JSON 序列化 (Flattened JSON Serialization) [RFC7515]

  • JWS 禁止 (MUST NOT) 具有多个签名

  • 禁止 (MUST NOT) 使用 JWS 未编码有效载荷选项 (JWS Unencoded Payload Option) [RFC7797]

  • 禁止 (MUST NOT) 使用 JWS 未保护头 (JWS Unprotected Header) [RFC7515]

  • JWS 有效载荷禁止 (MUST NOT) 分离

  • JWS 保护头必须 (MUST) 包括以下字段:

    • "alg" (算法, Algorithm)

      • 此字段禁止 (MUST NOT) 包含 "none" 或消息认证码 (Message Authentication Code, MAC) 算法 (例如, 算法注册表描述中提到 MAC/HMAC 的算法).
    • "nonce" (在第 6.5 节中定义)

    • "url" (在第 6.4 节中定义)

    • "jwk" (JSON Web 密钥, JSON Web Key) 或 "kid" (密钥 ID, Key ID), 如下所述

ACME 服务器必须 (MUST) 实现 "ES256" 签名算法 [RFC7518], 并应该 (SHOULD) 实现使用 "Ed25519" 变体 (由 "crv" 指示) 的 "EdDSA" 签名算法 [RFC8037].

"jwk" 和 "kid" 字段是互斥的.服务器必须 (MUST) 拒绝同时包含两者的请求.

对于 newAccount 请求以及由证书密钥认证的 revokeCert 请求, 必须 (MUST) 有一个 "jwk" 字段.此字段必须 (MUST) 包含与用于签名 JWS 的私钥对应的公钥.

对于所有其他请求, 请求使用现有账户签名, 并且必须 (MUST) 有一个 "kid" 字段.此字段必须 (MUST) 包含通过 POST 到 newAccount 资源接收的账户 URL.

如果客户端发送使用服务器不支持的算法签名的 JWS, 则服务器必须 (MUST) 返回状态码 400 (Bad Request) 和类型 "urn:ietf:params:acme:error:badSignatureAlgorithm" 的错误.随错误返回的问题文档必须 (MUST) 包括一个 "algorithms" 字段, 其中包含支持的 "alg" 值数组.有关错误响应结构的更多详细信息, 请参见第 6.7 节.

如果服务器支持签名算法 "alg" 但不支持或选择拒绝公钥 "jwk", 则服务器必须 (MUST) 返回状态码 400 (Bad Request) 和类型 "urn:ietf:params:acme:error:badPublicKey" 的错误.问题文档详细信息应该 (SHOULD) 描述拒绝公钥的原因; 一些示例原因是:

  • "alg" 是 "RS256" 但模数 "n" 太小 (例如, 512 位)

  • "alg" 是 "ES256" 但 "jwk" 不包含有效的 P-256 公钥

  • "alg" 是 "EdDSA" 且 "crv" 是 "Ed448", 但服务器仅支持带有 "Ed25519" 的 "EdDSA"

  • 相应的私钥已知已被泄露

由于 ACME 中的客户端请求在扁平化 JSON 序列化中携带 JWS 对象, 因此它们必须将 Content-Type 头字段设置为 "application/jose+json".如果请求不满足此要求, 则服务器必须 (MUST) 返回状态码 415 (Unsupported Media Type) 的响应.

6.3. GET and POST-as-GET Requests (GET 和 POST-as-GET 请求)​

请注意, 通过签名 JWS 请求主体进行身份验证意味着没有实体主体的请求未经身份验证, 特别是 GET 请求.除了本节中描述的情况外, 如果服务器收到 GET 请求, 它必须 (MUST) 返回状态码 405 (Method Not Allowed) 和类型 "malformed" 的错误.

如果客户端希望从服务器获取资源 (否则将使用 GET 完成), 则它必须 (MUST) 发送带有如上所述的 JWS 主体的 POST 请求, 其中 JWS 的有效载荷是零长度八位字节串.换句话说, JWS 对象的 "payload" 字段必须 (MUST) 存在并设置为空字符串 ("").

我们将这些称为 "POST-as-GET" 请求.在接收到具有零长度 (因此非 JSON) 有效载荷的请求时, 服务器必须 (MUST) 对发送者进行身份验证并验证任何访问控制规则.否则, 服务器必须 (MUST) 将此请求视为与对同一资源的 GET 请求具有相同的语义.

服务器必须 (MUST) 允许对目录和 newNonce 资源的 GET 请求 (参见第 7.1 节), 以及对这些资源的 POST-as-GET 请求.这使客户端能够引导到 ACME 身份验证系统中.

6.4. Request URL Integrity (请求 URL 完整性)​

在部署中, 终止 HTTPS 的 TLS 的实体与操作逻辑 HTTPS 服务器的实体不同是很常见的, 中间有一个 "请求路由" 层.例如, ACME CA 可能有一个内容分发网络终止来自客户端的 TLS 连接, 以便它可以检查客户端请求以进行拒绝服务 (Denial-of-Service, DoS) 保护.

这些中介也可以更改 HTTPS 请求中未签名的请求值, 例如请求 URL 和头字段.ACME 使用 JWS 提供完整性机制, 该机制可防止中介将请求 URL 更改为另一个 ACME URL.

如第 6.2 节所述, 所有 ACME 请求对象在其保护头中携带 "url" 头参数.此头参数编码客户端将请求定向到的 URL.在 HTTP 请求中接收到此类对象时, 服务器必须 (MUST) 将 "url" 头参数与请求 URL 进行比较.如果两者不匹配, 则服务器必须 (MUST) 将请求拒绝为未授权.

除目录资源外, 所有 ACME 资源都使用服务器提供给客户端的 URL 进行寻址.在发送到这些资源的 POST 请求中, 客户端必须 (MUST) 将 "url" 头参数设置为服务器提供的确切字符串 (而不是对 URL 执行任何重新编码).服务器应该 (SHOULD) 执行相应的字符串相等性检查, 为每个资源配置提供给客户端的 URL 字符串, 并让资源检查请求在其 "url" 头参数中是否具有相同的字符串.如果字符串相等性检查失败, 服务器必须 (MUST) 将请求拒绝为未授权.

6.4.1. "url" (URL) JWS Header Parameter ("url" (URL) JWS 头参数)​

"url" 头参数指定此 JWS 对象所针对的 URL [RFC3986]."url" 头参数必须 (MUST) 在 JWS 的保护头中携带."url" 头参数的值必须 (MUST) 是表示目标 URL 的字符串.

6.5. Replay Protection (重放保护)​

为了保护 ACME 资源免受任何可能的重放攻击, ACME POST 请求具有强制性的防重放机制.此机制基于服务器维护其已颁发的 nonce 列表, 并要求客户端的任何签名请求携带此类 nonce.

ACME 服务器使用 HTTP Replay-Nonce 头字段向客户端提供 nonce, 如第 6.5.1 节所述.服务器必须 (MUST) 在对 POST 请求的每个成功响应中包含 Replay-Nonce 头字段, 并应该 (SHOULD) 在错误响应中也提供它.

ACME 客户端发送的每个 JWS 必须 (MUST) 在其保护头中包含 "nonce" 头参数, 其内容如第 6.5.2 节中定义.作为 JWS 验证的一部分, ACME 服务器必须 (MUST) 验证 "nonce" 头的值是服务器先前在 Replay-Nonce 头字段中提供的值.一旦 nonce 值出现在 ACME 请求中, 服务器必须 (MUST) 将其视为无效, 就像它从未颁发过的值一样.

当服务器因其 nonce 值不可接受 (或不存在) 而拒绝请求时, 它必须 (MUST) 提供 HTTP 状态码 400 (Bad Request), 并指示 ACME 错误类型 "urn:ietf:params:acme:error:badNonce".带有 "badNonce" 错误类型的错误响应必须 (MUST) 包含一个 Replay-Nonce 头字段, 其中包含服务器将在原始查询的重试中接受的新鲜 nonce (并且可能在其他请求中, 根据服务器的 nonce 范围策略).在收到此类响应时, 客户端应该 (SHOULD) 使用新 nonce 重试请求.

用于生成和跟踪 nonce 的精确方法由服务器决定.例如, 服务器可以为每个响应生成一个随机的 128 位值, 保留已颁发 nonce 的列表, 并在使用时从此列表中删除 nonce.

除了上述关于在 "badNonce" 响应中颁发的 nonce 的约束外, ACME 不限制服务器如何限定 nonce 的范围.客户端可以 (MAY) 假设 nonce 具有广泛的范围, 例如, 通过为所有请求使用单个 nonce 池.但是, 在响应 "badNonce" 错误时重试时, 客户端必须 (MUST) 使用错误响应中提供的 nonce.服务器应该将 nonce 的范围设置得足够广, 以便不经常需要重试.

6.5.1. Replay-Nonce (Replay-Nonce 头字段)​

Replay-Nonce HTTP 头字段包含服务器生成的值, 服务器可以使用该值来检测未来客户端请求中的未授权重放.服务器必须 (MUST) 以这样的方式生成 Replay-Nonce 头字段中提供的值, 即它们对每条消息都是唯一的, 具有高概率, 并且对除服务器之外的任何人都是不可预测的.例如, 随机生成 Replay-Nonces 是可以接受的.

Replay-Nonce 头字段的值必须 (MUST) 是根据 [RFC7515] 第 2 节中描述的 base64url 编码进行编码的八位字节串.客户端必须 (MUST) 忽略无效的 Replay-Nonce 值.Replay-Nonce 头字段的 ABNF [RFC5234] 如下:

base64url = ALPHA / DIGIT / "-" / "_"

Replay-Nonce = 1*base64url

Replay-Nonce 头字段不应该 (SHOULD NOT) 包含在 HTTP 请求消息中.

6.5.2. "nonce" (Nonce) JWS Header Parameter ("nonce" (Nonce) JWS 头参数)​

"nonce" 头参数提供一个唯一值, 使 JWS 的验证者能够识别何时发生重放."nonce" 头参数必须 (MUST) 在 JWS 的保护头中携带.

"nonce" 头参数的值必须 (MUST) 是八位字节串, 根据 [RFC7515] 第 2 节中描述的 base64url 编码进行编码.如果 "nonce" 头参数的值根据此编码无效, 则验证者必须 (MUST) 将 JWS 拒绝为格式错误.

6.6. Rate Limits (速率限制)​

ACME 服务器可以对资源创建进行速率限制, 以确保公平使用并防止滥用.一旦超过速率限制, 服务器必须 (MUST) 响应类型为 "urn:ietf:params:acme:error:rateLimited" 的错误.此外, 服务器应该 (SHOULD) 发送 Retry-After 头字段 [RFC7231], 指示当前请求何时可能再次成功.如果有多个速率限制, 那就是所有速率限制都允许使用完全相同参数的当前请求再次访问的时间.

除了错误响应的人类可读 "detail" 字段外, 服务器可以 (MAY) 在 Link 头字段 [RFC8288] 中发送一个或多个链接关系, 使用 "help" 链接关系类型指向有关被触发的特定速率限制的文档.

6.7. Errors (错误)​

错误可以在 HTTP 层和挑战对象中报告, 如第 8 节所定义.ACME 服务器可以返回带有 HTTP 错误响应代码 (4XX 或 5XX) 的响应.例如, 如果客户端使用本文档中不允许的方法提交请求, 则服务器可以 (MAY) 返回状态码 405 (Method Not Allowed).

当服务器以错误状态响应时, 它应该 (SHOULD) 使用问题文档 [RFC7807] 提供附加信息.为了便于自动响应错误, 本文档定义了以下标准令牌, 用于 "type" 字段 (在 ACME URN 命名空间 "urn:ietf:params:acme:error:" 内):

类型描述
accountDoesNotExist请求指定的账户不存在
alreadyRevoked请求指定要吊销的证书已被吊销
badCSRCSR 不可接受 (例如, 由于密钥太短)
badNonce客户端发送了不可接受的防重放 nonce
badPublicKeyJWS 由服务器不支持的公钥签名
badRevocationReason提供的吊销原因不被服务器允许
badSignatureAlgorithmJWS 使用服务器不支持的算法签名
caa证书颁发机构授权 (Certification Authority Authorization, CAA) 记录禁止 CA 颁发证书
compound特定错误条件在 "subproblems" 数组中指示
connection服务器无法连接到验证目标
dns标识符验证期间 DNS 查询出现问题
externalAccountRequired请求必须包含 "externalAccountBinding" 字段的值
incorrectResponse收到的响应与挑战的要求不匹配
invalidContact账户的联系 URL 无效
malformed请求消息格式错误
orderNotReady请求尝试完成尚未准备好完成的订单
rateLimited请求超过速率限制
rejectedIdentifier服务器不会为该标识符颁发证书
serverInternal服务器遇到内部错误
tls服务器在验证期间收到 TLS 错误
unauthorized客户端缺乏足够的授权
unsupportedContact账户的联系 URL 使用了不支持的协议方案
unsupportedIdentifier标识符是不支持的类型
userActionRequired访问 "instance" URL 并在那里采取指定的操作

此列表并非详尽无遗.服务器可以 (MAY) 返回其 "type" 字段设置为上面定义的 URI 以外的 URI 的错误.服务器禁止 (MUST NOT) 对未在相应 IANA 注册表中列出的错误使用 ACME URN 命名空间 (参见第 9.6 节).客户端应该 (SHOULD) 显示所有错误的 "detail" 字段.

在本文档的其余部分, 我们使用上表中的令牌来引用错误类型, 而不是完整的 URN.例如, "类型为 'badCSR' 的错误" 是指 "type" 值为 "urn:ietf:params:acme:error:badCSR" 的错误文档.

6.7.1. Subproblems (子问题)​

有时 CA 可能需要响应一个请求返回多个错误.此外, CA 可能需要将错误归因于特定标识符.例如, newOrder 请求可能包含 CA 无法为其颁发证书的多个标识符.在这种情况下, ACME 问题文档可以 (MAY) 包含 "subproblems" 字段, 包含问题文档的 JSON 数组, 每个文档可以 (MAY) 包含 "identifier" 字段.如果存在, "identifier" 字段必须 (MUST) 包含 ACME 标识符 (第 9.7.7 节).

"identifier" 字段禁止 (MUST NOT) 出现在 ACME 问题文档的顶层.它只能出现在子问题中.子问题不需要都具有相同的类型, 并且它们不需要与顶层类型匹配.

ACME 客户端可以选择使用子问题的 "identifier" 字段作为提示, 如果省略该标识符, 操作将成功.例如, 如果订单包含十个 DNS 标识符, 并且 newOrder 请求返回带有两个子问题 (引用其中两个标识符) 的问题文档, 则 ACME 客户端可以选择提交另一个仅包含问题文档中未列出的八个标识符的订单.

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Link: \`https://example.com/acme/directory\`;rel="index"

{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Some of the identifiers requested were rejected",
"subproblems": [
{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Invalid underscore in DNS name \"_example.org\"",
"identifier": {
"type": "dns",
"value": "_example.org"
}
},
{
"type": "urn:ietf:params:acme:error:rejectedIdentifier",
"detail": "This CA will not issue for \"example.net\"",
"identifier": {
"type": "dns",
"value": "example.net"
}
}
]
}


7. Certificate Management (证书管理)​

在本节中, 我们描述 ACME 启用的证书管理功能:

  • 账户创建 (Account Creation)
  • 订购证书 (Ordering a Certificate)
  • 标识符授权 (Identifier Authorization)
  • 证书颁发 (Certificate Issuance)
  • 证书吊销 (Certificate Revocation)

7.1. Resources (资源)​

ACME 被构建为基于 HTTP 的应用程序, 具有以下类型的资源:

  • 账户资源 (Account Resources), 表示有关账户的信息 (第 7.1.2 节, 第 7.3 节)
  • 订单资源 (Order Resources), 表示账户颁发证书的请求 (第 7.1.3 节)
  • 授权资源 (Authorization Resources), 表示账户对标识符采取行动的授权 (第 7.1.4 节)
  • 挑战资源 (Challenge Resources), 表示证明对标识符控制的挑战 (第 7.5 节, 第 8 节)
  • 证书资源 (Certificate Resources), 表示已颁发的证书 (第 7.4.2 节)
  • "directory" 资源 (第 7.1.1 节)
  • "newNonce" 资源 (第 7.2 节)
  • "newAccount" 资源 (第 7.3 节)
  • "newOrder" 资源 (第 7.4 节)
  • "revokeCert" 资源 (第 7.6 节)
  • "keyChange" 资源 (第 7.3.5 节)

服务器必须 (MUST) 提供 "directory" 和 "newNonce" 资源.

ACME 对不同的管理功能使用不同的 URL.每个功能都与其相应的 URL 一起列在目录中, 因此客户端只需要配置目录 URL.这些 URL 通过几个不同的链接关系 [RFC8288] 连接.

"up" 链接关系与挑战资源一起使用, 以指示挑战所属的授权资源.对于某些媒体类型, 它也从证书资源使用, 以指示客户端可以从中获取可用于验证原始资源中证书的 CA 证书链的资源.

"index" 链接关系出现在除目录之外的所有资源上, 并指示目录的 URL.

下图说明了 ACME 服务器上资源之间的关系.在大多数情况下, 这些关系由资源的 JSON 表示中作为字符串提供的 URL 表示.带有引号标签的线表示 HTTP 链接关系.

                              directory
|
+--> newNonce
|
+----------+----------+-----+-----+------------+
| | | | |
| | | | |
V V V V V
newAccount newAuthz newOrder revokeCert keyChange
| | |
| | |
V | V
account | order --+--> finalize
| | |
| | +--> cert
| V
+---> authorization
| ^
| | "up"
V |
challenge

ACME Resources and Relationships

下表说明了建立与服务器的新账户、证明对标识符的控制、颁发证书以及在颁发后某个时间获取更新证书所需的典型请求序列."->" 是指向创建资源的 Location 头字段的助记符.

操作请求响应
获取目录GET directory200
获取 nonceHEAD newNonce200
创建账户POST newAccount201 -> account
提交订单POST newOrder201 -> order
获取挑战POST-as-GET order's authorization urls200
响应挑战POST authorization challenge urls200
轮询状态POST-as-GET order200
完成订单POST order's finalize url200
轮询状态POST-as-GET order200
下载证书POST-as-GET order's certificate url200

本节的其余部分提供了这些资源如何构建以及 ACME 协议如何使用它们的详细信息.

7.1.1. Directory (目录)​

为了帮助客户端为每个 ACME 操作配置正确的 URL, ACME 服务器提供目录对象.这应该是配置客户端所需的唯一 URL.它是一个 JSON 对象, 其字段名称来自资源注册表 (第 9.7.5 节), 其值是相应的 URL.

字段值中的 URL
newNonce新 nonce
newAccount新账户
newOrder新订单
newAuthz新授权
revokeCert吊销证书
keyChange密钥更改

目录的 URL 没有约束, 除了它应该与其他 ACME 服务器资源的 URL 不同, 并且不应与其他服务冲突.例如:

  • 同时充当 ACME 和 Web 服务器的主机可能希望将根路径 "/" 保留用于 HTML "首页", 并将 ACME 目录放在路径 "/acme" 下.
  • 仅充当 ACME 服务器的主机可以将目录放在路径 "/" 下.

如果 ACME 服务器不实现预授权 (Pre-authorization) (第 7.4.1 节), 它必须 (MUST) 省略目录的 "newAuthz" 字段.

该对象可以 (MAY) 另外包含 "meta" 字段.如果存在, 它必须 (MUST) 是 JSON 对象; 对象中的每个字段都是与 ACME 服务器提供的服务相关的元数据项.

定义了以下元数据项 (第 9.7.6 节), 所有这些都是可选的 (OPTIONAL):

termsOfService (可选, 字符串): 标识当前服务条款的 URL.

website (可选, 字符串): 定位提供有关 ACME 服务器更多信息的网站的 HTTP 或 HTTPS URL.

caaIdentities (可选, 字符串数组): ACME 服务器识别为引用自身的主机名, 用于 [RFC6844] 中定义的 CAA 记录验证.每个字符串必须 (MUST) 表示服务器期望在 CAA issue 或 issuewild 属性标签中看到的 "颁发者域名" (Issuer Domain Name) 的相同 ASCII 代码点序列.这允许客户端在配置 CAA 记录时确定要使用的正确颁发者域名.

externalAccountRequired (可选, 布尔值): 如果此字段存在并设置为 "true", 则 CA 要求所有 newAccount 请求包含 "externalAccountBinding" 字段, 将新账户与外部账户关联.

客户端通过向目录 URL 发送 GET 请求来访问目录.

HTTP/1.1 200 OK
Content-Type: application/json

{
"newNonce": "https://example.com/acme/new-nonce",
"newAccount": "https://example.com/acme/new-account",
"newOrder": "https://example.com/acme/new-order",
"newAuthz": "https://example.com/acme/new-authz",
"revokeCert": "https://example.com/acme/revoke-cert",
"keyChange": "https://example.com/acme/key-change",
"meta": {
"termsOfService": "https://example.com/acme/terms/2017-5-30",
"website": "https://www.example.com/",
"caaIdentities": ["example.com"],
"externalAccountRequired": false
}
}

7.1.2. Account Objects (账户对象)​

ACME 账户资源表示与账户关联的一组元数据.账户资源具有以下结构:

status (必需, 字符串): 此账户的状态.可能的值为 "valid", "deactivated" 和 "revoked".值 "deactivated" 应该用于指示客户端发起的停用, 而 "revoked" 应该用于指示服务器发起的停用.参见第 7.1.6 节.

contact (可选, 字符串数组): 服务器可以用来联系客户端以解决与此账户相关问题的 URL 数组.例如, 服务器可能希望通知客户端有关服务器发起的吊销或证书过期的信息.有关支持的 URL 方案的信息, 请参见第 7.3 节.

termsOfServiceAgreed (可选, 布尔值): 在 newAccount 请求中包含此字段, 值为 true, 表示客户端同意服务条款.此字段不能由客户端更新.

externalAccountBinding (可选, 对象): 在 newAccount 请求中包含此字段表示现有非 ACME 账户的持有者批准将该账户绑定到此 ACME 账户.此字段不可由客户端更新 (参见第 7.3.4 节).

orders (必需, 字符串): 一个 URL, 可以通过 POST-as-GET 请求从中获取此账户提交的订单列表, 如第 7.1.2.1 节所述.

{
"status": "valid",
"contact": [
"mailto:[email protected]",
"mailto:[email protected]"
],
"termsOfServiceAgreed": true,
"orders": "https://example.com/acme/orders/rzGoeA"
}
7.1.2.1. Orders List (订单列表)​

每个账户对象都包含一个 "orders" URL, 可以通过 POST-as-GET 请求从中获取账户创建的订单列表.请求的结果必须 (MUST) 是一个 JSON 对象, 其 "orders" 字段是一个 URL 数组, 每个 URL 标识属于该账户的订单.服务器应该 (SHOULD) 包括待处理的订单, 并且不应该 (SHOULD NOT) 在 URL 数组中包括无效的订单.服务器可以 (MAY) 返回不完整的列表, 以及带有 "next" 链接关系的 Link 头字段, 指示可以获取更多条目的位置.

HTTP/1.1 200 OK
Content-Type: application/json
Link: \`https://example.com/acme/directory\`;rel="index"
Link: \`https://example.com/acme/orders/rzGoeA?cursor=2\`;rel="next"

{
"orders": [
"https://example.com/acme/order/TOlocE8rfgo",
"https://example.com/acme/order/4E16bbL5iSw",
/* 为简洁起见未显示更多 URL */
"https://example.com/acme/order/neBHYLfw0mg"
]
}

7.1.3. Order Objects (订单对象)​

ACME 订单对象表示客户端对证书的请求, 并用于跟踪该订单直至颁发的进度.因此, 该对象包含有关所请求证书、服务器要求客户端完成的授权以及此订单产生的任何证书的信息.

status (必需, 字符串): 此订单的状态.可能的值为 "pending", "ready", "processing", "valid" 和 "invalid".参见第 7.1.6 节.

expires (可选, 字符串): 服务器将在此时间戳之后将此订单视为无效的时间戳, 以 [RFC3339] 中指定的格式编码.对于状态字段中具有 "pending" 或 "valid" 的对象, 此字段是必需的 (REQUIRED).

identifiers (必需, 对象数组): 订单涉及的标识符对象数组.

  • type (必需, 字符串): 标识符的类型.本文档定义了 "dns" 标识符类型.有关任何其他类型, 请参见第 9.7.7 节中定义的注册表.

  • value (必需, 字符串): 标识符本身.

notBefore (可选, 字符串): 证书中 notBefore 字段的请求值, 采用 [RFC3339] 中定义的日期格式.

notAfter (可选, 字符串): 证书中 notAfter 字段的请求值, 采用 [RFC3339] 中定义的日期格式.

error (可选, 对象): 处理订单时发生的错误 (如果有).此字段被构建为问题文档 [RFC7807].

authorizations (必需, 字符串数组): 对于待处理的订单, 客户端在颁发所请求的证书之前需要完成的授权 (参见第 7.5 节), 包括客户端过去为订单中指定的标识符完成的未过期授权.所需的授权由服务器策略决定; 订单标识符和所需授权之间可能没有 1:1 的关系.对于最终订单 (处于 "valid" 或 "invalid" 状态), 已完成的授权.每个条目都是一个 URL, 可以使用 POST-as-GET 请求从中获取授权.

finalize (必需, 字符串): 一旦订单的所有授权都满足, 必须将 CSR POST 到此 URL 以完成订单.成功完成的结果将是填充订单的证书 URL.

certificate (可选, 字符串): 响应此订单已颁发的证书的 URL.

{
"status": "valid",
"expires": "2016-01-20T14:09:07.99Z",

"identifiers": [
{ "type": "dns", "value": "www.example.org" },
{ "type": "dns", "value": "example.org" }
],

"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",

"authorizations": [
"https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI"
],

"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize",

"certificate": "https://example.com/acme/cert/mAt3xBGaobw"
}

newOrder 请求中类型为 "dns" 的任何标识符可以 (MAY) 将通配符域名作为其值.通配符域名由单个星号字符后跟单个句点字符 (".") 后跟 [RFC5280] 为在主体备用名称扩展中使用而定义的域名组成.服务器为通配符域名标识符返回的授权禁止 (MUST NOT) 在授权标识符值中包含星号和句点 (".") 前缀.返回的授权必须 (MUST) 包含可选的 "wildcard" 字段, 值为 true.

"authorizations" 和 "identifiers" 数组的元素一旦设置就是不可变的.服务器禁止 (MUST NOT) 在创建后更改任一数组的内容.如果客户端观察到任一数组内容的更改, 则它应该 (SHOULD) 将订单视为无效.

订单的 "authorizations" 数组应该 (SHOULD) 反映 CA 在决定颁发时考虑的所有授权, 即使某些授权在较早的订单或预授权事务中已完成.例如, 如果 CA 允许基于单个授权事务完成多个订单, 则它应该 (SHOULD) 在所有订单中反映该授权.

请注意, 仅仅因为授权 URL 列在订单对象的 "authorizations" 数组中并不意味着客户端需要采取行动.引用的授权可能已经有效的原因有几个:

  • 客户端作为先前订单的一部分完成了授权
  • 客户端先前预授权了标识符 (参见第 7.4.1 节)
  • 服务器基于外部账户授予客户端授权

客户端应该 (SHOULD) 检查订单的 "status" 字段以确定是否需要采取任何行动.


注意: 由于第7章内容非常长,本文件仅包含7.1-7.1.3节.7.1.4-7.6节将在Part 2中继续.



8. Identifier Validation Challenges (标识符验证挑战)​

世界上很少有标识符类型具有标准化机制来证明对给定标识符的拥有.在所有实际情况下, CA 依赖各种手段来测试申请具有给定标识符的证书的实体是否实际控制该标识符.

挑战为服务器提供保证, 账户持有者也是控制标识符的实体.对于每种类型的挑战, 必须满足以下条件: 为了实体成功完成挑战, 实体必须同时:

  • 持有用于响应挑战的账户密钥对的私钥, 以及
  • 控制所讨论的标识符.

第 10 节记录了本文档中定义的挑战如何满足这些要求.新挑战需要记录它们如何满足.

ACME 使用可扩展的挑战/响应框架进行标识符验证.服务器在发送给客户端的授权对象中呈现一组挑战 (作为 "challenges" 数组中的对象), 客户端通过向挑战 URL 发送 POST 请求中的响应对象进行响应.

本节描述了一组初始挑战类型.挑战类型的定义包括:

  1. 挑战对象的内容
  2. 响应对象的内容
  3. 服务器如何使用挑战和响应来验证对标识符的控制

挑战对象都包含以下基本字段:

type (必需, 字符串): 对象中编码的挑战类型.

url (必需, 字符串): 可以将响应发布到的 URL.

status (必需, 字符串): 此挑战的状态.可能的值为 "pending", "processing", "valid" 和 "invalid" (参见第 7.1.6 节).

validated (可选, 字符串): 服务器验证此挑战的时间, 以 [RFC3339] 中指定的格式编码.如果 "status" 字段为 "valid", 则此字段是必需的 (REQUIRED).

error (可选, 对象): 服务器验证挑战时发生的错误 (如果有), 被构建为问题文档 [RFC7807].可以使用子问题 (第 6.7.1 节) 指示多个错误.具有错误的挑战对象的状态必须 (MUST) 等于 "invalid".

所有其他字段由挑战类型指定.如果服务器将挑战的 "status" 设置为 "invalid", 它应该 (SHOULD) 还包括 "error" 字段以帮助客户端诊断挑战失败的原因.

不同的挑战允许服务器获得对标识符控制的不同方面的证明.在某些挑战中, 如 HTTP 和 DNS, 客户端直接证明其执行与标识符相关的某些操作的能力.在何种情况下向客户端提供哪些挑战的选择是服务器策略的问题.

本节中描述的标识符验证挑战都与域名验证有关.如果将来扩展 ACME 以支持其他类型的标识符, 则需要有新的挑战类型, 并且它们需要指定它们适用于哪些类型的标识符.

8.1. Key Authorizations (密钥授权)​

本文档中定义的所有挑战都使用密钥授权字符串.密钥授权是一个字符串, 它将挑战的令牌与密钥指纹连接起来, 由 "." 字符分隔:

keyAuthorization = token || '.' || base64url(Thumbprint(accountKey))

"Thumbprint" 步骤表示 [RFC7638] 中指定的计算, 使用 SHA-256 摘要 [FIPS180-4].如 [RFC7518] 中所述, 在进行计算之前, 必须 (MUST) 去除 JWK 对象字段中的任何前置零八位字节.

如下面各个挑战中所指定的, 挑战的令牌是完全由 URL 安全 base64 字母表中的字符组成的字符串."||" 运算符表示字符串的连接.

8.2. Retrying Challenges (重试挑战)​

ACME 挑战通常要求客户端设置一些网络可访问的资源, 服务器可以查询该资源以验证客户端控制标识符.在实践中, 在设置资源时服务器的查询失败并不罕见, 例如, 由于信息在集群中传播或防火墙规则尚未就位.

客户端不应该 (SHOULD NOT) 响应挑战, 直到它们相信服务器的查询将成功.如果服务器的初始验证查询失败, 服务器应该 (SHOULD) 在一段时间后重试查询, 以考虑设置响应 (如 DNS 记录或 HTTP 资源) 的延迟.精确的重试计划由服务器决定, 但服务器操作员应牢记计划试图适应的操作场景.鉴于重试旨在解决 HTTP 或 DNS 配置中的传播延迟等问题, 通常不应该有任何理由每 5 或 10 秒重试一次以上.当服务器仍在尝试时, 挑战的状态保持 "processing"; 只有在服务器放弃后才会标记为 "invalid".

服务器必须 (MUST) 通过挑战中的 "error" 字段和响应挑战资源请求的 Retry-After HTTP 头字段向客户端提供有关其重试状态的信息.服务器必须 (MUST) 在每次失败的验证查询后向挑战中的 "error" 字段添加一个条目.服务器应该 (SHOULD) 将 Retry-After 头字段设置为服务器下一次验证查询之后的时间, 因为挑战的状态在该时间之前不会改变.

客户端可以通过在新的 POST 请求中重新发送对挑战的响应 (使用新的 nonce 等) 来显式请求重试.这允许客户端在状态发生更改时 (例如, 在更新防火墙规则后) 请求重试.服务器应该 (SHOULD) 在收到此类 POST 请求时立即重试请求.为了避免通过客户端发起的重试进行拒绝服务攻击, 服务器应该 (SHOULD) 对此类请求进行速率限制.

8.3. HTTP Challenge (HTTP 挑战)​

通过 HTTP 验证, ACME 事务中的客户端通过证明它可以在该域名下可访问的服务器上配置 HTTP 资源来证明其对域名的控制.ACME 服务器挑战客户端在特定路径上配置文件, 并以特定字符串作为其内容.

由于域名可能解析为多个 IPv4 和 IPv6 地址, 服务器将自行决定连接到 DNS A 和 AAAA 记录中找到的至少一个主机.由于许多 Web 服务器以微妙且不直观的方式将默认 HTTPS 虚拟主机分配给特定的低特权租户用户, 因此挑战必须通过 HTTP 而不是 HTTPS 完成.

type (必需, 字符串): 字符串 "http-01".

token (必需, 字符串): 唯一标识挑战的随机值.此值必须 (MUST) 至少具有 128 位熵.它禁止 (MUST NOT) 包含 base64url 字母表之外的任何字符, 并且禁止 (MUST NOT) 包括 base64 填充字符 ("=").有关随机性要求的其他信息, 请参见 [RFC4086].

{
"type": "http-01",
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"status": "pending",
"token": "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
}

客户端通过从挑战中提供的 "token" 值和客户端的账户密钥构造密钥授权来完成此挑战.然后, 客户端将密钥授权作为资源配置在所讨论域名的 HTTP 服务器上.

配置资源的路径由固定前缀 "/.well-known/acme-challenge/" 组成, 后跟挑战中的 "token" 值.资源的值必须 (MUST) 是密钥授权的 ASCII 表示.

GET /.well-known/acme-challenge/LoqXcYV8...jxAjEuX0
Host: example.org

HTTP/1.1 200 OK
Content-Type: application/octet-stream

LoqXcYV8...jxAjEuX0.9jg46WB3...fm21mqTI

(在上面的示例中, "..." 表示令牌和密钥授权中的 JWK 指纹已被截断以适应页面.)

客户端使用空对象 (\{\}) 进行响应以确认服务器可以验证挑战.

POST /acme/chall/prV_B7yEyA4
Host: example.com
Content-Type: application/jose+json

{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "UQI1PoRi5OuXzxuX7V7wL0",
"url": "https://example.com/acme/chall/prV_B7yEyA4"
}),
"payload": base64url({}),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}

在接收到响应时, 服务器从挑战 "token" 值和当前客户端账户密钥构造并存储密钥授权.

给定挑战/响应对, 服务器通过验证资源是否按预期配置来验证客户端对域名的控制.

  1. 通过填充 URL 模板 [RFC6570] "http://\{domain\}/.well-known/acme-challenge/\{token\}" 构造 URL, 其中:

    • domain 字段设置为正在验证的域名; 以及
    • token 字段设置为挑战中的令牌.
  2. 验证生成的 URL 格式良好.

  3. 使用 HTTP GET 请求取消引用 URL.此请求必须 (MUST) 发送到 HTTP 服务器上的 TCP 端口 80.

  4. 验证响应的主体是格式良好的密钥授权.服务器应该 (SHOULD) 忽略主体末尾的空白字符.

  5. 验证 HTTP 服务器提供的密钥授权与服务器存储的密钥授权匹配.

服务器在取消引用 URL 时应该 (SHOULD) 遵循重定向.例如, 客户端可能使用重定向, 以便响应可以由集中式证书管理服务器提供.有关与重定向相关的安全考虑, 请参见第 10.2 节.

如果上述所有验证都成功, 则验证成功.如果请求失败, 或主体未通过这些检查, 则验证失败.

客户端应该 (SHOULD) 在挑战完成后取消为此挑战配置的资源, 即, 一旦挑战的 "status" 字段的值为 "valid" 或 "invalid".

请注意, 由于令牌同时出现在 ACME 服务器发送的请求和响应中的密钥授权中, 因此可以构建从请求到响应复制令牌的客户端.客户端应避免这种行为, 因为它可能导致跨站点脚本漏洞; 相反, 客户端应该在每个挑战的基础上进行显式配置.确实从请求到响应复制令牌的客户端必须 (MUST) 验证请求中的令牌与上面的令牌语法匹配 (例如, 它仅包含来自 base64url 字母表的字符).

8.4. DNS Challenge (DNS 挑战)​

当正在验证的标识符是域名时, 客户端可以通过为特定验证域名配置包含指定值的 TXT 资源记录来证明对该域名的控制.

type (必需, 字符串): 字符串 "dns-01".

token (必需, 字符串): 唯一标识挑战的随机值.此值必须 (MUST) 至少具有 128 位熵.它禁止 (MUST NOT) 包含 base64url 字母表之外的任何字符, 包括填充字符 ("=").有关随机性要求的其他信息, 请参见 [RFC4086].

{
"type": "dns-01",
"url": "https://example.com/acme/chall/Rg5dV14Gh1Q",
"status": "pending",
"token": "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
}

客户端通过从挑战中提供的 "token" 值和客户端的账户密钥构造密钥授权来完成此挑战.然后, 客户端计算密钥授权的 SHA-256 摘要 [FIPS180-4].

配置到 DNS 的记录包含此摘要的 base64url 编码.客户端通过在正在验证的域名前添加标签 "_acme-challenge" 来构造验证域名, 然后在该名称下配置带有摘要值的 TXT 记录.例如, 如果正在验证的域名是 "www.example.org", 则客户端将配置以下 DNS 记录:

_acme-challenge.www.example.org. 300 IN TXT "gfj9Xq...Rg85nM"

客户端使用空对象 (\{\}) 进行响应以确认服务器可以验证挑战.

POST /acme/chall/Rg5dV14Gh1Q
Host: example.com
Content-Type: application/jose+json

{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "SS2sSl1PtspvFZ08kNtzKd",
"url": "https://example.com/acme/chall/Rg5dV14Gh1Q"
}),
"payload": base64url({}),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}

在接收到响应时, 服务器从挑战 "token" 值和当前客户端账户密钥构造并存储密钥授权.

要验证 DNS 挑战, 服务器执行以下步骤:

  1. 计算存储的密钥授权的 SHA-256 摘要 [FIPS180-4]

  2. 查询验证域名的 TXT 记录

  3. 验证其中一条 TXT 记录的内容与摘要值匹配

如果上述所有验证都成功, 则验证成功.如果未找到 DNS 记录, 或 DNS 记录和响应有效载荷未通过这些检查, 则验证失败.

客户端应该 (SHOULD) 在挑战完成后取消为此挑战配置的资源记录, 即, 一旦挑战的 "status" 字段的值为 "valid" 或 "invalid".



RFC 8555 第9-12章摘要​

说明: 本文档提供RFC 8555第9-12章的关键要点摘要.完整技术细节请参考RFC 8555官方文档.

9. IANA Considerations (IANA考虑事项)​

9.1 媒体类型注册​

  • application/pem-certificate-chain: 用于证书链的PEM格式

9.2 Well-Known URI​

  • /.well-known/acme-challenge: HTTP挑战的标准路径

9.3 HTTP头字段​

  • Replay-Nonce: 防重放nonce头字段

9.4-9.5 JWS头参数​

  • url: JWS中的URL参数
  • nonce: JWS中的nonce参数

9.6 URN命名空间​

  • urn:ietf:params:acme: ACME协议的URN命名空间

9.7 新注册表​

IANA为ACME创建了以下注册表:

  1. Account Object Fields (账户对象字段)
  2. Order Object Fields (订单对象字段)
  3. Authorization Object Fields (授权对象字段)
  4. Error Types (错误类型)
  5. Resource Types (资源类型)
  6. Directory Metadata Fields (目录元数据字段)
  7. Identifier Types (标识符类型)
  8. Validation Methods (验证方法)

10. Security Considerations (安全考虑)​

10.1 威胁模型​

ACME的两个主要安全目标:

  1. 只有控制标识符的实体才能获得该标识符的授权
  2. 授权后,账户密钥的授权不能被另一个账户不当使用

通信渠道:

  • ACME通道:客户端与服务器之间的HTTPS请求
  • 验证通道:服务器执行验证查询的通道

10.2 授权的完整性​

密钥绑定:所有挑战都通过密钥授权将账户私钥与验证查询绑定.

潜在攻击:

  • MitM攻击:CDN或反向代理可能成为中间人
  • DNS攻击:攻击者可以通过DNS劫持影响验证
  • 托管提供商风险:托管服务提供商可能篡改验证

防御措施:

  • 使用DNSSEC验证的解析器
  • 从多个网络位置查询DNS
  • 应用DNS防护措施(如DNS0x20)

10.3 拒绝服务考虑​

CA应实施:

  • 速率限制
  • 资源配额
  • 验证查询的超时设置

10.4 服务器端请求伪造(SSRF)​

HTTP-01挑战可能被用于SSRF攻击,CA应该:

  • 拒绝私有IP地址
  • 限制重定向
  • 设置合理的超时

10.5 CA策略考虑​

CA应制定关于以下方面的策略:

  • 验证方法的选择
  • 证书有效期
  • 吊销条件

11. Operational Considerations (操作考虑)​

11.1 密钥选择​

推荐的密钥类型:

  • ECDSA P-256或P-384
  • RSA 2048位或更高

11.2 DNS安全​

使用DNS-01挑战时:

  • 确保DNS基础设施安全
  • 考虑使用DNSSEC
  • 保护DNS管理接口

11.3 令牌熵​

挑战令牌必须具有足够的熵:

  • 至少128位熵
  • 使用加密安全的随机数生成器

11.4 畸形证书链​

客户端应该:

  • 验证下载的证书链的完整性
  • 检查证书的有效期
  • 验证证书链的信任路径

12. References (参考文献)​

12.1 规范性参考文献 (部分列表)​

  • RFC2119: 关键词定义 (MUST, SHOULD, MAY等)
  • RFC5280: X.509证书和CRL配置
  • RFC7515: JSON Web Signature (JWS)
  • RFC7518: JSON Web Algorithms (JWA)
  • RFC8259: JSON数据格式
  • RFC2818: HTTPS
  • RFC3339: 日期和时间格式
  • RFC7807: HTTP API的问题详细信息

12.2 信息性参考文献 (部分列表)​

  • RFC3552: 互联网协议的安全考虑指南
  • RFC6844: DNS证书颁发机构授权(CAA)资源记录
  • RFC7525: TLS和DTLS的安全建议

附录​

Acknowledgements (致谢)​

RFC 8555的开发得到了众多IETF社区成员的贡献.

Authors' Addresses (作者地址)​

主要作者:

  • Richard Barnes (Cisco)
  • Jacob Hoffman-Andrews (EFF)
  • Daniel McCarney (Let's Encrypt)
  • James Kasten (University of Michigan)

相关资源​