3. 版本 2 副署名
副署名 (countersignature) 通常定义为确认主签名的第二个签名. 一个常见示例是公证人在文档上加盖的签名, 用来见证你已经签署了该文档. 公证人通常会包含时间戳, 用于指示公证发生的时间; 但是, COSE 尚未定义这类时间戳. 一旦未来文档定义了时间戳, 它可能会作为受保护头部参数包含进来. 因此, 将副署名应用于 COSE_Signature 或 COSE_Sign1 对象符合这个传统定义. 本文档扩展了副署名的上下文, 允许将其应用于已定义的所有安全结构. 即使副署名由同一用户应用, 如 [GROUP-OSCORE] 中那样, 也需要把副署名视为独立于初始操作的单独操作.
COSE 支持两种不同形式的副署名. 完整副署名使用 COSE_Countersignature 结构, 该结构与 COSE_Signature 具有相同结构. 因此, 完整副署名可以具有受保护属性和未受保护属性, 包括链式副署名. 缩略副署名使用 COSE_Countersignature0 结构. 该结构只包含签名值, 不包含其他内容. 这些结构不能相互转换; 由于签名计算包含一个用于标识所用结构的参数, 转换后的结构将无法通过签名验证.
版本 2 副署名改变了用于计算签名的算法, 不再使用 [RFC8152] 第 4.5 节指定的原始版本. 新版本现在包含为所有结构生成的密码学材料, 而不只是某个子集.
COSE 的设计目标是在指定数据结构时保持一致性. 其结果之一是, 对 COSE 而言, 可以把副署名概念从仅签署一个签名扩展到能够签署大多数结构, 而不必创建新的签名层. 创建副署名时, 需要清楚由此产生的安全属性. 当副署名应用于 COSE_Signature 或 COSE_Sign1 时, 会保留正常副署名语义. 也就是说, 副署名对某个签名的存在作出陈述, 并且在与尚待指定的时间戳一起使用时, 对该签名存在的某个时间点作出陈述. 当副署名应用于 COSE_Mac 或 COSE_Mac0 时, 负载以及 MAC 值都会包含在内. 当副署名应用于 COSE_Encrypt 或 COSE_Encrypt0 时, 它证明加密数据的存在. 需要注意, 证明加密数据不同于证明未加密数据. 如果后者才是期望目标, 则需要先对数据应用签名, 然后再加密该数据. 总是可以构造出这样的情况: 使用两个不同密钥会成功解密, tag 检查成功, 但产生两个完全不同的明文. 对加密数据的副署名无法检测这种情况.
3.1. 完整副署名
COSE_Countersignature 结构允许与 COSE_Signature 相同的一组能力. 这意味着签名的所有能力都会通过该结构复制出来. 具体而言, 副署名者不需要与被副署名内容的产生者相关, 因为密钥和算法标识可以放入副署名属性中. 这也意味着副署名本身也可以被副署名. 这是长期归档服务等协议所需的特性. 关于副署名如何使用的更多信息, 可参见 [RFC4998] 中描述的证据记录语法.
完整副署名结构可以根据上下文编码为带标签或不带标签. 带标签的 COSE_Countersignature 结构由 CBOR tag 19 标识. 副署名结构与签名对象上签名者使用的结构相同. 完整副署名的 CDDL 片段为:
COSE_Countersignature_Tagged = #6.19(COSE_Countersignature)
COSE_Countersignature = COSE_Signature
副署名各字段的细节见 [RFC9052] 第 4.1 节.
签名上的副署名示例见附录 A.1.1. 加密对象中的副署名示例见附录 A.3.1.
需要注意, 只有带附录的签名算法 (见 [RFC9052] 第 8.1 节) 可用于副署名. 这是因为正文应当能够在不评估副署名的情况下处理, 而对于带消息恢复的签名方案来说这是不可能的.
3.2. 缩略副署名
缩略副署名支持加密组消息传递, 这种场景要求识别消息发起者, 但又希望副署名尽可能小. 对于缩略副署名, 不提供任何与签名操作相关的受保护属性. 也就是说, 用于计算或验证缩略副署名的参数由描述加密, 签名或 MAC 处理的同一上下文提供.
缩略副署名的 CDDL 片段为:
COSE_Countersignature0 = bstr
表示签名值的字节字符串放在 Countersignature0 属性中. 随后, 该属性被编码为未受保护头部参数.
3.3. 签名和验证过程
为了创建签名, 需要一个定义良好的字节字符串. Countersign_structure 用于创建规范形式. 这个签名和验证过程接收副署名目标结构 (COSE_Signature, COSE_Sign1, COSE_Sign, COSE_Mac, COSE_Mac0, COSE_Encrypt 或 COSE_Encrypt0), 签名者信息 (COSE_Signature), 以及应用数据 (外部来源). Countersign_structure 是一个 CBOR 数组. 在计算签名之前, 副署名的目标结构需要完成其所有密码学功能. Countersign_structure 的字段按顺序如下:
context: 一个上下文文本字符串, 用于标识签名上下文. 上下文文本字符串是以下之一:
-
"CounterSignature": 当 other_fields 不存在时, 用于使用 COSE_Countersignature 结构的副署名. -
"CounterSignature0": 当 other_fields 不存在时, 用于使用 COSE_Countersignature0 结构的副署名. -
"CounterSignatureV2": 当 other_fields 存在时, 用于使用 COSE_Countersignature 结构的副署名. -
"CounterSignature0V2": 当 other_fields 存在时, 用于使用 COSE_Countersignature0 结构的副署名.
body_protected: 来自目标结构的序列化受保护属性, 编码为 bstr 类型. 如果没有受保护属性, 则使用零长度字节字符串.
sign_protected: 来自签名者结构的序列化受保护属性, 编码为 bstr 类型. 如果没有受保护属性, 则使用零长度字节字符串. 对 Countersignature0V2 属性会省略此字段.
external_aad: 应用从外部提供的附加认证数据 (AAD), 编码为 bstr 类型. 如果未提供此字段, 则默认为零长度字节字符串. 关于构造此字段的应用指导, 见 [RFC9052] 第 4.4 节.
payload: 要签名的负载, 编码为 bstr 类型. 无论其如何传输, 负载都放在这里.
other_fields: 如果目标结构中只有两个 bstr 字段, 则省略. 此字段是第二个字段之后所有 bstr 字段组成的数组. 例如, 对 COSE_Sign1 结构而言, 这会是一个包含签名值的单元素数组.
描述上述文本的 CDDL 片段为:
Countersign_structure = [
context : "CounterSignature" / "CounterSignature0" /
"CounterSignatureV2" / "CounterSignature0V2" /,
body_protected : empty_or_serialized_map,
? sign_protected : empty_or_serialized_map,
external_aad : bstr,
payload : bstr,
? other_fields : [+ bstr ]
]
计算副署名的方法:
-
创建 Countersign_structure 并用适当字段填充它.
-
使用第 4 节描述的编码方式, 将 Countersign_structure 编码为字节字符串, 以创建 ToBeSigned 值.
-
调用签名创建算法, 传入 K (用于签名的密钥), alg (用于签名的算法) 和 ToBeSigned (要签名的值).
-
将得到的签名值放到正确位置. 对完整副署名而言, 这是 COSE_Countersignature 结构的
"signature"字段 (见第 3.1 节). 对缩略副署名而言, 这是 Countersignature0 属性的值 (见第 3.2 节).
验证副署名的步骤:
-
创建 Countersign_structure 并用适当字段填充它.
-
使用第 4 节描述的编码方式, 将 Countersign_structure 编码为字节字符串, 以创建 ToBeSigned 值.
-
调用签名验证算法, 传入 K (用于验证的密钥), alg (用于签名的算法), ToBeSigned (要签名的值) 和 sig (要验证的签名).
除了执行签名验证外, 应用还会执行适当检查, 确保密钥与签名身份正确配对, 并确保签名身份在执行动作之前已获得授权.