第 4 章:在线票据协议

本章定义 Trusted_Ticket 的签发、查询、转换和降级流程。本章流程对应蓝图 §2.2 的设计意图。

4.1 适用前提

Trusted_Ticket 仅在终端能够联网访问 Ticket_Issuer 或在线撤销服务时使用。当在线服务不可用时,终端按 §4.5 自动回退到离线授权。

终端 MUST 同时支持 Authorization_Descriptor(第 3 章)和 Trusted_Ticket。终端 MUST NOT 拒绝接受合法签名的 Trusted_Ticket,即使该终端策略偏好离线授权。

4.2 签发流程

4.2.1 签发请求

授权人通过支持 CAP 的客户端(如手机 App、Web 控制台)向 Ticket_Issuer 发起签发请求。客户端与 Ticket_Issuer 之间的交互不在本规范范围内,但 Ticket_Issuer MUST:

  1. 验证请求来源是经过身份认证的授权人
  2. 验证授权人有权对目标 Fay 和资源进行授权
  3. 应用本地策略校验授权范围(如最长有效期、资源白名单)

4.2.2 签发步骤

Ticket_Issuer MUST 按以下步骤生成 Trusted_Ticket:

  1. 构造 Header:按 §2.4.2 设置 algtypkid
  2. 构造 Payload:按 §2.4.3 填充 TicketPayload,所有 required 字段 MUST 设置
  3. 校验约束:本地校验 exp - nbf ≤ 7 天
  4. JSON 序列化:将 Header 和 Payload 序列化为 UTF-8 JSON 字节
  5. Base64URL 编码:对 Header 和 Payload 字节分别进行 Base64URL 编码
  6. 签名:按 RFC 7515 计算签名输入 base64url(header) + "." + base64url(payload)
  7. 生成 Compact Serialization:拼接为 header.payload.signature 字符串
  8. 登记记录:在 Ticket_Issuer 内部维护已签发票据状态(用于撤销查询)

4.2.3 签发后交付

Trusted_Ticket 通过 HTTPS 等加密通道交付给目标 Fay 或其 iFay_Runtime。交付的具体方式不在本规范范围内。

4.3 终端校验流程

终端校验 Trusted_Ticket 与校验 Authorization_Descriptor 的差异在于:

  1. Trusted_Ticket 在请求时由 iFay_Runtime 完整传输给终端(不预先存储)
  2. 终端在联网时 MAY 实时查询 Ticket_Issuer 的撤销状态
  3. 校验失败时的错误码以 E_TICKET_* 前缀

4.3.1 校验请求

iFay_Runtime 发送的 AuthRequest 携带完整 Trusted_Ticket:

AuthRequest (body of ProtocolMessage) {
  required fay_id        : Fay_ID
  required resource_id   : Resource_ID
  required access_mode   : AccessMode
  required credential    : CredentialContent
}

CredentialContent {
  required type : enum["descriptor_ref", "ticket"]
  oneof:
    descriptor_id : string                  // type == "descriptor_ref"
    ticket        : string                  // type == "ticket",JWS Compact 字符串
}

4.3.2 校验步骤(按顺序执行)

终端 MUST 按以下顺序执行校验。任一步骤失败立即返回错误。

第 1 步:JWS 解析

终端解析 JWS Compact 字符串:

  • 拆分 header.payload.signature 三部分
  • Base64URL 解码 header 和 payload
  • 验证 header.typ == "cap-ticket+jws"
  • 验证 header.alg 在 §8 允许的算法集合内

任一步骤失败 → 返回 E_TICKET_MALFORMED

第 2 步:签名校验

终端使用 header.kid 对应的 Verification_Key 校验 JWS 签名(按 RFC 7515)。

  • 签名校验失败 → 返回 E_INVALID_SIGNATURE
  • kid 对应的密钥未注册或已撤销 → 返回 E_VERIFICATION_KEY_INVALID

第 3 步:有效期

按 §3.3.2 第 3 步的规则校验 nbfexp

第 4 步:主体、终端、资源、模式匹配

按 §3.3.2 第 4–6 步的规则进行匹配。错误码使用 E_TICKET_* 前缀(参见 §9)。

第 5 步:在线撤销查询(可选)

若终端策略要求在线撤销验证,终端 MAY 向 Ticket_Issuer 发起撤销状态查询:

TicketRevocationQuery {
  required jti : uuid
}

TicketRevocationResponse {
  required jti      : uuid
  required revoked  : boolean
  optional revoked_at : timestamp
}
  • 查询超时(默认 2 秒)→ 终端 SHOULD 拒绝该请求并返回 E_REVOCATION_QUERY_TIMEOUT,但 MAY 配置为允许通过(接受撤销延迟风险)
  • 查询返回 revoked == true → 返回 E_TICKET_REVOKED
  • 查询失败(如服务不可达)→ 终端 MAY 按本地缓存的撤销状态决定,超出缓存有效期则按超时处理

终端 SHOULD 缓存撤销查询结果,缓存有效期不超过 5 分钟。

4.3.3 校验通过后

校验通过后处理与 §3.3.3 一致,创建 Session 并返回 AuthResult。

