本规范描述了使用密码学(特别是数字签名和相关数学证明)确保可验证凭证及类似受限数字文档的真实性和完整性的机制。
工作组正在积极寻求对本规范的实施反馈。为了退出候选推荐阶段,工作组要求至少有两个独立的实现支持规范中的每个强制性功能。有关一致性测试过程的详细信息,请参阅实施报告中列出的测试套件。
本规范描述了使用密码学(特别是数字签名和相关数学证明)确保可验证凭证及类似受限数字文档的真实性和完整性的机制。密码学证明为分布式系统的实现者提供了有用的功能。例如,证明可以用于:
数据完整性的操作在概念上很简单。创建密码学证明时,执行以下步骤:1)转换,2)哈希,3)生成证明。
转换是由转换算法描述的过程,该算法接收输入数据并为哈希过程做好准备。一个可能的转换示例是获取参加会议的人员姓名记录,按个人姓氏的字母顺序对列表进行排序,并将姓名按顺序逐行写在纸上。转换的示例包括规范化和二进制到文本编码。
哈希是由哈希算法描述的过程,该算法使用密码学哈希函数计算转换数据的标识符。这个过程在概念上类似于电话地址簿的功能,其中一个人获取某人的姓名(输入数据)并将该姓名映射到该人的电话号码(哈希)。密码学哈希函数的示例包括SHA-3和BLAKE-3。
生成证明是由证明序列化算法描述的过程,该算法计算一个值以保护输入数据的完整性免受修改或证明某个期望的信任阈值。这个过程在概念上类似于使用蜡封在信封上以建立对发送者的信任并表明信件在传输过程中未被篡改。证明序列化函数的示例包括数字签名、权益证明和知识证明。
验证密码学证明时,执行以下步骤:1)转换,2)哈希,3)验证证明。
在验证过程中,[=转换=]和[=哈希=]步骤在概念上与上述描述相同。
验证证明是由证明验证算法描述的过程,该算法应用密码学证明验证函数以查看输入数据是否可以被信任。可能的证明验证函数包括数字签名、权益证明和知识证明。
本规范详细说明了密码学软件架构师和实现者如何将这些过程打包成称为[=密码套件=]的东西,并将其提供给应用程序开发人员,以保护应用程序数据在传输和存储中的完整性。
本规范针对以下设计目标进行了优化:
虽然本规范主要关注可验证凭证,但该技术的设计是通用的,因此可以用于其他用例。在这些情况下,实施者应自行尽职调查并进行专家审查,以确定该技术是否适用于其用例。
一个符合规范的安全文档是任何可以转换为 JSON 文档的字节序列,并符合第[[[#proofs]]]节、第[[[#proof-purposes]]]节、第[[[#resource-integrity]]]节、第[[[#contexts-and-vocabularies]]]节和第[[[#dataintegrityproof]]]节中的相关规范性要求。
一个符合规范的密码学套件规范是任何符合第[[[#cryptographic-suites]]]节中相关规范性要求的规范。
一个符合规范的处理器是任何以软件和/或硬件实现的算法,该算法根据第[[[#algorithms]]]节中的相关规范性声明生成和/或消费[=符合规范的安全文档=]。符合规范的处理器在消费不符合规范的文档时必须产生错误。
本节定义了本规范中使用的术语。这些术语在本规范中出现时会附带链接。
一组参数,可与一个过程一起使用以独立验证证明。例如,密码学[=公钥=]可用作数字签名的验证方法;在这种用法中,它验证签名者拥有相关的密码学[=私钥=]。
在此定义中,“验证”和“证明”旨在广泛适用。例如,在 Diffie-Hellman 密钥交换期间,密码学公钥可能用于协商对称加密密钥,从而保证密钥协商过程的完整性。因此,这也是一种验证方法,即使过程描述可能未使用“验证”或“证明”一词。
表达主体与[=验证方法=]之间关系的一种方式。验证关系的一个示例是 身份验证。
本节规定了用于表达[=数据完整性证明=]及相关资源完整性的数据模型。
本规范中的所有数据模型属性和类型都映射到 URL。这些 URL 所在的词汇表定义在[[[?SECURITY-VOCABULARY]]]中。在受保护文档中执行此映射的显式机制是 `@context` 属性。
映射机制由[[[JSON-LD11]]]定义。为了确保文档可以在不使用 JSON-LD 库的情况下进行互操作处理,文档作者被建议确保领域专家已完成以下工作:1)指定与 `@context` 属性相关的所有值的预期顺序,2)发布每个 `@context` 文件的密码学哈希,3)确认每个 `@context` 文件的内容适合预期的使用场景。
当文档由不使用 JSON-LD 库的处理器处理时,如果需要使用与 JSON-LD 环境中相同的语义,建议实现者:1)强制执行 `@context` 属性中的预期顺序和值,2)确保每个 `@context` 文件与其已知的密码学哈希匹配。
结合 JSON Schema 使用带有已发布密码学哈希的静态版本化 `@context` 文件,是实现上述机制的一种可接受方法。这种方法确保在不使用 JSON-LD 库的处理器中,正确的术语识别、类型和顺序得以实现。有关更多详细信息,请参阅[[[?VC-DATA-MODEL-2.0]]]中的 类型特定处理部分。
[=数据完整性证明=] 提供了关于证明机制、验证该证明所需的参数以及证明值本身的信息。所有这些信息都使用诸如[[[?SECURITY-VOCABULARY]]]之类的链接数据词汇表提供。
在对象上表达[=数据完整性证明=]时,必须使用`proof`属性。可验证凭证中的`proof`属性是一个[=命名图=]。如果存在,其值必须是以下属性中描述的单个对象或无序对象集。
可以像以下示例一样将证明添加到 JSON 文档中:
{ "myWebsite": "https://hello.world.example/" };
以下证明使用`eddsa-jcs-2022`密码套件[[?DI-EDDSA]]保护上述文档,该套件通过使用 JSON 规范化方案(JCS)[[?RFC8785]]转换输入数据并使用 Edwards 数字签名算法(EdDSA)对其进行数字签名来生成可验证的数字证明。
{ "myWebsite": "https://hello.world.example/", "proof": { "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022", "created": "2023-03-05T19:23:24Z", "verificationMethod": "https://di.example/issuer#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG", "proofPurpose": "assertionMethod", "proofValue": "zQeVbY4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYV 8nQApmEcqaqA3Q1gVHMrXFkXJeV6doDwLWx" } }
同样,可以将证明添加到如下的 JSON-LD 数据文档中:
{ "@context": {"myWebsite": "https://vocabulary.example/myWebsite"}, "myWebsite": "https://hello.world.example/" };
以下证明使用`ecdsa-rdfc-2019`密码套件[[?DI-ECDSA]]保护上述文档,该套件通过使用 RDF 数据集规范化方案[[?RDF-CANON]]转换输入数据并使用椭圆曲线数字签名算法(ECDSA)对其进行数字签名来生成可验证的数字证明。
{ "@context": [ {"myWebsite": "https://vocabulary.example/myWebsite"}, "https://w3id.org/security/data-integrity/v2" ], "myWebsite": "https://hello.world.example/", "proof": { "type": "DataIntegrityProof", "cryptosuite": "ecdsa-rdfc-2019", "created": "2020-06-11T19:14:04Z", "verificationMethod": "https://ldi.example/issuer#zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP", "proofPurpose": "assertionMethod", "proofValue": "zXb23ZkdakfJNUhiTEdwyE598X7RLrkjnXEADLQZ7vZyUGXX8cyJZR BkNw813SGsJHWrcpo4Y8hRJ7adYn35Eetq" } }
本规范支持表达日期和时间,例如通过`created`和`expires`属性。如果处理证明时检测到超出允许的时间范围,这些信息可能会间接暴露给个人。在显示与密码学证明有效性相关的日期和时间值时,建议实现者尊重个人的语言环境和本地日历偏好[[?LTLI]]。将时间戳转换为本地时间值时,预计会考虑个人的时区期望。有关向个人表示时间值的更多详细信息,请参阅。
{ "@context": [ {"myWebsite": "https://vocabulary.example/myWebsite"}, "https://w3id.org/security/data-integrity/v2" ], "myWebsite": "https://hello.world.example/", "proof": { "type": "DataIntegrityProof", "cryptosuite": "ecdsa-rdfc-2019", "created": "2020-06-11T19:14:04Z", // 该证明在创建一个月后过期 "expires": "2020-07-11T19:14:04Z", "verificationMethod": "https://ldi.example/issuer#zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP", "proofPurpose": "assertionMethod", "proofValue": "z98X7RLrkjnXEADJNUhiTEdwyE5GXX8cyJZRLQZ7vZyUXb23Zkdakf RJ7adYY8hn35EetqBkNw813SGsJHWrcpo4" } }
数据完整性规范支持在单个文档中包含多个证明的概念。识别了两种多证明方法:证明集(无序)和证明链(有序)。
证明集在需要由多个实体保护相同数据但证明顺序无关紧要的情况下很有用,例如合同上的一组签名。证明集没有顺序,通过将一组证明与文档中的`proof`键关联来表示。
{
"@context": [
{"myWebsite": "https://vocabulary.example/myWebsite"},
"https://w3id.org/security/data-integrity/v2"
],
"myWebsite": "https://hello.world.example/",
"proof": [{
// 这是集合中的一个证明
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T19:23:24Z",
"verificationMethod": "https://ldi.example/issuer/1#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG",
"proofPurpose": "assertionMethod",
"proofValue": "z4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQeVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV6"
}, {
// 这是集合中的另一个证明
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T13:08:49Z",
"verificationMethod": "https://pfps.example/issuer/2#z6MkGskxnGjLrk3gKS2mesDpuwRBokeWcmrgHxUXfnncxiZP",
"proofPurpose": "assertionMethod",
"proofValue": "z5QLBrp19KiWXerb8ByPnAZ9wujVFN8PDsxxXeMoyvDqhZ6Qnzr5CG9876zNht8BpStWi8H2Mi7XCY3inbLrZrm95"
}]
}
证明链在需要对相同数据进行多方签名且签名顺序重要的情况下非常有用,例如公证人在文档上对已创建的证明进行副签的情况。证明链需要保留证明的顺序,通过为至少一个证明提供一个`id`(例如 UUID [[?RFC9562]])以及另一个包含`previousProof`值的证明来标识前一个证明。
{
"@context": [
{"myWebsite": "https://vocabulary.example/myWebsite"},
"https://w3id.org/security/data-integrity/v2"
],
"myWebsite": "https://hello.world.example/",
"proof": [{
// 'id' 值标识此特定证明
"id": "urn:uuid:60102d04-b51e-11ed-acfe-2fcd717666a7",
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T19:23:42Z",
"verificationMethod": "https://ldi.example/issuer/1#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG",
"proofPurpose": "assertionMethod",
"proofValue": "zVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV64oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQe"
}, {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T21:28:14Z",
"verificationMethod": "https://pfps.example/issuer/2#z6MkGskxnGjLrk3gKS2mesDpuwRBokeWcmrgHxUXfnncxiZP",
"proofPurpose": "assertionMethod",
"proofValue": "z6Qnzr5CG9876zNht8BpStWi8H2Mi7XCY3inbLrZrm955QLBrp19KiWXerb8ByPnAZ9wujVFN8PDsxxXeMoyvDqhZ",
// 'previousProof' 值标识在此之前验证的证明
"previousProof": "urn:uuid:60102d04-b51e-11ed-acfe-2fcd717666a7"
}]
}
在保护文档中的数据时,明确划分被保护的数据非常重要,这包括文档中表达的所有图,除了包含与保护机制相关的数据的图,该图被称为证明图。创建这种分离使处理算法能够确定性地保护和验证受保护的文档。
在添加[=数据完整性证明=]之前,输入文档中包含的信息以一个或多个图的形式表达。为了确保来自不同[=数据完整性证明=]的信息不会意外混合,使用[=证明图=]的概念来封装每个[=数据完整性证明=]。文档中与`proof`属性相关联的每个值标识一个单独的图,有时称为命名图,类型为ProofGraph,其中包含一个[=数据完整性证明=]。
使用这些图在执行 JSON-LD 处理时具有具体效果,因为这能正确地将一个图中表达的语句与另一个图中的语句分开。限制其处理为其他媒体类型(如 JSON、YAML 或 CBOR)的实现者需要注意,如果将一个文档中的数据与另一个文档中的数据合并(例如两个文档中`id`值字符串相同),可能会出现问题。重要的是不要合并看似具有相似属性的对象,除非这些对象具有`id`属性和/或使用全局标识符类型(如 URL),否则无法判断两个对象是否表达了同一实体的信息。
描述其目的的证明有助于防止被误用于其他目的。[=证明目的=]使[=验证者=]能够了解证明创建者的意图,从而防止消息被意外滥用于其他目的。例如,一个仅用于断言(可能意图广泛共享)的签名消息被滥用为用于身份验证服务或执行某些操作(例如调用某种能力)的消息。
需要注意的是,[=证明目的=]与[[[?RFC7517]]]中的`key_ops`限制、[[[?WEBCRYPTOAPI]]]中的`KeyUsage`限制以及[[[?RFC5280]]]中的限制是不同的机制。[=证明目的=]表达了为什么创建[=证明=]及其预期的使用领域,而上述提到的其他机制旨在限制私钥的用途。[=证明目的=]会随[=证明=]一起“传递”,而密钥限制则不会。
以下是常用[=证明目的=]值的列表。
当[=符合规范的安全文档=]中包含指向外部资源的链接时,了解自证明创建以来所标识的资源是否已更改是很有必要的。这适用于远程检索的外部资源以及[=验证者=]可能拥有的资源本地缓存副本。
为了确认[=符合规范的安全文档=]引用的资源自文档被保护以来未发生更改,实现者可以在包含`id`属性的任何对象中包含一个名为`digestMultibase`的属性。如果存在,`digestMultibase`值必须是单个字符串值,或字符串值的列表,其中每个值都是Multibase编码的Multihash值。
JSON-LD 上下文作者应将`digestMultibase`添加到将用于引用其他资源的文档的上下文中,并包含相关的密码学摘要。例如,[[[?VC-DATA-MODEL-2.0]]]基础上下文(`https://www.w3.org/ns/credentials/v2`)包含`digestMultibase`属性。
以下是一个受资源完整性保护的对象示例:
{
...
"image": {
"id": "https://university.example.org/images/58473",
"digestMultibase": "zQmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
},
...
}
建议实现者参考适当的资源,例如 FIPS 180-4 安全哈希标准和 商业国家安全算法套件 2.0,以确保选择适合其用例的哈希算法。
执行 JSON-LD 处理的实现必须将以下 JSON-LD 上下文 URL 视为已解析,其中解析后的文档与下方对应的哈希值匹配:
上下文 URL 和哈希值 |
---|
URL: https://w3id.org/security/data-integrity/v2 SHA2-256 摘要:
|
URL: https://w3id.org/security/multikey/v1 SHA2-256 摘要:
|
URL: https://w3id.org/security/jwk/v1 SHA2-256 摘要:
|
可以通过运行以下命令(将 `<DOCUMENT_URL>` 替换为适当的值)在现代类 UNIX 操作系统的命令行界面中确认上述密码学摘要:`curl -sL -H "Accept: application/ld+json" <DOCUMENT_URL> | openssl dgst -sha256`
上述 JSON-LD 上下文解析到的安全词汇表术语位于 https://w3id.org/security# 命名空间中。也就是说,该词汇表中的所有安全术语的形式为 `https://w3id.org/security#TERM`,其中 `TERM` 是术语的名称。
执行 RDF 处理的实现必须将词汇表 URL 的 JSON-LD 序列化视为已解引用,其中解引用的文档与下方对应的哈希值匹配。
除了本规范定义的安全术语外,https://w3id.org/security# 命名空间还包括 [[[CID]]] [[CID]] 规范中定义的术语,并在上述上下文文件中进行了相应的映射。
在解引用 https://w3id.org/security# URL 时,返回数据的媒体类型取决于 HTTP 内容协商。具体如下:
媒体类型 | 描述和哈希值 |
---|---|
application/ld+json |
JSON-LD 格式的词汇表 [[?JSON-LD11]]。 SHA2-256 摘要:
|
text/turtle |
Turtle 格式的词汇表 [[?TURTLE]]。 SHA2-256 摘要:
|
text/html |
HTML+RDFa 格式的词汇表 [[?HTML-RDFA]]。 SHA2-256 摘要:
|
可以通过运行以下命令(将 `<MEDIA_TYPE>` 和 `<DOCUMENT_URL>` 替换为适当的值)在现代类 UNIX 操作系统的命令行界面中确认上述密码学摘要:`curl -sL -H "Accept: <MEDIA_TYPE>" <DOCUMENT_URL> | openssl dgst -sha256`
特定应用词汇表和规范的作者应确保其 JSON-LD 上下文和词汇表文件是永久可缓存的,可以使用上述缓存方法或功能等效的机制。
实现者可以在开发过程中从网络加载特定应用的 JSON-LD 上下文文件,但在生产环境中,应该永久缓存用于[=符合规范的安全文档=]的 JSON-LD 上下文文件,以提高其安全性和隐私特性。可以通过上述缓存方法或功能等效的机制实现处理速度目标。
某些应用程序(如数字钱包)能够保存任意可验证凭证或其他受数据完整性保护的文档,这些文档可能来自任何发行者并使用任何上下文。在生产环境中,这些应用可能需要加载外部链接资源(如 JSON-LD 上下文文件)。这预计会随着时间的推移增加用户选择、可扩展性和生态系统的去中心化升级。此类应用的作者被建议阅读本文档的安全性和隐私部分以获取进一步的考虑。
有关处理 JSON-LD 上下文和词汇表的更多信息,请参阅可验证凭证 v2.0:基础上下文和可验证凭证 v2.0:词汇表。
确保消费应用明确批准其将处理的输入文档的类型及其语义是必要的。不检查 JSON-LD 上下文值是否与已知的良好值匹配可能会导致安全漏洞,因为它们传递的语义可能存在差异。应用程序必须使用第 [[[#context-validation]]] 节中的算法,或实现等效保护的算法,以验证[=符合规范的安全文档=]中的上下文。上下文验证必须在运行第 [[[#verify-proof]]] 节或第 [[[#verify-proof-sets-and-chains]]] 节中的适用算法之后运行。
虽然第 [[[#context-validation]]] 节中描述的算法提供了一种检查上下文值的方法,以及一种安全处理未知上下文值的可选方法,但实现者可以使用替代方法或不同的步骤顺序,只要它们提供相同的保护。
例如,如果不需要进行 JSON-LD 处理,则应用程序可以遵循任何可信文档中提供的指导,而不是执行此检查,以正确理解该类型文档的语义。
另一种方法是配置应用程序使用 JSON-LD 上下文加载器(有时称为文档加载器),仅使用已批准上下文文件的本地副本。这将保证上下文文件及其密码学哈希永远不会更改,实际上与第 [[[#context-validation]]] 节中的算法结果相同。
另一种等效于第 [[[#context-validation]]] 节算法的替代方法是,应用程序维护一个已知上下文 URL 及其关联的批准密码学哈希的列表,而不存储每个上下文文件的本地副本。这将允许从网络安全加载这些上下文,而不会影响应用程序的安全预期。
另一种有效的方法是,发送应用程序通过协议(如请求可验证展示)将文档“压缩”为接收应用程序请求的内容,省略用于保护原始文档的额外发送方特定上下文值。只要密码学套件的验证算法提供成功的验证结果,这种转换是有效的,并将导致先前由省略的上下文压缩的术语扩展为完整的 URL。例如,先前基于发送方提供的上下文压缩为 `foo` 的术语(如 `https://ontology.example/v1`)将被“扩展”为类似 `https://ontology.example#foo` 的 URL,然后在接收应用程序应用 JSON-LD 压缩算法时再次“压缩”为相同的 URL。
`@context` 属性用于确保在处理本规范中的术语时,所有实现都使用相同的语义。例如,当处理诸如 `type` 这样的属性及其值(如 `DataIntegrityProof`)时,这一点尤为重要。
当应用程序保护文档时,如果文档中未提供 `@context` 属性,或者文档中使用的数据完整性术语未通过现有的 `@context` 属性值进行映射,实现应注入或追加一个 `@context` 属性,其值为 `https://w3id.org/security/data-integrity/v2`,或包含至少相同声明的一个或多个上下文,例如可验证凭证数据模型 v2.0 上下文(`https://www.w3.org/ns/credentials/v2`)。
不打算使用 JSON-LD 处理的实现可以选择不在文档的顶层包含 `@context` 声明。然而,如果未包含 `@context` 声明,则不得进行与本规范或相应密码套件相关的扩展(如添加新属性)。
HTML 处理器在检测到可恢复的错误时会继续处理。JSON-LD 处理器以类似方式运行。这种设计理念旨在确保开发人员可以仅使用 JSON-LD 语言中对其有用的部分,而不会因处理器对开发者可能不重要的内容抛出错误。这种设计导致 JSON-LD 处理器在遇到未定义的术语等情况时不会抛出错误,而是警告开发人员。
当从 JSON-LD 转换为 RDF 数据集(例如在规范化文档时 [[?RDF-CANON]]),未定义的术语和相对 URL 可能会被静默丢弃。当值被丢弃时,它们不会受到数字证明的保护。这会导致预期的不匹配,开发人员可能误以为某些数据已被保护,但实际上并未保护,因为未抛出错误。本规范要求在执行 JSON-LD 转换时,任何可恢复的数据丢失都必须导致错误,以避免开发人员的安全预期不匹配。
使用 JSON-LD 处理的实现(例如 RDF 数据集规范化 [[?RDF-CANON]])在 JSON-LD 处理器丢弃数据时(例如在输入文档中检测到未定义的术语时),必须抛出错误,错误类型应为 `DATA_LOSS_DETECTION_ERROR`。
同样,由于[=符合规范的安全文档=]可以从一个安全域传输到另一个安全域,处理[=符合规范的安全文档=]的[=符合规范的处理器=]不能假定文档的任何特定基础 URL。在反序列化为 RDF 时,实现必须确保基础 URL 设置为 null。
本节定义了本规范使用的数据类型。
本规范将密码套件标识符编码为可枚举的字符串,这在需要高效编码此类字符串的过程中(如压缩算法)非常有用。在支持字符串值数据类型的环境中(如 RDF [[?RDF-CONCEPTS]]),密码标识符内容通过数据类型设置为 `https://w3id.org/security#cryptosuiteString` 的文字值表示。
`cryptosuiteString` 数据类型定义如下:
“关联数据”一词用于描述一种推荐的最佳实践,用于通过使用标准(如 URL)标识事物及其属性,在 Web 上公开、共享和连接信息。当信息以关联数据的形式呈现时,可以轻松发现其他相关信息,并轻松将新信息与之关联。关联数据以去中心化的方式具有可扩展性,从而大大降低了大规模集成的障碍。
随着关联数据在各种应用中的使用增加,需要能够验证关联数据文档的真实性和完整性。本规范通过使用数学证明为数据文档添加了身份验证和完整性保护,同时不牺牲关联数据的特性,如可扩展性和可组合性。
虽然本规范提供了对关联数据进行数字签名的机制,但使用关联数据并不是获得本规范所提供某些优势的必要条件。
实现本规范的密码套件可用于保护可验证凭证和可验证展示。针对这些用例的实现者需要注意,在处理这些类型的文档时,可能需要进行额外的检查。
在某些用例中,确保证明中使用的[=验证方法=]与`issuer`(发行者)或`holder`(持有者)相关联是很重要的。这种关联可以通过验证证明的[=验证方法=]的`controller`属性值是否与标识`issuer`或`holder`的 URL 值匹配来检查,并确保验证方法在符合证明目的的验证关系下表达。这种关联表明`issuer`或`holder`是用于验证证明的[=验证方法=]的控制者。
文档作者和实现者应了解证明的有效期(通过`created`和`expires`属性表示)与凭证的有效期(通过`validFrom`和`validUntil`属性表示)之间的区别。这些属性有时可能表示相同的有效期,但在其他情况下可能不一致。在验证证明时,确保感兴趣的时间(可能是当前时间或其他时间)在证明的有效期内(即在`created`和`expires`之间)是很重要的。在验证可验证凭证时,确保感兴趣的时间在凭证的有效期内(即在`validFrom`和`validUntil`之间)也很重要。未能验证证明或凭证的有效期可能导致接受本应被拒绝的数据。
最后,实施者还应了解可验证凭证的吊销信息与[=验证方法=]的吊销和过期时间之间的区别。[=验证方法=]的吊销和过期时间通过`revocation`和`expires`属性表示,与诸如[=私钥=]被泄露或过期等事件相关,并可能提供时间信息,这些信息可能揭示控制者的细节,例如其安全实践或可能被泄露的时间。可验证凭证的吊销信息通过`credentialStatus`属性表示,与个人失去可验证凭证授予的特权等事件相关,并且不会提供时间信息,从而增强隐私性。
[=数据完整性证明=] 旨在让开发者易于使用,因此尽量减少生成证明时需要记住的信息量。通常,开发者只需提供 [=密码套件=] 的名称(例如 `eddsa-rdfc-2022`)即可开始创建证明。这些 [=密码套件=] 通常由具备必要密码学培训的人创建和审核,以确保使用安全的密码学原语组合。本节规定了编写密码套件规范的要求。
所有数据完整性密码套件规范的要求如下:
[=密码套件实例=] 使用 [=密码套件实例化算法=] 实例化,并以实现特定的方式提供给算法。实现可以使用 [[[?VC-EXTENSIONS]]] 文档来发现已知的 [=密码套件实例化算法=]。
许多 [=密码套件=] 在表达 [=数据完整性证明=] 时遵循相同的基本模式。本节规定了这种通用设计模式,一种称为 `DataIntegrityProof` 的 [=密码套件=] 类型,通过重用设计原语和源代码减少了编写和实现 [=密码套件=] 的负担。
在指定使用此设计模式的 [=密码套件=] 时,`proof` 值采用以下形式:
[=密码套件=] 设计者必须使用第 [[[#proofs]]] 节中定义的强制性 `proof` 值属性,并可以定义特定于其密码套件的其他属性。
从 2012 年到 2020 年,数据完整性密码套件中常见的一种设计模式是使用 `type` 属性为密码套件建立特定类型;
Ed25519Signature2020 密码套件 就是其中一个规范。这种模式增加了密码套件实现的负担,每个新密码套件都需要指定一个新的 JSON-LD 上下文,从而导致开发者体验不佳。
2020 年出现了一种简化的设计模式,开发者只需包含一个 JSON-LD 上下文即可支持所有现代密码套件。这鼓励了更多现代密码套件(例如 EdDSA 密码套件 [[?DI-EDDSA]] 和 ECDSA 密码套件 [[?DI-ECDSA]])基于本节描述的简化模式构建。
为了改善开发者体验,创建新数据完整性密码套件规范的作者应使用现代模式——其中 `type` 设置为 `DataIntegrityProof`;`cryptosuite` 属性携带密码套件的标识符;任何特定于密码套件的密码学数据都被封装(即,不直接暴露为应用层数据)在 `proofValue` 中。已知遵循此模式的密码套件规范列表可在
可验证凭证扩展的安全机制部分 文档中找到。
以下定义的算法适用于表示为JSON 对象的文档。本规范遵循[[[JSON-LD11-API]]]规范,将 JSON 对象表示为映射。一个不安全数据文档是一个映射,其中不包含任何证明值。一个输入文档是尚未添加当前证明的映射,但它可能包含由先前过程添加的证明值。一个安全数据文档是一个包含一个或多个证明值的映射。
实现者可以在以下算法的基础上实现合理的默认值和保护措施,以帮助减轻开发者错误、过度的资源消耗、新发现的攻击模型(针对这些模型有特定保护)以及其他改进。以下提供的算法是实现互操作性的最低要求,建议开发者加入其他措施,以构建更安全、更高效的生态系统。
本节描述了[=符合规范的处理器=]及其特定于应用程序的软件所使用的处理模型。当软件需要确保信息具有防篡改性时,它会执行以下步骤:
当软件需要使用通过本规范描述的机制传输给它的信息时,它会执行以下步骤:
以下算法指定了如何将数字证明添加到[=输入文档=]中,然后可以用来验证输出文档的真实性和完整性。所需输入包括[=输入文档=](映射 |inputDocument|)、[=密码套件实例=](结构体 |cryptosuite|)以及一组选项(映射 |options|)。输出是[=符合规范的安全文档=](映射)或错误。每当此算法对字符串进行编码时,必须使用 UTF-8 编码。
以下算法指定了如何从包含证明或证明集/链的受保护文档开始,逐步向证明集或证明链中添加证明。所需输入包括[=符合规范的安全文档=](映射 |securedDocument|)、[=密码套件=]([=密码套件实例=] |suite|)以及一组选项(映射 |options|)。输出是新的[=符合规范的安全文档=](映射)。每当此算法对字符串进行编码时,必须使用 UTF-8 编码。
此步骤添加对[=命名图=]的引用,并添加[=证明图=]中包含的所有声明的副本。此步骤至关重要,因为它在应用当前证明之前将任何匹配的证明“绑定”到文档。文档的 |proof| 值将在此算法的后续步骤中更新。
以下算法指定了如何通过验证数字证明来检查[=符合规范的安全文档=]的真实性和完整性。该算法的输入包括:
当某一步骤说明“必须引发错误”时,这意味着必须返回一个 [=验证结果=],其 [=verification result/verified=] 值为 `false`,且 [=verification result/errors=] 列表非空。
在[=证明集=]或[=证明链=]中,[=符合规范的安全文档=]具有一个 `proof` 属性,其中包含[=证明=]的列表 (|allProofs|)。以下算法提供了一种方法,用于通过验证 |allProofs| 中的每个证明来检查[=符合规范的安全文档=]的真实性和完整性。如果只需要验证 |allProofs| 中的部分证明,也可以采用其他方法。但如果选择验证部分证明,需要注意,任何包含 `previousProof` 的证明只有在其引用的证明也被验证通过时,才能被视为已验证。
所需输入是一个[=符合规范的安全文档=] (|securedDocument|)。生成一个与 |allProofs| 中每个证明对应的[=验证结果=]列表,并返回一个单一的综合[=验证结果=]作为输出。实现可以选择返回其他[=验证结果=]和/或任何其他元数据,作为综合[=验证结果=]的补充。
请参阅第 [[[#add-proof-set-chain]]] 节第 6 步中的注释,了解此步骤保护的文档属性和先前的证明。
以下算法提供了一种机制,用于确保应用程序在执行文档中输入的业务规则之前,理解与文档关联的上下文。有关此算法的更多原理,请参阅第 [[[#validating-contexts]]] 节。此算法的输入包括文档(映射 |inputDocument|)、一组已知的 JSON-LD 上下文(列表 |knownContext|)以及在检测到未知上下文时是否重新压缩的布尔值(布尔值 |recompact|)。
此算法返回一个 上下文验证结果,即一个 结构体,其结构项包括:
上下文验证算法如下:
实现可以包括其他警告或错误,以强制执行特定于实现或特定用例的进一步验证规则。
本规范中描述的算法以及各种密码学套件规范中描述的算法会抛出特定类型的错误。实现者可能会发现将这些错误传递给其他库或软件系统很有用。本节提供了错误的特定 URL 和描述,以便实现本规范所描述技术的生态系统在发生错误时能够更有效地互操作。
当通过 HTTP 接口公开这些错误时,实施者应使用 [[RFC9457]] 将错误数据结构编码为 问题详情 映射。如果使用 [[RFC9457]]:
以下部分描述了开发人员在实现本规范时需要注意的安全事项,以创建安全的软件。
密码学通过使用密钥来保护信息。掌握必要的密钥可以轻松地访问某些信息。同样的信息也可以通过计算上困难的暴力破解尝试来访问。所有现代密码学都要求这种计算上困难的方法在时间上保持困难,但科学和数学的突破可能会改变这一点。也就是说,密码学是有时效性的。
本规范通过假设当前使用的任何密码学方法在未来很可能会被破解,来规划所有密码学方法的过时性。软件系统必须能够随着时间的推移更改所使用的密码学方法,以继续保护信息安全。这些更改可能涉及增加所需密钥的大小或修改所使用的密码学原语。然而,一些密码学参数的组合可能实际上会降低安全性。基于这些假设,系统需要能够区分不同的安全密码学参数组合,也称为密码套件。在标识或版本化密码套件时,可以采用以下几种方法:参数、数字和日期。
参数化版本控制指定了密码套件中使用的特定密码学参数。例如,可以使用类似于 `RSASSA-PKCS1-v1_5-SHA1` 的标识符。这种方案的好处是,受过良好训练的密码学家可以通过标识符确定所有使用的参数。然而,这种方案的缺点是,大多数使用这些标识符的人并未受过良好训练,因此无法理解上述标识符代表的是一个不再安全的密码套件。此外,这种知识的缺乏可能会导致软件开发人员泛化密码套件标识符的解析,使得任何密码学原语的组合都变得可接受,从而降低安全性。理想情况下,密码套件应在软件中实现为特定的、可接受的密码学参数配置文件。
数字版本控制可能会指定主版本号和次版本号,例如 `1.0` 或 `2.1`。数字版本控制传达了一个特定的顺序,并暗示较高的版本号比较低的版本号更强大。这种方法的好处是,它消除了不太专业的开发人员可能无法理解的复杂参数,提供了一个更简单的模型,表明可能需要升级。然而,这种方法的缺点是,升级是否必要并不明确,因为软件版本号的增加通常不需要升级软件即可继续运行。这可能导致开发人员认为他们使用的特定版本是安全的,而实际上并非如此。理想情况下,使用密码套件的开发人员应定期审查这些套件以确保其持续安全。
基于日期的版本控制为特定密码套件指定了一个发布日期。日期(例如年份)的好处是,开发人员可以立即判断日期是相对较旧还是较新。看到一个旧日期可能会促使开发人员寻找更新的密码套件,而参数化或基于数字的版本控制方案可能不会。然而,基于日期的版本控制的缺点是,一些密码套件可能在 5-10 年内不会过期,这可能会促使开发人员寻找更新的密码套件,但却找不到更新的版本。尽管这可能会带来不便,但这种不便会导致更安全的生态系统行为。
现代密码学算法提供了许多可调参数和选项,以确保算法能够满足不同用例的多样化需求。例如,嵌入式系统的处理能力和内存环境有限,可能无法生成给定算法的最强数字签名。其他环境(如金融交易系统)可能只需要在交易期间保护数据一天,而其他环境可能需要保护数据数十年。为了满足这些需求,密码学算法设计者通常会提供多种配置密码学算法的方法。
密码学库的实现者通常会根据密码学算法设计者和规范作者创建的规范进行实现,使所有选项都可供使用其库的应用程序开发人员使用。这可能是因为他们不知道特定应用程序开发人员在特定密码学部署中可能需要哪些功能组合。因此,所有选项通常都会暴露给应用程序开发人员。
使用密码学库的应用程序开发人员通常没有必要的密码学专业知识和知识,无法为特定应用程序适当地选择密码学参数和选项。这种缺乏专业知识可能会导致为特定应用程序选择了不适当的密码学参数和选项。
本规范将优先级设置为保护应用程序开发人员优先于密码学库实现者,优先于密码学规范作者,优先于密码学算法设计者。基于这些优先级,提出以下建议:
上述指导旨在确保在架构的较低层提供有用的密码学选项和参数,同时不向可能无法完全理解每个选项的权衡利弊的应用程序开发人员暴露这些选项和参数。
第 [[[#versioning-cryptography-suites]]] 节强调了提供易于理解的密码套件时效性信息的重要性,而第 [[[#protecting-application-developers]]] 节进一步强调了减少需要指定的选项数量的必要性。实际上,第 [[[#cryptographic-suites]]] 节列出了密码套件的要求,包括算法、转换、哈希和序列化的详细规范。因此,密码套件的名称不需要包含所有这些细节,这意味着第 [[[#versioning-cryptography-suites]]] 节中提到的参数化版本控制既不必要也不可取。
推荐的密码套件命名约定是一个字符串,由签名算法标识符组成,与选项标识符(如果密码套件支持不兼容的实现选项)用连字符分隔,最后加上套件提出的大致年份。
例如,[[?DI-EDDSA]] 基于 EdDSA 数字签名,支持两种基于规范化方法的不兼容选项,并大约在 2022 年提出,因此它有两个不同的密码套件名称:eddsa-rdfc-2022
和 eddsa-jcs-2022
。
尽管 [[?DI-ECDSA]] 基于 ECDSA 数字签名,支持与 [[?DI-EDDSA]] 相同的两种不兼容规范化方法,并通过两组不同的椭圆曲线和哈希支持两种不同的安全级别(128 位和 192 位),但它只有两个密码套件名称:ecdsa-rdfc-2019
和 ecdsa-jcs-2019
。安全级别和相应的曲线及哈希由验证中使用的公钥的多密钥格式决定。
密码学灵活性 是一种设计频繁连接的信息安全系统以支持在多种密码学原语和/或算法之间切换的实践。密码学灵活性的主要目标是使系统能够快速适应新的密码学原语和算法,而无需对系统基础设施进行破坏性更改。因此,当某个特定的密码学原语(例如 SHA-1 算法)被认为不再安全时,可以通过简单的配置文件更改将系统重新配置为使用更新的原语。
当信息安全系统中的客户端和服务器定期联系时,密码学灵活性最为有效。然而,当特定密码学算法保护的消息是长期存在的(例如 可验证凭证),并且/或者客户端(持有者)可能无法轻松地重新联系服务器(发行者)时,密码学灵活性无法提供所需的保护。
密码学分层 是一种设计很少连接的信息安全系统以同时使用多种原语和/或算法的实践。密码学分层的主要目标是使系统能够在一个或多个密码学算法或原语失效的情况下,仍然保持对有效载荷的密码学保护。例如,使用 RSA、ECDSA 和 Falcon 算法并行对单个信息进行数字签名,可以提供一种机制,即使这三种数字签名算法中的两种失效,系统仍然可以利用未失效的密码学保护继续保护信息。开发人员被建议对所有可能需要保护一年或更长时间的签名内容利用此功能。
本规范同时支持这两种灵活性形式。它支持密码学灵活性,允许轻松地从一种算法切换到另一种算法。它还支持密码学分层,允许同时使用多种密码学算法,通常是并行的,这样可以在不依赖或要求其他算法的情况下使用任何一种算法来保护信息,同时仍然保持数字证明格式对开发人员的易用性。
一个 [=证明=] 包含一个 `proofValue`,其中可以嵌入与密码学证明相关的多个参数。例如,[[[?VC-DI-ECDSA]]] 规范中的选择性披露算法在 `proofValue` 中包含多个密码学签名,每个可选择披露的项目都有一个签名。这是为了使技术更易于应用程序开发人员使用并更安全。
本规范敦促规范作者使用单一值来抽象应用层很少需要的信息。类似于表达图像的 `data:` URL 将许多图像渲染参数封装为单一值,`proofValue` 属性(以及其他类似属性)将对应用程序开发人员无用的信息抽象出来,以简化对对应用程序重要的字段的识别。以这种方式抽象信息可以使数据结构更易于开发人员使用,同时减少例如由于编程错误添加或删除关键属性而意外破坏密码学层的可能性。
本规范中的数据完整性设计将通常仅对密码学层有用的信息抽象为单一属性,以减轻应用程序开发人员的负担并增强系统的安全性。
在密码保护过程中,有时对被保护的数据进行转换是有益的。这种“内联”转换可以使特定类型的密码保护与其承载的数据格式无关。例如,一些数据完整性密码套件利用 RDF 数据集规范化 [[?RDF-CANON]],将初始表示形式转换为规范化形式 [[?N-QUADS]],然后进行序列化、哈希和数字签名。只要表达被保护数据的任何语法都可以转换为这种规范化形式,数字签名就可以被验证。这使得相同的数字签名可以在 JSON、CBOR、YAML 和其他兼容语法中表达,而无需为每种语法创建密码证明。
能够在多种语法中表达相同的数字签名是有益的,因为系统通常具有其操作的本地数据格式。例如,一些系统基于 JSON 数据编写,而另一些系统基于 CBOR 数据编写。如果没有转换,内部以 CBOR 处理数据的系统需要将数字签名的数据结构存储为 JSON(或反之亦然)。这会导致数据的双重存储,并可能增加安全攻击面,如果存储在数据库中的未签名表示意外偏离了签名表示。通过使用转换,数字证明可以以本地数据格式存在,从而有助于防止数据库随时间发生不可检测的漂移。
本规范旨在通过利用“内联”数据转换避免对签名信息的重复存储。建议应用程序开发人员在其应用程序的本地数据格式中处理密码保护数据,而不是将密码证明与被保护数据分开存储。同时建议开发人员定期确认密码保护数据在写入和从应用程序存储中读取时未被篡改。
一些转换(例如 RDF 数据集规范化 [[?RDF-CANON]])具有针对攻击者可能利用的输入数据集的缓解措施,这些输入数据集可能会消耗过多的处理周期。这类攻击被称为 数据集投毒,所有现代 RDF 数据集规范化工具都需要检测此类恶意输入并停止处理。RDF 数据集规范化的测试套件包括此类投毒数据集,以确保所有符合规范的实现都具备这些缓解措施。一般来说,使用转换的密码套件规范需要缓解此类攻击,实施者应确保其使用的软件库强制执行这些缓解措施。这些攻击与任何资源耗尽攻击(例如故意减慢连接的 HTTP 客户端,从而耗尽服务器连接)属于同一类别。实施者在实施防御性安全策略时应考虑此类攻击。
任何 [=数据完整性证明=] 所保护的数据是 [=转换|转换后的数据=]。[=转换|转换后的数据=] 是由特定 [=密码套件=] 指定的 [=转换算法=] 生成的。这种保护机制不同于一些不对输入数据进行任何 [=转换=] 的传统数字签名机制。[=转换=] 的好处详见第 [[[#transformations]]] 节。
例如,[=密码套件=] 如 ecdsa-jcs-2019 和 eddsa-jcs-2022 使用 [[[?RFC8785]]] 将数据 [=转换|转换=] 为规范化 JSON,然后进行密码哈希和数字签名。这种方法的一个好处是,添加或删除不影响所签名信息含义的格式字符(例如空格、制表符和换行符)不会使数字签名失效。传统的数字签名机制不具备此功能。
其他 [=密码套件=] 如 ecdsa-rdfc-2019 和 eddsa-rdfc-2022 使用 [[[?RDF-CANON]]] 将数据 [=转换|转换=] 为规范化 N-Quads [[?N-QUADS]],然后进行密码哈希和数字签名。这种方法的一个好处是,密码签名可以在多种不同的语法(如 JSON、YAML 和 CBOR)中移植,而不会使签名失效。传统的密码签名机制不具备此功能。
实施者和开发人员被建议不要信任包含 [=数据完整性证明=] 的信息,除非该证明已被 [=验证=],并且验证后的数据由软件库返回值提供,该库已确认所有返回的数据均已成功受到保护。
应用数据的可检查性会影响系统效率和开发人员的生产力。当密码学保护的应用数据(如基于编码的二进制数据)无法被应用子系统(如数据库)轻松处理时,处理这些密码学保护信息的工作量会增加。例如,可以被数据库本地存储和索引的密码学保护负载将简化系统设计,使其:
同样,可以被多个上游网络系统处理的密码学保护负载提高了正确分层安全架构的能力。例如,如果上游系统不需要反复解码传入的负载,系统就能通过专门化上游子系统来分配处理负载,从而积极应对攻击。尽管在采取实质性行动之前始终需要检查数字签名,但其他上游检查可以在透明负载上执行——例如基于标识符的速率限制、签名过期检查或随机数/挑战检查——以拒绝明显的恶意请求。
此外,如果开发人员无法轻松查看系统中的数据,审计或调试系统正确性的能力将受到阻碍。例如,要求应用程序开发人员复制粘贴基于编码的应用数据会使开发变得更加困难,并增加明显错误被忽略的可能性,因为每条消息都需要通过手动操作的基于解码工具。
然而,有时正确的设计决策是使数据不透明。无需被其他应用子系统处理的数据,以及无需被应用开发人员修改或访问的数据,可以序列化为不透明格式。例如,数字签名值、密码学密钥参数以及其他仅需由密码学库访问而无需应用开发人员修改的数据字段。还有一些例子表明,当底层子系统不向应用开发人员暴露不透明数据的复杂性时,数据不透明性是合适的,例如执行静态加密的数据库。在这些情况下,应用开发人员继续使用透明的应用数据格式进行开发,而数据库负责管理将应用数据加密和解密到长期存储的复杂性。
本规范力求提供一种架构,其中应用数据保持其原生格式而不变得不透明,而其他密码学数据(如数字签名)则保持其不透明的二进制编码形式。密码套件的实现者被敦促在设计其套件时适当地使用数据不透明性,并在使应用数据不透明与在应用层提供密码学数据访问之间权衡设计取舍。
实现者通过从[=验证方法=]的定义到[=受控标识符文档=],并确保[=受控标识符文档=]也包含对[=验证方法=]的引用,来确保[=验证方法=]绑定到特定的控制器。此过程在 检索验证方法的算法中进行了描述。
当实现正在验证证明时,必须验证生成证明所用的[=验证方法=]不仅列在[=受控标识符文档=]中,还必须验证其确实用于生成正在验证的证明。此过程称为“验证关系验证”。
验证关系的过程在[[[CID]]]规范的 3.3 检索验证方法节中概述。
此过程用于确保密码学材料(如私有密码学密钥)不会被应用于非预期用途。例如,如果一个本应用于颁发可验证凭证的私有密码学密钥被用于登录网站(即用于身份验证),这就是密码学材料的误用。不检查验证关系是危险的,因为某些密码学材料的限制和保护配置文件可能由其预期用途决定。例如,一些应用程序可能被信任仅将密码学材料用于一个目的,或者某些密码学材料可能受到更高的保护,例如存储在数据中心的硬件安全模块中,而不是作为笔记本电脑上的未加密文件。
当实现正在验证证明时,必须验证[=证明目的=]是否与预期用途匹配。
此过程用于确保证明不会被应用程序误用于非预期目的,因为这对证明创建者来说是危险的。例如,如果一个证明声明其目的是用于保护可验证凭证中的断言,但却被用于[=身份验证=]以登录网站,这就是一种误用。在这种情况下,证明创建者将证明附加到他们期望分发给无限数量其他方的可验证凭证中。如果网站错误地接受此类证明作为[=身份验证=]而非其预期用途,则这些方中的任何一方都可以冒充证明创建者登录网站。
转换(如规范化)的执行方式会影响系统的安全特性。选择最佳的规范化机制取决于具体的用例。通常,满足所需安全要求的最简单机制是最佳选择。本节尝试为实现者在本规范中提到的两种主要规范化机制之间提供简单的指导,即 JSON 规范化方案 [[RFC8785]] 和 RDF 数据集规范化 [[RDF-CANON]]。
如果应用程序仅使用 JSON 且不依赖任何形式的 RDF 语义,那么使用基于 JSON 规范化方案 [[RFC8785]] 的密码套件是一个有吸引力的选择。
如果应用程序使用 JSON-LD 并需要保护文档的语义,那么使用基于 RDF 数据集规范化 [[RDF-CANON]] 的密码套件是一个有吸引力的选择。
实现者还被建议可以使用其他不执行任何转换的机制,这些机制通过将数据封装在密码学信封中而不是将证明嵌入数据中来保护数据,例如 JWTs [[?RFC7519]] 和 CWTs [[?RFC8392]]。这些方法在某些用例中具有简化的优势,但会牺牲本规范中详细描述的方法所提供的一些好处。
本规范使用的算法过程之一是规范化,这是一种[=转换=]。规范化是将可能以多种语义等价方式表达的信息作为输入,并以一种称为“规范形式”的单一方式表达所有输出的过程。
使用规范化的[=数据完整性证明=]的安全性高度依赖于算法的正确性。例如,如果规范化算法将具有不同含义的两个输入转换为相同的输出,则作者的意图可能会被[=验证者=]误解。这可能被对手用作攻击向量。
此外,如果输入中的语义相关信息未出现在输出中,则攻击者可能会将此类信息插入消息中,而不会导致证明验证失败。这类似于密码学签名消息时常用的另一种转换:密码学哈希。如果攻击者能够从不同的输入生成相同的密码学哈希,则该密码学哈希算法被认为是不安全的。
强烈建议实现者确保对任何用于[=转换=]输入到[=哈希=]过程的规范化算法进行适当的审查。适当的审查至少包括与算法正确性的同行评审数学证明相关联;多个实现和标准制定组织的专家审查是首选。强烈建议实现者不要发明或使用新的机制,除非他们在信息规范化方面接受过正式培训和/或能够获得领域专家的帮助,这些专家能够提供算法正确性的同行评审数学证明。
本规范的设计方式确保在[=符合规范的安全文档=]上验证证明时不需要网络请求。然而,读者可能会注意到,JSON-LD 上下文和[=验证方法=]可能包含可以通过网络连接检索的 URL。这种担忧适用于验证期间或之后可能从网络加载的任何 URL。
在可能的情况下,建议实现者永久或积极地缓存此类信息,以减少需要通过网络获取此类 URL 的实现的攻击面。例如,JSON-LD 上下文的缓存技术在[[[#contexts-and-vocabularies]]]节中进行了描述,而某些[=验证方法=](如 `did:key` [[?DID-KEY]])根本不需要从网络中获取。
当无法使用缓存信息时,例如首次遇到基于特定 HTTP URL 的[=验证方法=]实例时,建议实现者采取防御性措施,以减轻在任何可能从网络获取资源的过程中拒绝服务攻击的风险。
由于本规范描述的用于保护文档的技术具有通用性,其使用的安全影响可能不会立即显现给读者。为了了解在完整的软件系统中可能需要考虑的安全问题,建议实现者阅读有关此技术在可验证凭证生态系统中的使用方式 [[?VC-DATA-MODEL-2.0]];有关更多信息,请参阅可验证凭证安全注意事项部分。
以下部分描述了开发人员在实现本规范时需要注意的隐私事项,以创建增强隐私的软件。
当一个数字签名的有效负载包含被多个验证者看到的数据时,它会成为一个关联点。例如,购物会员卡号就是一个例子。可关联的数据可能会被验证者用于跟踪目的,这有时会违反隐私预期。一些数据可以被用于跟踪的事实可能并不立即显现。可关联数据的示例包括但不限于静态数字签名或图像的密码学哈希值。
可以创建一个数字签名的有效负载,该负载不包含任何可关联的跟踪数据,同时也能在特定交互中提供一定程度的可信性。这种特性被称为不可关联性,它确保数字签名的有效负载中不使用任何可关联数据,同时提供一定程度的信任,其充分性需要由每个验证者决定。
需要理解的是,并非所有用例都需要或允许不可关联性。在某些情况下,由于法规或安全原因,关联性和相关性是必要的,例如关联运输和存储危险材料的组织和个人。当某次交互中存在隐私预期时,不可关联性是有用的。
至少有两种机制可以提供一定程度的不可关联性。第一种方法是确保消息中使用的数据值永远不会在未来的消息中重复。第二种方法是确保任何重复的数据值提供足够的群体隐私,以使得在实践中几乎不可能关联期望在交互中保持一定隐私的实体。
可以使用多种方法来实现不可关联性。这些方法包括确保消息是一个一次性使用的持有者令牌,且不包含任何可用于关联的信息,使用确保足够群体隐私的属性,以及使用密码套件使消息的呈现实体能够重新生成新签名,同时不影响消息的可信性。
选择性披露是一种技术,它使得先前签名消息的接收者(即由其创建者签名的消息)能够仅披露消息的部分内容,而不影响这些部分的可验证性。例如,某人可能会选择性地披露数字驾驶执照以租车。这可能涉及仅披露执照的签发机构、执照号码、出生日期和授权的机动车类别。请注意,在这种情况下,执照号码是可关联的信息,但由于未共享驾驶员的全名和地址,因此保留了一定程度的隐私。
并非所有软件或密码套件都能够提供选择性披露。如果消息的作者希望其接收者能够选择性披露消息,则需要在特定消息上启用选择性披露,并且双方都需要使用支持该功能的密码套件。作者还可能要求强制披露消息的某些部分。希望选择性披露消息部分内容的接收者需要使用能够执行该技术的软件。支持选择性披露的密码套件的一个示例是`bbs-2023`。
可以以不保留不可关联性的方式选择性披露信息。例如,某人可能希望披露与某次运输相关的检查结果,其中包括运输标识符或批号,这可能由于法规要求而必须是可关联的。然而,可能不需要披露整个检查结果,仅选择性披露通过/未通过状态可能被认为是足够的。有关在保留隐私的同时披露信息的更多信息,请参阅第[[[#unlinkability]]]节。
当使用[[[#proof-chains]]]中定义的`previousProof`功能时,实施者需要对一个或多个先前的证明进行数字签名,以将其包含在受保护的有效负载中。这不可避免地会暴露与每个添加先前证明的实体相关的信息。
至少,先前证明的[=验证方法=](例如公钥)会被证明链中下一个证明的创建者看到。如果先前证明的创建者并不打算被包含在证明链中,这可能会引发隐私问题,但这是在任何类型的文档中添加不可否认的数字签名时不可避免的结果。
可以使用更高级的密码学机制,例如群签名,来隐藏消息签名者的身份,同时数据完整性密码套件也可以缓解这一隐私问题。
在验证证明期间或之后,任何可能从网络加载的URL都存在指纹识别问题。本规范的设计方式确保在[=符合规范的安全文档=]上验证证明时不需要网络请求。然而,读者可能会注意到,JSON-LD上下文和[=验证方法=]可能包含可以通过网络连接检索的URL,这会引发指纹识别问题。
例如,[=符合规范的安全文档=]的创建者可能会为JSON-LD上下文和[=验证方法=]创建唯一的每文档URL。在验证此类文档时,验证者从网络获取该信息会向文档的创建者暴露其对[=符合规范的安全文档=]的兴趣,这可能导致与任何非文档创建者的隐私预期不匹配。
建议实施者遵循第[[[#network-requests]]]节中关于URL缓存和防御性实现的指导。鼓励使用诸如Oblivious HTTP之类的技术,从网络中检索资源而不暴露发出请求的客户端。此外,可以使用启发式方法来确定[=符合规范的安全文档=]的创建者是否以可能违反隐私预期的方式使用指纹识别URL。这些启发式方法可以用于向可能处理包含可疑指纹识别URL的文档的实体显示警告。
执行转换(即规范化)的方式会影响系统的隐私特性。选择最佳的规范化机制取决于具体的用例。本节尝试为实施者在本规范中提到的两种主要规范化机制之间提供简单的指导,即JSON规范化方案[[RFC8785]]和RDF数据集规范化[[RDF-CANON]]。
如果应用程序不需要对受保护文档中的信息执行选择性披露,也不使用JSON-LD,那么基于JSON规范化方案[[RFC8785]]的密码套件是一个有吸引力的选择。
如果应用程序使用JSON-LD并可能需要对受保护文档中的信息执行选择性披露,那么使用基于RDF数据集规范化[[RDF-CANON]]的密码套件是一个有吸引力的选择。
实施者还被建议可以使用其他不执行任何转换的选择性披露机制,这些机制通过将数据封装在密码学信封中而不是将证明嵌入数据中来保护数据,例如SD-JWTs[[?SD-JWT]]。这些方法在某些用例中具有简化的优势,但会牺牲本规范中详细描述的方法所提供的一些好处。
由于本规范描述的用于保护文档的技术具有通用性,其使用的隐私影响可能不会立即显现给读者。为了了解在完整的软件系统中可能需要考虑的隐私问题,建议实施者阅读有关此技术在可验证凭证生态系统中的使用方式[[?VC-DATA-MODEL-2.0]];有关更多信息,请参阅可验证凭证隐私注意事项部分。
以下部分描述了开发人员在实现本规范时需要考虑的无障碍性注意事项,以确保其软件能够被具有不同认知、运动和视觉需求的人使用。通常情况下,本规范由系统软件使用,并不会直接向个人暴露需要考虑无障碍性的相关信息。然而,在某些情况下,个人可能会间接接触到由本规范表达的信息,因此针对这些情况提供了以下指导。
本规范支持表达与密码学证明有效期相关的日期和时间。如果处理某个证明时发现其超出了允许的时间范围,这些信息可能会间接暴露给个人。在向个人展示这些日期和时间时,建议实施者考虑 文化规范和地区习惯,以适应显示软件。此外,建议以减轻接收信息的个人认知负担的方式呈现时间值。
例如,在传达某组数字签名信息的过期时间时,建议实施者使用更易理解的语言,而不是以优化准确性为目标的语言。将过期时间呈现为“此票据已于三天前过期。”优于“此票据已于2023年7月25日下午3:43过期。”前者提供了更易理解的相对时间,而后者需要个人在脑海中进行计算,并假设他们能够完成这样的计算。
第[[[#proof-sets]]]节和第[[[#proof-chains]]]节描述了如何在[=安全数据文档=]中表达多个证明;也就是说,除了在[=安全数据文档=]中包含单个[=证明=]外,还可以将多个证明表达为列表,如[[[#example-a-proof-set-in-a-data-document]]]和[[[#example-a-proof-chain-in-a-data-document]]]中所示。该列表的元素是[=证明集=]的成员,并且可以选择性地成为[=证明链=]的成员。本节的目的是解释每种功能的预期用途,特别是它们不同的安全属性。这些不同的安全属性导致了第[[[#add-proof-set-chain]]]节中处理方式的差异。
本节以简化的方式表示[=安全数据文档=]及其证明,以便观察重要的安全属性。
考虑一个有三个签署人的场景:CEO、CFO 和工程副总裁(VP of Engineering)。每个人都需要拥有一对用于签署文档的公钥和私钥。我们分别用secretCEO/publicCEO、secretCFO/publicCFO和secretVPE/publicVPE表示这些签署人的私钥/公钥。
在构建一个[=证明集=]时,每个签署人对|inputDocument|进行签名而无需担忧,我们可以象征性地构建一个证明如下:
{ "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022", "created": "2023-03-05T19:23:24Z", "proofPurpose": "assertionMethod", "verificationMethod":publicCEO
, "proofValue": signature(secretCEO
,inputDocument
) }
其中publicCEO用作解析为CEO公钥的占位符,signature(`secretKey`, `inputDocument`)表示使用特定数据完整性密码套件,通过特定私钥对特定文档进行数字签名的计算。`type`、`cryptosuite`、`created`和`proofPurpose`属性不影响我们的讨论,因此将省略它们。特别是,下面展示了在一个文档上由工程副总裁、CFO 和 CEO 签署的[=证明集=]中的所有证明:
{ // 受保护的数据文档的其余部分未显示(如上) "proof": [{ "verificationMethod":publicVPE
, "proofValue": signature(secretVPE
,inputDocument
) }, { "verificationMethod":publicCFO
, "proofValue": signature(secretCFO
,inputDocument
) }, { "verificationMethod":publicCEO
, "proofValue": signature(secretCEO
,inputDocument
) }] }
持有者或任何其他中间方在接收到包含[=证明集=]的[=安全数据文档=]时,可以在将其传递给其他实体之前移除`proof`集合中的任何值,并且[=安全数据文档=]仍然可以验证。这可能符合或不符合意图。例如,对于签署人发送生日贺卡给一位重要员工,使用[=证明集=]可能是可以的。但如果我们试图模拟一个公司层级审批的业务流程,这就不理想了,因为任何中间方都可以从[=证明集=]中移除签名,且仍然可以验证;例如,在下面的示例中,看起来 CFO 和 CEO 批准了某些内容,而没有工程副总裁的同意。
{ // 受保护的数据文档的其余部分未显示(如上) "proof": [{ "verificationMethod":publicCFO
, "proofValue": signature(secretCFO
,inputDocument
) }, { "verificationMethod":publicCEO
, "proofValue": signature(secretCEO
,inputDocument
) }] }
可以通过为每个证明设置`id`属性,使另一个证明能够引用它,从而在[=证明集=]中的[=证明=]之间引入依赖关系。换句话说,依赖证明将通过使用`previousProof`属性被其他依赖证明引用。这种依赖链可以具有任意深度。使用这种[=证明链=]的意图是模拟业务流程中的审批链或公证人见证模拟签名。
以下示例展示了如何构建[=证明链=]:首先工程副总裁在文档上签字;基于工程副总裁的签名和审查,CFO 随后在文档上签字;最后,基于之前的签名和审查,CEO 在文档上签字。由于其他人将引用工程副总裁的签名,我们需要为证明添加一个`id`。首先,工程副总裁在[=输入文档=]上签字:
{ // 受保护的数据文档的其余部分未显示(如上) "proof": { "id": "urn:proof-1", "verificationMethod":publicVPE
, "proofValue": signature(secretVPE
,inputDocument
) } }
接下来,CFO 收到文档,验证工程副总裁已签署,并基于审查和工程副总裁的签名签署文档。为此,我们需要通过指示文档中刚接收到的证明的依赖关系来设置[=证明链=]。我们通过将第二个证明的`previousProof`属性设置为值`urn:proof-1`来实现,这将第二个证明“绑定”到第一个证明,然后签署。以下示例展示了如何创建对第一个证明的依赖关系:
{ // 受保护的数据文档的其余部分未显示(如上) "proof": [{ "id": "urn:proof-1", "verificationMethod":publicVPE
, "proofValue": signature(secretVPE
,inputDocument
) }, { "id": "urn:proof-2", "verificationMethod":publicCFO
, "previousProof": "urn:proof-1", "proofValue": signature(secretCFO
,inputDocumentWithProof1
) }] }
现在,当 CEO 验证包含上述[=证明链=]的[=安全数据文档=]时,他们将检查 CFO 的签名是否基于工程副总裁的签名。首先,他们将根据工程副总裁的公钥检查`id`属性值为`urn:proof-1`的证明。请注意,此证明是针对原始文档的。
接下来,CEO 将根据 CFO 的公钥检查`id`属性值为`urn:proof-2`的证明。然而,为了确保 CFO 签署的文档包含工程副总裁已签署的证明,我们将验证此证明是否覆盖了文档和`urn:proof-1`的组合。如果验证成功,CEO 将签署文档,生成一个覆盖包含`urn:proof-1`和`urn:proof-2`的文档的证明。最终的[=证明链=]如下所示:
{ // 受保护的数据文档的其余部分未显示(如上) "proof": [{ "id": "urn:proof-1", "verificationMethod":publicVPE
, "proofValue": signature(secretVPE
,inputDocument
) }, { "id": "urn:proof-2", "verificationMethod":publicCFO
, "previousProof": "urn:proof-1", "proofValue": signature(secretCFO
,inputDocumentWithProof1
) }, { "id": "urn:proof-3", "verificationMethod":publicCEO
, "previousProof": "urn:proof-2", "proofValue": signature(secretCEO
,inputDocumentWithProof2
) }] }
此[=安全数据文档=]的接收者随后以类似的方式验证它,检查链中的每个证明。
本节包含了此规范随时间推移所做的实质性更改。
自 第二版候选推荐标准以来的更改:
自 第一版候选推荐标准以来的更改:
自 第一版公开工作草案以来的更改:
本规范的工作得到了“重启信任网络”(Rebooting the Web of Trust)社区的支持,该社区由 Christopher Allen、Shannon Appelcline、Kiara Robles、Brian Weller、Betty Dhamers、Kaliya Young、Manu Sporny、Drummond Reed、Joe Andrieu、Heather Vescent、Kim Hamilton Duffy、Samantha Chase、Andrew Hughes、Will Abramson、Erica Connell 和 Eric Schuh 共同推动。由 Phil Windley、Kaliya Young、Doc Searls 和 Heidi Nobantu Saul 主持的互联网身份研讨会(Internet Identity Workshop)的参与者也通过多次旨在教育、讨论和改进本规范的工作会议支持了本规范的完善。
工作组还感谢我们的主席 Brent Zundel、前主席 Kristina Yasuda,以及 W3C 工作人员联络人 Ivan Herman,他们在 W3C 标准化过程中提供了专业管理和稳定指导。
本规范的部分工作得到了美国国土安全部科学与技术局的资助,合同编号为 70RSAT20T00000029、70RSAT21T00000016、70RSAT23T00000005、70RSAT20T00000010/P00001、70RSAT20T00000029、70RSAT21T00000016/P00001、70RSAT23T00000005、70RSAT23C00000030、70RSAT23R00000006、70RSAT24T00000011,以及国家科学基金会(NSF 22-572)的资助。本规范的内容不一定反映美国政府的立场或政策,也不应推断为官方认可。
工作组还要感谢以下个人对本规范的审阅和反馈(按姓氏字母顺序排列,或在未提供姓名的情况下按其 GitHub 用户名排列):
Will Abramson, Mahmoud Alkhraishi, Christopher Allen, Joe Andrieu, Bohdan Andriyiv, George Aristy, Anthony, Greg Bernstein, Bob420, Sarven Capadisli, Melvin Carvalho, David Chadwick, Gabe Cohen, Matt Collier, Sebastian Crane, Kim Hamilton Duffy, Snorre Lothar von Gohren Edwin, Veikko Eeva, Eric Elliott, Raphael Flechtner, Julien Fraichot, Benjamin Goering, Kyle Den Hartog, Joseph Heenan, Helge Krueger, Ivan Herman, Michael Herman, Alen Horvat, Anil John, Andrew Jones, Michael B. Jones, Rieks Joosten, Gregory K., Gregg Kellogg, Filip Kolarik, David I. Lehn, Charles E. Lehner, Christine Lemmer-Webber, Eric Lim, Dave Longley, Tobias Looker, Jer Miller, nightpool, Bert Van Nuffelen, Luis Osta, Nate Otto, George J. Padayatti, Addison Phillips, Mike Prorock, Brian Richter, Anders Rundgren, Eugeniu Rusu, Markus Sabadello, silverpill, Wesley Smith, Manu Sporny, Orie Steele, Patrick St-Louis, Henry Story, Oliver Terbu, Ted Thibodeau Jr., John Toohey, Mike Varley, Jeffrey Yasskin, Kristina Yasuda, Benjamin Young, Dmitri Zagidulin, and Brent Zundel.