7. Certificate Management (证书管理) - Part 2
(续第7章 Part 1)
7.1.4. Authorization Objects (授权对象)
ACME 授权对象表示服务器对账户代表标识符的授权。除了标识符之外, 授权还包括几个元数据字段, 例如授权的状态 (例如, "pending", "valid" 或 "revoked") 以及使用哪些挑战来验证对标识符的拥有。
ACME 授权资源的结构如下:
identifier (必需, 对象): 账户被授权代表的标识符。
-
type (必需, 字符串): 标识符的类型 (见下文和第 9.7.7 节)。
-
value (必需, 字符串): 标识符本身。
status (必需, 字符串): 此授权的状态。可能的值为 "pending", "valid", "invalid", "deactivated", "expired" 和 "revoked"。参见第 7.1.6 节。
expires (可选, 字符串): 服务器将在此时间戳之后将此授权视为无效的时间戳, 以 [RFC3339] 中指定的格式编码。对于 "status" 字段中具有 "valid" 的对象, 此字段是必需的 (REQUIRED)。
challenges (必需, 对象数组): 对于待处理的授权, 客户端可以完成以证明拥有标识符的挑战。对于有效授权, 已验证的挑战。对于无效授权, 尝试但失败的挑战。每个数组条目都是一个对象, 其参数是验证挑战所需的。客户端应该尝试完成其中一个挑战, 服务器应该将任何一个挑战视为足以使授权有效。
wildcard (可选, 布尔值): 对于作为包含值为通配符域名的 DNS 标识符的 newOrder 请求的结果创建的授权, 此字段必须 (MUST) 存在且为 true。对于其他授权, 它必须 (MUST) 不存在。通配符域名在第 7.1.3 节中描述。
本规范定义的唯一标识符类型是完全限定域名 (类型: "dns")。域名必须 (MUST) 以其在证书中出现的形式编码。也就是说, 它必须 (MUST) 根据 [RFC5280] 第 7 节中的规则编码。服务器必须 (MUST) 验证以 [RFC5890] 中定义的 ASCII 兼容编码前缀 "xn--" 开头的任何标识符值是否正确编码。通配符域名 (第一个标签为 "*") 禁止 (MUST NOT) 包含在授权对象中。如果授权对象传达对包含通配符域名的 newOrder DNS 标识符的基域的授权, 则可选授权 "wildcard" 字段必须 (MUST) 存在且值为 true。
第 8 节描述了一组用于域名验证的挑战。
{
"status": "valid",
"expires": "2015-03-01T14:09:07.99Z",
"identifier": {
"type": "dns",
"value": "www.example.org"
},
"challenges": [
{
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"type": "http-01",
"status": "valid",
"token": "DGyRejmCefe7v4NfDGDKfA",
"validated": "2014-12-01T12:05:58.16Z"
}
],
"wildcard": false
}
7.1.5. Challenge Objects (挑战对象)
ACME 挑战对象表示服务器提供的以特定方式验证客户端拥有标识符的提议。与上面列出的其他对象不同, 挑战对象没有单一的标准结构。挑战对象的内容取决于所使用的验证方法。挑战对象的一般结构和一组初始验证方法在第 8 节中描述。
7.1.6. Status Changes (状态更改)
每种 ACME 对象类型在其生命周期中都会经历一个简单的状态机。对象的 "status" 字段指示对象当前处于哪个状态。
挑战对象在 "pending" 状态下创建。当客户端响应挑战 (参见第 7.5.1 节) 并且服务器开始尝试验证客户端是否已完成挑战时, 它们转换到 "processing" 状态。请注意, 在 "processing" 状态内, 服务器可能会多次尝试验证挑战 (参见第 8.2 节)。同样, 客户端请求重试不会导致状态更改。如果验证成功, 挑战移动到 "valid" 状态; 如果出现错误, 挑战移动到 "invalid" 状态。
pending
|
| Receive
| response
V
processing <-+
| | | Server retry or
| | | client retry request
| +----+
|
|
Successful | Failed
validation | validation
+---------+---------+
| |
V V
valid invalid
State Transitions for Challenge Objects
授权对象在 "pending" 状态下创建。如果授权中列出的挑战之一转换到 "valid" 状态, 则授权也更改为 "valid" 状态。如果客户端尝试完成挑战但失败, 或者在授权仍处于待处理状态时出现错误, 则授权转换到 "invalid" 状态。一旦授权处于 "valid" 状态, 它可以过期 ("expired"), 被客户端停用 ("deactivated", 参见第 7.5.2 节), 或被服务器吊销 ("revoked")。
pending --------------------+
| |
Challenge failure | |
or | |
Error | Challenge valid |
+---------+---------+ |
| | |
V V |
invalid valid |
| |
| |
| |
+--------------+--------------+
| | |
| | |
Server | Client | Time after |
revoke | deactivate | "expires" |
V V V
revoked deactivated expired
State Transitions for Authorization Objects
订单对象在 "pending" 状态下创建。一旦订单对象中列出的所有授权都处于 "valid" 状态, 订单转换到 "ready" 状态。客户端向订单的 "finalize" URL 提交请求并且 CA 开始证书的颁发过程后, 订单移动到 "processing" 状态。一旦证书颁发, 订单进入 "valid" 状态。如果在这些阶段中的任何一个发生错误, 订单移动到 "invalid" 状态。如果订单过期或其授权之一进入 "valid" 以外的最终状态 ("expired", "revoked" 或 "deactivated"), 订单也会移动到 "invalid" 状态。
pending --------------+
| |
| All authz |
| "valid" |
V |
ready ---------------+
| |
| Receive |
| finalize |
| request |
V |
processing ------------+
| |
| Certificate | Error or
| issued | Authorization failure
V V
valid invalid
State Transitions for Order Objects
账户对象在 "valid" 状态下创建, 因为成功的 newAccount 请求后不需要进一步操作来创建账户。如果账户被客户端停用或被服务器吊销, 它会移动到相应的状态。
valid
|
|
+-----------+-----------+
Client | Server |
deactiv.| revoke |
V V
deactivated revoked
State Transitions for Account Objects
请注意, 根据服务器行为, 这些状态中的某些可能永远不会出现在 "status" 字段中。例如, 同步颁发的服务器永远不会在 "processing" 状态下显示订单。立即删除过期授权的服务器永远不会在 "expired" 状态下显示授权。
7.2. Getting a Nonce (获取 Nonce)
在向服务器发送 POST 请求之前, ACME 客户端需要有一个新鲜的防重放 nonce 放入 JWS 的 "nonce" 头中。在大多数情况下, 客户端将从先前的请求中获得 nonce。但是, 客户端有时可能需要获取新的 nonce, 例如, 在对服务器的第一个请求时或如果现有 nonce 不再有效。
要获取新鲜的 nonce, 客户端向服务器上的 newNonce 资源发送 HEAD 请求。服务器的响应必须 (MUST) 包含一个包含新鲜 nonce 的 Replay-Nonce 头字段, 并应该 (SHOULD) 具有状态码 200 (OK)。服务器也必须 (MUST) 响应此资源的 GET 请求, 返回空主体 (同时仍提供 Replay-Nonce 头), 状态码为 204 (No Content)。
HEAD /acme/new-nonce HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Replay-Nonce: oFvnlFP1wIhRlYS2jTaXbA
Cache-Control: no-store
Link: \`https://example.com/acme/directory\`;rel="index"
来自 newNonce 资源的响应的代理缓存可能导致客户端重复接收相同的 nonce, 从而导致 "badNonce" 错误。服务器必须 (MUST) 在 newNonce 资源的响应中包含带有 "no-store" 指令的 Cache-Control 头字段, 以防止此资源的缓存。
7.3. Account Management (账户管理)
在本节中, 我们描述 ACME 客户端如何在 ACME 服务器上创建账户以及在创建账户后对账户执行一些修改。
客户端通过向服务器的 newAccount URL 发送 POST 请求来创建新账户。请求的主体是一个存根账户对象, 包含以下字段的某个子集:
contact (可选, 字符串数组): 与第 7.1.2 节中定义的相应服务器字段含义相同。
termsOfServiceAgreed (可选, 布尔值): 与第 7.1.2 节中定义的相应服务器字段含义相同。
onlyReturnExisting (可选, 布尔值): 如果此字段存在且值为 "true", 则服务器禁止 (MUST NOT) 创建新账户 (如果账户尚不存在)。这允许客户端根据账户密钥查找账户 URL (参见第 7.3.1 节)。
externalAccountBinding (可选, 对象): 与第 7.1.2 节中定义的相应服务器字段含义相同。
POST /acme/new-account HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"jwk": {...},
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
"url": "https://example.com/acme/new-account"
}),
"payload": base64url({
"termsOfServiceAgreed": true,
"contact": [
"mailto:[email protected]",
"mailto:[email protected]"
]
}),
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}
服务器必须 (MUST) 忽略客户端发送的账户对象中 "orders" 字段中提供的任何值, 以及它不识别的任何其他字段。如果将来指定新字段, 这些字段的规范必须 (MUST) 描述客户端是否可以提供它们。服务器禁止 (MUST NOT) 在生成的账户对象中反映 "onlyReturnExisting" 字段或任何无法识别的字段。这允许客户端检测服务器何时不支持扩展字段。
服务器应该 (SHOULD) 验证 "contact" 字段中的联系 URL 是否有效并且服务器支持。如果服务器验证联系 URL, 它必须 (MUST) 支持 "mailto" 方案。客户端禁止 (MUST NOT) 在 "contact" 字段中提供包含 "hfields" [RFC6068] 或 "to" 组件中多个 "addr-spec" 的 "mailto" URL。如果服务器遇到不符合这些标准的 "mailto" 联系 URL, 则它应该 (SHOULD) 将其拒绝为无效。
如果服务器因使用不支持的方案而拒绝联系 URL, 它必须 (MUST) 返回类型为 "unsupportedContact" 的错误, 其中包含错误描述以及服务器认为可接受的联系 URL 类型。如果服务器因使用支持的方案但值无效而拒绝联系 URL, 则服务器必须 (MUST) 返回类型为 "invalidContact" 的错误。
如果服务器希望要求客户端同意使用 ACME 服务的条款, 它必须 (MUST) 在目录对象的 "meta" 字段的 "termsOfService" 子字段中指示可以访问此类条款的 URL, 并且服务器必须 (MUST) 拒绝未将 "termsOfServiceAgreed" 字段设置为 "true" 的 newAccount 请求。客户端不应该 (SHOULD NOT) 默认自动同意条款。相反, 它们应该 (SHOULD) 需要一些用户交互来同意条款。
服务器创建账户并存储用于验证 JWS 的公钥 (即 JWS 头的 "jwk" 元素) 以认证账户的未来请求。服务器在 201 (Created) 响应中返回此账户对象, 账户 URL 在 Location 头字段中。账户 URL 用作 JWS 中的 "kid" 值, 用于认证此账户的后续请求 (参见第 6.2 节)。账户 URL 也用于对此账户的管理操作的请求, 如下所述。
HTTP/1.1 201 Created
Content-Type: application/json
Replay-Nonce: D8s4D2mLs8Vn-goWuPQeKA
Link: \`https://example.com/acme/directory\`;rel="index"
Location: https://example.com/acme/acct/evOfKhNU60wg
{
"status": "valid",
"contact": [
"mailto:[email protected]",
"mailto:[email protected]"
],
"orders": "https://example.com/acme/acct/evOfKhNU60wg/orders"
}
7.3.1. Finding an Account URL Given a Key (根据密钥查找账户 URL)
如果服务器接收到使用其已为提供的账户密钥注册了账户的密钥签名的 newAccount 请求, 则它必须 (MUST) 返回状态码 200 (OK) 的响应, 并在 Location 头字段中提供该账户的 URL。此响应的主体表示在此请求之前服务器上存在的账户对象; 请求对象中的任何字段必须 (MUST) 被忽略。这允许拥有账户密钥但没有相应账户 URL 的客户端恢复账户 URL。
如果客户端希望查找现有账户的 URL 并且不希望在账户不存在时创建账户, 则它应该 (SHOULD) 通过向 newAccount URL 发送 POST 请求来实现, 该请求的 JWS 的有效载荷具有设置为 "true" 的 "onlyReturnExisting" 字段 (\{"onlyReturnExisting": true\})。如果客户端发送此类请求且账户不存在, 则服务器必须 (MUST) 返回状态码 400 (Bad Request) 和类型 "urn:ietf:params:acme:error:accountDoesNotExist" 的错误响应。
7.3.2. Account Update (账户更新)
如果客户端希望将来更新此信息, 它会向账户 URL 发送带有更新信息的 POST 请求。服务器必须 (MUST) 忽略对 "orders" 字段, "termsOfServiceAgreed" 字段 (参见第 7.3.3 节), "status" 字段 (除第 7.3.6 节允许的情况外) 或它不识别的任何其他字段的任何更新。如果服务器接受更新, 它必须 (MUST) 返回状态码 200 (OK) 的响应和生成的账户对象。
例如, 要更新上述账户中的联系信息, 客户端可以发送以下请求:
POST /acme/acct/evOfKhNU60wg HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "ax5RnthDqp_Yf4_HZnFLmA",
"url": "https://example.com/acme/acct/evOfKhNU60wg"
}),
"payload": base64url({
"contact": [
"mailto:[email protected]",
"mailto:[email protected]"
]
}),
"signature": "hDXzvcj8T6fbFbmn...rDzXzzvzpRy64N0o"
}
7.3.3. Changes of Terms of Service (服务条款的更改)
如上所述, 客户端可以通过将其账户对象中的 "termsOfServiceAgreed" 字段设置为 "true" 来表示其同意 CA 的服务条款。
如果自客户端最初同意以来服务器已更改其服务条款, 并且服务器不愿意在没有明确同意新条款的情况下处理请求, 则它必须 (MUST) 返回状态码 403 (Forbidden) 和类型 "urn:ietf:params:acme:error:userActionRequired" 的错误响应。此响应必须 (MUST) 包含带有链接关系 "terms-of-service" 和最新服务条款 URL 的 Link 头字段。
随错误返回的问题文档也必须 (MUST) 包括 "instance" 字段, 指示客户端应引导人类用户访问的 URL, 以获取有关如何同意条款的说明。
HTTP/1.1 403 Forbidden
Replay-Nonce: T81bdZroZ2ITWSondpTmAw
Link: \`https://example.com/acme/directory\`;rel="index"
Link: \`https://example.com/acme/terms/2017-6-02\`;rel="terms-of-service"
Content-Type: application/problem+json
Content-Language: en
{
"type": "urn:ietf:params:acme:error:userActionRequired",
"detail": "Terms of service have changed",
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
}
7.3.4. External Account Binding (外部账户绑定)
服务器可以 (MAY) 要求 "newAccount" 请求中存在 "externalAccountBinding" 字段的值。这可用于将 ACME 账户与非 ACME 系统中的现有账户关联, 例如 CA 客户数据库。
要启用 ACME 账户绑定, 运行 ACME 服务器的 CA 需要使用 ACME 之外的某种机制向 ACME 客户端提供 MAC 密钥和密钥标识符。密钥标识符必须 (MUST) 是 ASCII 字符串。MAC 密钥应该 (SHOULD) 以 base64url 编码形式提供, 以最大化非 ACME 配置系统和 ACME 客户端之间的兼容性。
然后, ACME 客户端计算绑定 JWS 以指示外部账户持有者批准 ACME 账户密钥。此 JWS 的有效载荷是正在注册的 ACME 账户密钥, 采用 JWK 形式。JWS 的保护头必须 (MUST) 满足以下标准:
- "alg" 字段必须 (MUST) 指示基于 MAC 的算法
- "kid" 字段必须 (MUST) 包含 CA 提供的密钥标识符
- "nonce" 字段禁止 (MUST NOT) 存在
- "url" 字段必须 (MUST) 设置为与外部 JWS 相同的值
JWS 的 "signature" 字段将包含使用 CA 提供的 MAC 密钥计算的 MAC 值。
(由于篇幅限制,7.3.5-7.6节将在下一个文件中继续)