4.4 转换为离线 Authorization_Descriptor

TicketPayload.convertible == true(默认)时,终端可在校验通过后将 Trusted_Ticket 转换为本地 Authorization_Descriptor 以供离线使用。

4.4.1 转换触发

转换 SHOULD 在以下情况自动执行:

  1. Trusted_Ticket 校验通过且 convertible == true
  2. 终端策略允许离线后续访问
  3. TicketPayload.exp 距当前时间足够长(建议 > 1 小时)

4.4.2 转换步骤

  1. 字段映射:按 §2.4.4 表格将 TicketPayload 字段映射到 DescriptorPayload
  2. 有效期约束:转换后的 not_after MUST 不超过 min(TicketPayload.exp, 转换时间 + 7 天)
  3. 元数据保留:将原始 Trusted_Ticket 的关键审计信息存入 metadata:
    • metadata["origin"] = "converted_from_ticket"
    • metadata["origin_jti"] = TicketPayload.jti
    • metadata["origin_iss"] = TicketPayload.iss
    • metadata["origin_kid"] = TicketHeader.kid
  4. 本地签名:终端使用其本地存储密钥对转换后的 payload 重新签名(参见 §4.4.3)
  5. 本地存储:将转换后的 Authorization_Descriptor 按 §3.2.3 加密存储

4.4.3 终端本地签名密钥

为支持转换流程,终端 MUST 持有一对本地签名密钥:

  • 私钥:保存在终端安全存储中,仅供本地转换流程使用
  • 公钥:作为 source == "pre-installed" 类型的 Verification_Key 注册到自身的密钥存储

转换后的 Authorization_Descriptor 的 signature.key_id MUST 标识该本地密钥,且 payload.issuer_id MUST 设置为 "local-conversion:" + terminal_id

终端在校验本地转换的 Authorization_Descriptor 时,使用本地公钥校验签名。这种设计确保:

  • 转换后的凭证可独立离线校验,不依赖原始 Ticket_Issuer
  • 转换信任链锚定在终端自身的密钥安全性

4.4.4 转换的局限性

本地转换的 Authorization_Descriptor MUST NOT:

  1. 在其他终端被信任(其签名 key_id 是本地的)
  2. 跨终端迁移使用
  3. 通过 Descriptor_Issuer 的撤销机制被撤销(必须通过本地删除)

终端 SHOULD 在以下情况主动删除本地转换的 Authorization_Descriptor:

  1. 收到原始 Trusted_Ticket 的撤销通知(通过 jti 关联)
  2. 凭证已过期超过 24 小时

4.5 在线到离线降级

4.5.1 降级触发

终端持续监测在线服务可用性。当满足以下任一条件时,触发降级:

  1. 与 Ticket_Issuer 的连接超过 30 秒不可达
  2. 撤销查询请求连续 3 次超时
  3. 网络栈报告全局网络不可用

4.5.2 降级行为

降级后,终端按以下规则处理:

  1. 拒绝接受新的 Trusted_Ticket(除非配置为离线接受)——避免无法在线验证撤销状态
  2. 已转换为本地 Authorization_Descriptor 的票据继续按 §3 规则使用
  3. 直接以 Authorization_Descriptor 进行授权校验的请求不受降级影响
  4. 终端 MAY 提示 iFay_Runtime 当前处于离线模式,使 Fay 可调整行为策略

4.5.3 恢复

终端检测到在线服务恢复后 MUST:

  1. 优先同步撤销列表,将离线期间可能漏掉的撤销声明应用到本地状态
  2. 重新检查活跃 Session,对于使用本地转换 Authorization_Descriptor 且已被原始 ticket 撤销的 Session,按第 5 章规则强制终止
  3. 恢复正常的 Trusted_Ticket 接受策略

恢复过程对 iFay_Runtime 透明,但终端 MAY 通过 SessionStateChanged 通知模式变更。

4.6 双凭证场景的优先级

当一个 Fay 同时持有针对同一资源的 Trusted_Ticket 和 Authorization_Descriptor 时,iFay_Runtime 的选择策略:

网络状态推荐选择原因
在线且 Ticket_Issuer 可达Trusted_Ticket时效性更强,可实时撤销
离线Authorization_Descriptor 或转换后的本地凭证Trusted_Ticket 无法在线验证撤销

iFay_Runtime MAY 在 AuthRequest 中提供回退凭证,使终端在主凭证校验失败时尝试备用凭证。本规范不强制定义回退机制的具体格式,但 SHOULD 通过两次独立的 AuthRequest 实现以避免协议复杂化。

4.7 Trusted_Ticket 与 Authorization_Descriptor 的语义一致性

当 iFay_Runtime 同时使用两种凭证类型时,终端 MUST 保证:

  1. 校验结果一致性:相同授权范围在两种凭证下校验通过/拒绝的判定结果一致
  2. 错误码语义一致性:等价的失败原因(如有效期、签名、撤销)使用语义对应的错误码(参见第 9 章)
  3. Session 创建一致性:通过两种凭证创建的 Session 在生命周期管理上无差异