本规范描述了使用密码学,确保 [=verifiable credentials=] 以及 类似类型受约束数字文档的真实性和完整性的机制,尤其是 通过使用数字签名以及与之相关的数学证明 来实现这一目的。
欢迎随时对本规范提出评论。 请直接在 GitHub 上提交问题, 或者如果无法这样做, 请将其发送至 public-vc-comments@w3.org。 (订阅, 存档)。
本规范描述了使用密码学 来确保[=verifiable credentials=]及类似类型 受约束数字文档真实性与完整性的机制,特别是通过使用数字签名(digital signatures) 及相关数学证明。加密证明(cryptographic proofs)能够提供 对分布式系统实现者有用的功能。例如,证明 可用于:
数据完整性(Data Integrity)的操作在概念上很简单。要创建加密 证明,需要执行以下步骤:1)转换(Transformation), 2)哈希(Hashing),以及3)证明生成(Proof Generation)。
Transformation(转换)是由transformation algorithm(转换算法)描述的过程,该过程接受输入数据并为哈希过程做准备。 一个可能的转换示例是:取得参加会议的人员姓名 记录,按个人的姓氏字母顺序对该列表 进行排序,并以排序后的顺序将姓名重新写在一张纸上,每行一个。 转换的示例包括 canonicalization(规范化) 和 binary-to-text(二进制到文本)编码。
Hashing(哈希)是由hashing algorithm(哈希算法)描述的过程, 该过程使用 cryptographic hash function(密码学哈希函数)为转换后的数据计算标识符。此过程在概念上类似于 电话通讯录的工作方式,即取得某人的姓名(输入数据) 并将该姓名映射到该个人的电话号码(哈希值)。 密码学哈希函数的示例包括 SHA-3 和 BLAKE-3。
Proof Generation(证明生成)是由 proof serialization algorithm(证明序列化算法)描述的过程,该过程计算一个用于 保护输入数据完整性使之免受修改、或以其他方式证明 达到某种期望信任阈值的值。此过程在概念上类似于 在装有信件的信封上使用蜡封,以建立对 发件人的信任并表明该信件在传输过程中未被篡改。 证明序列化函数的示例总体上包括 digital signatures、 proofs of stake(权益证明)和 proofs of knowledge(知识证明)。
要验证加密证明,需要执行以下步骤: 1)转换,2)哈希,以及3)证明验证(Proof Verification)。
在验证过程中,[=transformation=]和[=hashing=]步骤在 概念上与上述描述相同。
Proof Verification(证明验证)是由 proof verification algorithm(证明验证算法)描述的过程,该过程应用加密证明 验证函数以查看输入数据是否可信。可能的证明 验证函数包括 digital signatures、 proofs of stake(权益证明)和 proofs of knowledge(知识证明)。
本规范详细说明了密码学软件架构师和 实现者如何将这些过程打包为称为 [=cryptographic suites=]的东西,并将其提供给应用程序开发者, 以保护应用程序数据在传输中和静止状态下的完整性。
本规范针对以下设计目标进行优化:
虽然本规范主要关注 [=verifiable credentials=],但 此技术的设计是通用化的,因此可以用于其他 用例。在这些情况下,期望实现者就该技术对其 用例的适用性进行尽职调查和专家评审 。
conforming secured document是任何可以 转换为 JSON 文档的[=byte sequence=],且该 JSON 文档遵循 [[[#proofs]]]、[[[#proof-purposes]]]、[[[#resource-integrity]]]、 [[[#contexts-and-vocabularies]]] 和 [[[#dataintegrityproof]]] 章节中的相关规范性要求。
conforming cryptographic suite specification是 任何遵循 [[[#cryptographic-suites]]] 章节中相关 规范性要求的规范。
conforming processor是任何以软件和/或硬件实现 的算法,其根据 [[[#algorithms]]] 章节中相关的规范性陈述生成和/或消费 [=conforming secured document=]。一致性处理器在消费 不一致文档时必须(MUST)产生错误。
本文档中使用的部分术语在 [[[VC-DATA-MODEL-2.0]]] 规范的 术语章节 以及 [[[CID]]] 规范的 术语章节中定义。 本章节定义本规范中使用的额外 术语。
本节规定了用于表达[=data integrity proofs=] 以及相关资源完整性的数据模型。
本规范中所有的数据模型属性和类型都映射到 URL。 定义这些 URL 的词汇表是 [[[?SECURITY-VOCABULARY]]]。 在受保护文档中,用于执行该映射的显式机制是 `@context` 属性。
该映射机制由 [[[JSON-LD11]]] 定义。 为了确保文档可以在不使用 JSON-LD 库的情况下 互操作地被消费,建议文档作者确保领域专家已经:1)规定了与 `@context` 属性关联的所有值的预期顺序,2)发布了每个 `@context` 文件的 密码学哈希,并 3)确认每个 `@context` 文件的内容 适合于预期的用例。
当文档由不使用 JSON-LD 库的处理器处理, 且需要使用与 JSON-LD 环境中相同的语义时, 建议实现者:1)强制执行 `@context` 属性中的预期顺序 和值,并 2)确保每个 `@context` 文件 与每个 `@context` 文件已知的密码学哈希相匹配。
将带有已发布密码学哈希的静态、版本化的 `@context` 文件与 JSON Schema 配合使用,是实现上述机制的一种可接受方法, 当使用不利用 JSON-LD 库的处理器时, 可确保正确的术语识别、类型化和顺序。 详见 [[[?VC-DATA-MODEL-2.0]]] 中的 类型特定处理章节。
[=data integrity proof=]提供有关证明机制的信息、 验证该证明所需的参数以及证明值本身。所有 这些信息都使用诸如 [[[?SECURITY-VOCABULARY]]] 之类的链接数据 词汇表来提供。
在对象上表达[=data integrity proof=]时,必须(MUST)使用 `proof` 属性。[=verifiable credential=]中的 `proof` 属性是一个[=named graph=]。如果存在, 其值必须(MUST)是单个对象,或者是一个无序的对象集合, 使用以下属性表达:
可以将证明添加到 JSON 文档中,例如:
{
"myWebsite": "https://hello.world.example/"
};
以下证明使用 `eddsa-jcs-2022` 密码套件 [[?DI-EDDSA]] 保护上述文档, 该套件通过使用 JSON 规范化方案(JCS) [[?RFC8785]] 转换输入数据,然后使用爱德华兹数字签名 算法(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": "zQeVbY4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYV8nQApmEcqaqA3Q1gVHMrXFkXJeV6doDwLWx"
}
}
类似地,也可以将证明添加到 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": "zXb23ZkdakfJNUhiTEdwyE598X7RLrkjnXEADLQZ7vZyUGXX8cyJZRBkNw813SGsJHWrcpo4Y8hRJ7adYn35Eetq"
}
}
本规范支持日期和时间的表达, 例如通过 `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": "z98X7RLrkjnXEADJNUhiTEdwyE5GXX8cyJZRLQZ7vZyUXb23ZkdakfRJ7adYY8hn35EetqBkNw813SGsJHWrcpo4"
}
}
数据完整性规范支持在单个文档中包含多个 证明的概念。已识别出两种多证明 方法:证明集(无序)和证明链 (有序)。
proof set在同一份数据需要由多个实体共同保护, 但各项证明的顺序并不重要时(例如合同上的一组签名)非常有用。 证明集没有顺序,通过将一组证明与文档中的 `proof` 键 相关联来表示。
{
"@context": [
{"myWebsite": "https://vocabulary.example/myWebsite"},
"https://w3id.org/security/data-integrity/v2"
],
"myWebsite": "https://hello.world.example/",
"proof": [{
// This is one of the proofs in the set
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T19:23:24Z",
"verificationMethod": "https://ldi.example/issuer/1#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG",
"proofPurpose": "assertionMethod",
"proofValue": "z4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQeVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV6"
}, {
// This is the other proof in the set
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-rdfc-2022",
"created": "2020-11-05T13:08:49Z",
"verificationMethod": "https://pfps.example/issuer/2#z6MkGskxnGjLrk3gKS2mesDpuwRBokeWcmrgHxUXfnncxiZP",
"proofPurpose": "assertionMethod",
"proofValue": "z5QLBrp19KiWXerb8ByPnAZ9wujVFN8PDsxxXeMoyvDqhZ6Qnzr5CG9876zNht8BpStWi8H2Mi7XCY3inbLrZrm95"
}]
}
proof chain在同一份数据需要由多个实体签署, 且各项证明发生的先后顺序很重要时非常有用,例如公证人对 已经在文档上创建的证明进行连署的情形。证明链需要保留 证明的顺序,其表示方式是:提供至少一个带有 `id` 值 (例如 UUID [[?RFC9562]])的证明,以及另一个带有 `previousProof` 值(用于标识前一个证明)的证明。
{
"@context": [
{"myWebsite": "https://vocabulary.example/myWebsite"},
"https://w3id.org/security/data-integrity/v2"
],
"myWebsite": "https://hello.world.example/",
"proof": [{
// The 'id' value identifies this specific proof
"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",
// The 'previousProof' value identifies which proof is verified before this one
"previousProof": "urn:uuid:60102d04-b51e-11ed-acfe-2fcd717666a7"
}]
}
在保护文档中的数据时,必须清晰地界定哪些数据 受到保护——即文档中表达的每一个图,但承载与 保护机制相关数据的那一个图除外,该图被称为 proof graph。建立这种分离使得 处理算法能够以确定的方式保护并验证一份受保护 数据文档。
在向文档添加 [=data integrity proof=] 之前,输入文档 中所包含的信息以一个或多个图来表达。为了确保来自不同 [=data integrity proofs=] 的信息不会被意外混合, [=proof graph=] 这一概念被用来封装每个 [=data integrity proof=]。与文档 `proof` 属性相关联的 每个值都标识一个独立的图,该图有时被称为 named graph,其类型为 ProofGraph,其中包含一个 [=data integrity proof=]。
在执行 JSON-LD 处理时,使用这些图会产生具体的效果,因为 它能正确地将一个图中表达的陈述与另一个图中表达的陈述 分离开来。将处理范围限制在其他媒体类型(例如 JSON、YAML 或 CBOR)的实现者,在将一份文档的数据与 另一份文档的数据合并时(例如当两份文档中 `id` 值 字符串相同时),需要牢记这一点。重要的是,当对象没有 `id` 属性、和/或不使用 URL 等全局标识符类型时,不要 合并那些看似具有相似属性的对象,因为没有这些标识, 就无法判断两个这样的对象是否在表达同一实体的信息。
描述其用途的证明有助于防止它被滥用于其他目的。 [=Proof purposes=] 使 [=verifiers=] 能够了解证明 创建者的意图,从而避免消息被意外滥用于其他目的。 例如,仅为了做出断言(也许打算被广泛分享)而签署的 消息,被滥用为向服务进行身份验证或采取某种行动 (例如调用某个能力来做某事)的 消息。
需要注意的是,[=proof purposes=] 是一种不同于 [[[?RFC7517]]] 中 `key_ops` 限制、[[[?WEBCRYPTOAPI]]] 中 `KeyUsage` 限制以及 [[[?RFC5280]]] 的机制。 [=Proof purposes=] 表达的是 [=proof=] 为何被创建 以及其预期的使用领域,而上述其他机制旨在限制 私钥能被用于做什么。[=proof purpose=] 会与 [=proof=] 一同 "传递",而密钥限制则不会。
以下是常用的 [=proof purpose=] 值的列表。
当 [=conforming secured document=] 中包含 指向外部资源的链接时,了解所标识的资源 自证明创建以来是否发生过变化是有意义的。这既适用于 远程获取外部资源的情形,也适用于 [=verifier=] 可能持有该资源本地缓存副本的 情形。
为了能够确认 [=conforming secured document=] 所引用的资源 自该文档被保护以来未发生变化,实现者可以(MAY)在任何包含 `id` 属性的对象中 加入一个名为 `digestMultibase` 的属性。 若存在,`digestMultibase` 的值必须(MUST)是 单个 [=string=] 值,或一个 [=string=] 值的 [=list=],其中每个值都是 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 处理的实现必须(MUST)将以下 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 摘要:
|
可以通过在现代类 UNIX 操作系统命令行界面中运行类似如下的命令来确认上述 密码学摘要(将 `<DOCUMENT_URL>` 替换为合适的 值):`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 处理的实现必须(MUST)将 该词汇 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 摘要:
|
可以通过在现代类 UNIX 操作系统命令行界面中运行类似如下的命令来确认 上述密码学摘要(将 `<MEDIA_TYPE>` 和 `<DOCUMENT_URL>` 替换为合适的值): `curl -sL -H "Accept: <MEDIA_TYPE>" <DOCUMENT_URL> | openssl dgst -sha256`
应用特定词汇和规范的作者应当(SHOULD)确保 其 JSON-LD 上下文文件和词汇文件可以使用上述缓存方法或功能等效的 机制实现永久可缓存。
实现可以(MAY)在开发期间从网络加载应用特定的 JSON-LD 上下文文件, 但应当(SHOULD)在生产环境中永久缓存 [=conforming secured documents=] 所使用的 JSON-LD 上下文文件, 以提升其安全性与隐私特性。处理速度方面的目标可以(MAY)通过上述缓存方法 或功能等效的机制来达成。
某些应用(例如能够持有来自任意发行者、使用任意上下文的任意 [=verifiable credentials=] 或其他受数据完整性保护文档的数字钱包), 可能需要在生产环境中加载外部链接的 资源(如 JSON-LD 上下文文件)。这一做法预期会随着时间推移 在生态系统中增加用户选择、可扩展性以及去中心化升级。 此类应用的作者应阅读本文档的安全与隐私章节以了解更多注意事项。
有关 JSON-LD 上下文与词汇处理的更多信息, 请参阅 可验证凭证 v2.0:基础上下文 以及 可验证凭证 v2.0:词汇。
有必要确保消费应用程序已经显式批准 其将要处理的输入文档的类型,以及由此引申的 语义。不依据已知良好值核对 JSON-LD 上下文值, 可能因其所传达语义的差异而导致安全漏洞。 应用程序必须(MUST)使用第 [[[#context-validation]]] 节中的算法, 或能实现等效保护的算法,来校验 [=conforming secured document=] 中的上下文。上下文校验必须(MUST)在运行 第 [[[#verify-proof]]] 节或第 [[[#verify-proof-sets-and-chains]]] 节中 适用算法 之后 执行。
虽然第 [[[#context-validation]]] 节中描述的算法提供了一种检查 上下文值的方式,以及一种可选的安全处理未知上下文值的方式, 但实现者可以(MAY)使用提供同等保护的替代方法, 或采用不同的步骤顺序。
例如,如果不会进行 JSON-LD 处理,那么应用程序可以不执行 此项检查,而是遵循通过带外方式提供的可信文档中的指引, 以正确理解 该类型文档的语义。
另一种方式是配置应用程序使用 JSON-LD 上下文加载器 (有时也称为文档加载器),仅使用已批准上下文文件的本地副本。 这将保证上下文文件及其密码学哈希都不会发生变化, 实际效果与第 [[[#context-validation]]] 节 中的算法相同。
另一种替代方式(同样实际等效于第 [[[#context-validation]]] 节中的算法),是让应用程序保留一份 公认上下文 URL 及其关联的已批准密码学哈希列表, 而不必在本地存储每个上下文文件。这样便能在不损害 应用程序安全预期的前提下, 安全地从网络加载这些上下文。
还有一种有效的方式是让发送方应用程序通过 压实(compact)文档 为接收方应用程序所请求的精确形式(例如通过 请求 [=verifiable presentation=] 的协议),并省略保护原始文档时使用的 额外发送方专用上下文值。只要 密码套件的验证算法 能给出验证成功的结果,此类变换就是有效的, 并会使先前由所省略上下文压实的术语恢复为完整的 URL。 也就是说,先前基于发送方提供的、对接收方未知的上下文 (例如 ``https://ontology.example/v1`)而被压实为 `foo` 的术语, 将被"展开"为类似 `https://ontology.example#foo` 的 URL,然后 在所述未知上下文被省略且接收方应用程序应用 JSON-LD 压实算法后,将被"压实"为同一 URL。
`@context` 属性用于确保在处理本规范中的术语时, 各实现使用相同的语义。例如,当处理诸如 `type` 之类的属性 及其值(如 `DataIntegrityProof`)时, 这一点可能很重要。
当应用程序在保护文档时,如果文档中未提供 `@context` 属性, 或文档中使用的 Data Integrity 术语未通过 `@context` 属性中 的现有值进行映射,则实现应当(SHOULD) 注入或追加一个 `@context` 属性,其值为 `https://w3id.org/security/data-integrity/v2`,或为一个或多个至少包含 相同声明的上下文,例如可验证凭证数据模型 v2.0 上下文(`https://www.w3.org/ns/credentials/v2`)。
不打算使用 JSON-LD 处理的实现可以(MAY)选择不在文档顶层 包含 `@context` 声明。然而,如果未包含 `@context` 声明, 则禁止(MUST NOT)对本规范或对应密码套件 进行扩展(例如新增 新属性)。
HTML 处理器在设计上会在检测到可恢复错误时 继续处理。JSON-LD 处理器以类似方式运作。这种设计理念 旨在确保开发者只需使用 JSON-LD 语言中 他们觉得有用的部分,而不会因为对开发者可能并不重要的事情 导致处理器抛出错误。除其他效果外, 此理念也使得 JSON-LD 处理器在遇到诸如未定义术语之类的情况时, 被设计为不抛出错误,而是向开发者发出警告。
当从 JSON-LD 转换为 RDF 数据集时(例如对文档进行规范化时 [[?RDF-CANON]]),未定义术语和相对 URL 可能会被静默丢弃。 当值被丢弃时,它们不受数字证明的保护。这 造成了一种期望上的错位:不了解 JSON-LD 处理器工作原理的开发者可能以为某些数据已被保护, 随后却惊讶地发现并未被保护,因为没有抛出任何错误。 本规范要求在执行 JSON-LD 变换时, 任何可恢复的数据丢失都必须导致错误,以避免 开发者在安全方面产生预期错位。
使用 JSON-LD 处理(例如 RDF 数据集 规范化 [[?RDF-CANON]])的实现,必须(MUST)抛出错误,该错误应当(SHOULD)为 `DATA_LOSS_DETECTION_ERROR`,当 JSON-LD 处理器丢弃数据时( 例如在输入文档中检测到未定义术语时)。
同样,由于 [=conforming secured documents=] 可以从一个安全域 转移至另一个安全域,处理 [=conforming secured document=] 的 [=conforming processors=] 不能假设 该文档具有任何特定的基础 URL。在反序列化为 RDF 时,实现必须(MUST)确保 基础 URL 设置为 null。
本节定义本规范所使用的数据类型。
本规范将密码套件标识符编码为可枚举的 字符串,这在需要对此类字符串进行高效编码的处理过程 (例如压缩算法)中十分有用。在支持 [=string=] 值 数据类型的环境(例如 RDF [[?RDF-CONCEPTS]])中,密码标识符 内容通过字面量值表示,其数据类型设置为 `https://w3id.org/security#cryptosuiteString`。
`cryptosuiteString` 数据类型定义如下:
"关联数据"一词用于描述一种推荐的最佳实践:使用诸如 URL 等 标准来标识事物及其属性,从而在 Web 上公开、共享并连接信息。 当信息以关联数据的形式呈现时,其他相关信息可以 被方便地发现,新信息也可以方便地链接到它。关联数据以 去中心化的方式可扩展,极大降低了大规模集成 的障碍。
随着关联数据在各种应用中的使用日益增多,人们 需要能够验证关联数据文档的真实性与完整性。本规范 通过使用数学证明为数据文档增加身份认证与完整性保护, 同时不牺牲关联数据的可扩展性、可组合性等 特性。
虽然本规范提供了对关联数据进行数字签名的机制, 但要获得本规范所提供的某些优势,并不必须 使用关联数据。
实现本规范的密码套件可用于保护 [=verifiable credentials=] 与 [=verifiable presentations=]。处理此类用例 的实现者请注意:在处理这些类型的文档时,可能需要 进行额外的检查。
在某些用例中,重要的是要确保 证明中所使用的 [=verification method=] 与 `issuer`(在 可验证凭证中),或与 `holder`(在 可验证展示中)在 校验过程中相关联。检查 此类关联的一种方式是确保证明的 [=verification method=] 中 `controller` 属性的值 与分别用于标识 `issuer` 或 `holder` 的 URL 值相匹配, 并且该验证方法在给定证明用途下, 是在一个可接受的验证关系下表达的。这一特定关联表明, 分别地, `issuer` 或 `holder` 是用于验证该证明的 [=verification method=] 的控制者。
建议文档作者和实现者理解 证明的有效期(使用 `created` 与 `expires` 属性表示) 与 凭证 的有效期之间的区别,后者 使用 `validFrom` 与 `validUntil` 属性表示。 虽然这些属性有时可能表达相同的有效期,但在 其他情况下它们可能并不一致。在验证 证明时,重要的是要确保关注的时间 (可能是当前时间或任何其他时间)位于 该证明的有效期内(即介于 `created` 与 `expires` 之间)。 在 校验 [=verifiable credential=] 时,重要的是要确保关注 的时间位于 凭证的有效期内(即 介于 `validFrom` 与 `validUntil` 之间)。请注意,未能校验 证明的有效期,或 凭证的有效期, 可能会导致接受本应被拒绝的数据。
最后,还敦促实现者理解,[=verifiable credential=] 所关联的撤销信息,与 [=verification method=] 的 撤销 及过期时间 之间存在差异。 [=verification method=] 的撤销与 过期时间分别使用 `revocation` 与 `expires` 属性表示; 与诸如 [=secret key=] 被泄露或 过期等事件相关;并且可能提供时间信息,从而泄露关于 控制者的细节,例如其安全实践,或其 何时可能已被入侵。[=verifiable credential=] 的撤销信息使用 `credentialStatus` 属性表示;与 诸如个人失去由 [=verifiable credential=] 所授予权限等事件相关;且不提供时间信息,从而增强隐私性。
[=data integrity proof=] 旨在便于开发者使用, 因此力求尽量减少生成证明时需要记住的信息量。 通常,开发者只需提供 [=cryptographic suite=] 名称(例如 `eddsa-rdfc-2022`)即可发起证明的创建。这些 [=cryptographic suite=] 通常由具备必要密码学训练的人员 创建和审查,以确保使用安全的 密码原语组合。本节规定了 编写密码套件规范的要求。
所有数据完整性密码套件规范的要求 如下:
[=cryptosuite instance=] 使用 [=cryptosuite instantiation algorithm=] 进行实例化,并以实现特定的方式 提供给算法使用。实现可以(MAY)使用 [[[?VC-EXTENSIONS]]] 文档来 发现已知的 [=cryptosuite instantiation algorithms=]。
许多 [=cryptographic suites=] 在表达 [=data integrity proof=] 时 遵循相同的基本模式。本节规定了这种通用 设计模式,即一种称为 `DataIntegrityProof` 的 [=cryptographic suite=] 类型, 它通过重用设计原语和源代码减轻了编写 和实现 [=cryptographic suites=] 的负担。
在指定使用此设计模式的 [=cryptographic suite=] 时, `proof` 值采用以下形式:
[=Cryptographic suite=] 设计者必须(MUST)使用 [[[#proofs]]] 章节中定义的 强制性 `proof` 值属性,并且可以(MAY)定义特定于 其密码套件的其他属性。
2012 至 2020 年间数据完整性密码套件中可见的一种设计模式是
使用 `type` 属性为密码套件建立特定类型;
例如
Ed25519Signature2020 密码套件就是这样一份规范。
这导致密码套件实现的负担加重,因为每个新的
密码套件都需要规定一个新的 JSON-LD Context,
从而带来次优的开发者体验。该设计模式的精简版本
于 2020 年出现,使开发者只需包含
单一的 JSON-LD Context 即可支持所有现代密码套件。这
鼓励了更现代的密码套件 — 例如 EdDSA Cryptosuites
[[?DI-EDDSA]] 和 ECDSA Cryptosuites [[?DI-ECDSA]] —
基于本节所描述的精简模式构建。
为了改善开发者体验,编写新的数据完整性
密码套件规范的作者应当(SHOULD)使用现代模式 — 其中
`type` 设为 `DataIntegrityProof`;`cryptosuite` 属性承载
该密码套件的标识符;任何特定于密码套件的
密码数据都封装(即不直接作为应用层数据暴露)
在 `proofValue` 中。已知遵循此模式的密码套件规范
列表见
Verifiable Credentials Extensions 的 Securing Mechanisms 章节
文档。
下文定义的算法对以 JSON objects 表示的文档进行操作。本规范遵循 [[[JSON-LD11-API]]] 规范,将 JSON 对象表示为 [=map=]。 unsecured data document 是一个不包含 任何证明值的 [=map=]。input document 是一个尚未添加 当前证明的 [=map=],但其可以(MAY)包含由先前流程添加的 证明值。secured data document 是一个包含一个或多个证明值的 [=map=]。
除下列算法外,实现者可以(MAY)实现合理的默认值与防护措施,以帮助缓解 开发者错误、过度的资源消耗、新发现的具有特定 防护机制的攻击模型,以及其他改进。下列提供的算法是 可互操作实现的最低要求, 我们鼓励开发者引入额外措施, 以促成更安全、更高效 的生态系统。
本节描述了 [=conforming processor=] 及其 特定于应用程序的软件所使用的处理模型。当软件 需要确保信息可被防篡改地检测时,它将执行以下步骤:
当软件需要使用通过本规范所述机制传输给它的信息时, 它将执行以下步骤:
下列算法规定了如何向 [=input document=] 添加数字证明,以便随后用于验证输出文档的真实性 与完整性。所需输入为一个 [=input document=]([=map=] |inputDocument|)、一个 [=cryptosuite instance=]([=struct=] |cryptosuite|),以及 一组选项([=map=] |options|)。输出为一个 [=secured data document=] ([=map=])或一个错误。每当本算法对字符串进行编码时,必须(MUST)使用 UTF-8 编码。
下列算法规定了如何在已包含证明或证明集/链的受保护文档基础上, 增量地向证明集或证明链中添加一个证明。所需输入为 一个 [=secured data document=]([=map=] |securedDocument|)、一个 [=cryptographic suite=]([=cryptosuite instance=] |suite|),以及一组选项([=map=] |options|)。输出为一个新的 [=secured data document=]([=map=])。每当本算法对字符串进行编码时, 必须(MUST)使用 UTF-8 编码。
此步骤添加对 [=named graphs=] 的引用,并添加
[=proof graphs=] 中所包含的所有声明的副本。该步骤至关重要,
因为它在应用当前证明之前,将任何匹配的证明 绑定
到该文档。
该文档的 |proof| 值将在本算法的后续
步骤中更新。
以下算法规定了如何通过验证 [=secured data document=] 的数字证明来检查其 真实性和完整性。该算法 接受以下输入:
该算法返回一个 verification result,一个 [=struct=],其 [=struct/items=] 包括:
当某步骤说"必须(MUST)抛出错误"时,意味着必须(MUST)返回一个 [=verification result=],其 [=verification result/verified=] 值为 `false`,且 [=verification result/errors=] 列表非空。
在 [=proof set=] 或 [=proof chain=] 中,[=secured data document=] 具有 `proof` 属性,其中包含 [=proofs=] 列表(|allProofs|)。以下 算法提供了一种检查 [=secured data document=] 真实性和完整性的方法, 通过验证 |allProofs| 中的每个证明来实现。 也可能采取其他方法,特别是当只想验证 |allProofs| 中所含证明的子集时。如果采取另一种方法仅 验证证明的子集,则需要注意,该子集中 任何带有 `previousProof` 的证明,仅当其引用的 证明也被视为已验证时,才能被视为已验证。
必需的输入是 [=secured data document=](|securedDocument|)。生成与 |allProofs| 中每个证明对应的 [=verification results=] 列表, 并返回单个组合的 [=verification result=] 作为输出。 实现可以(MAY)在组合的 [=verification result=] 之外返回任何其他 [=verification result=] 和/或任何其他元数据。
参见 [[[#add-proof-set-chain]]] 章节步骤 6 中的注释,了解 此步骤保护哪些文档属性和前序证明。
以下算法提供了一种机制,可用于确保应用程序 在执行针对文档输入的特定业务规则之前, 先理解与该文档关联的上下文。有关该算法的 更多原理说明,参见第 [[[#validating-contexts]]] 节。 该算法接受以下输入:一个文档([=map=] |inputDocument|)、 一组已知 JSON-LD 上下文([=list=] |knownContext|),以及一个布尔值, 用于在检测到未知上下文时进行重新压缩([=boolean=] |recompact|)。
该算法返回一个 context validation result, 即一个 [=struct=],其 [=struct/items=] 为:
上下文校验算法如下:
实现可以(MAY)包含额外的警告或错误,以执行 特定于该实现或特定用例的 进一步校验规则。
本规范以及各种密码套件规范中描述的算法 会抛出特定类型的错误。实现者可能会发现 将这些错误传达给其他库或软件系统是有用的。 本节为这些错误提供了具体的 URL 和描述, 以便实现本规范所述技术的生态系统 在发生错误时能够更高效地互操作。
在通过 HTTP 接口暴露这些错误时,实现者应当(SHOULD)使用 [[RFC9457]] 将错误数据结构编码为一个 ProblemDetails [=map=]。如果使用 [[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
。安全级别和相应的
曲线与哈希函数是由验证时使用的公钥的多密钥格式
确定的。
Cryptographic agility(密码学敏捷性)是一种实践, 通过这种实践,人们可以将频繁连接的信息安全系统设计为 支持在多种密码学原语和/或算法之间切换。密码学敏捷性的 首要目标是使系统能够快速适应新的 密码学原语和算法,而无需对 系统的基础设施作出破坏性的变更。因此,当某种特定的密码学原语 (例如 SHA-1 算法)被认定为不再安全可用时,可以通过 简单的配置文件变更将系统重新配置为使用更新的原语。
密码学敏捷性在信息安全系统中的客户端与服务器 保持定期联系时最为有效。然而,当 某种特定密码算法所保护的消息是长期存续的时, 例如 [=verifiable credentials=],并且/或当客户端(持有者)可能 难以再次联系到服务器(发行者)时,密码学敏捷性 便无法提供所期望的保护。
Cryptographic layering(密码学分层)是一种实践, 通过这种实践,人们将极少连接的信息安全系统设计为 同时采用多种原语和/或算法。密码学分层的 首要目标是使系统在一个或多个密码算法或原语 失效时,仍能在不丧失对负载的密码学保护的情况下继续运转。 例如,使用 RSA、ECDSA 和 Falcon 算法并行地对单一 信息片段进行数字签名,将 提供一种能够在这三种数字签名算法中两种失效时 仍可继续运转的机制。当某种特定的密码学保护被攻破时, 例如使用 768 位密钥的 RSA 数字签名,系统仍可利用 未被攻破的密码学保护来继续保护 信息。强烈建议开发者对所有可能需要保护一年或更长时间的 已签名内容利用此特性。
本规范同时提供了两种形式的敏捷性。它提供了 密码学敏捷性,从而允许人们轻松地从一种算法切换到 另一种算法。它还提供了密码学分层,从而允许人们 同时使用多种密码算法(通常是并行的), 使得用于保护信息的任意一种算法都可以独立使用, 而不依赖或要求其他算法,同时仍保持数字证明格式 对开发者而言易于使用。
[=proof=] 包含一个 `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 客户端,从而耗尽服务器上的连接。 建议实现者在实施防御性 安全策略时考虑此类攻击。
任何 [=data integrity proof=] 所保护的数据都是 [=transformation|transformed data=]。[=transformation|Transformed data=] 由特定 [=cryptosuite=] 指定的 [=transformation algorithm=] 生成。这种保护机制不同于一些 更传统的不对输入数据执行任何 [=transformation=] 的数字签名机制。[=transformation=] 的好处 在 [[[#transformations]]] 一节中详细说明。
例如,[=cryptosuites=] 如 ecdsa-jcs-2019 和 eddsa-jcs-2022 使用 [[[?RFC8785]]] 将数据 [=transformation|transform=] 为规范化的 JSON, 然后对其进行密码学哈希和数字签名。这种方法的一个好处是, 增删不影响被签名信息含义的格式字符(例如空格、 制表符和换行符)不会使 数字签名失效。更传统的数字签名 机制不具备此能力。
其他 [=cryptosuites=] 如 ecdsa-rdfc-2019 和 eddsa-rdfc-2022 使用 [[[?RDF-CANON]]] 将数据 [=transformation|transform=] 为规范化的 N-Quads [[?N-QUADS]],然后对其进行密码学哈希和数字 签名。这种方法的一个好处是,密码学签名可移植到 多种不同的语法,例如 JSON、YAML 和 CBOR, 而不会使签名失效。更传统的密码学签名 机制不具备此能力。
敦促实现者和开发者不要信任 包含 [=data integrity proof=] 的信息,除非该证明已被 [=verified=] 且经过验证的数据由确认所有返回数据 均已被成功保护的软件库的返回值提供。
应用数据的可检查性影响系统效率和 开发者生产力。当被密码学保护的应用数据,例如 base 编码的二进制数据,不易被应用子系统(例如 数据库)处理时,会增加处理被密码学 保护信息的工作量。例如,可以被数据库本机 存储和索引的被密码学保护的载荷,将带来 一个更简单的系统,其:
类似地,可以被多个上游联网系统处理的被密码学 保护的载荷,增加了正确分层 安全架构的能力。例如,如果上游系统不必 重复解码传入的载荷,就增加了系统通过将上游 子系统专门化以主动对抗攻击来分配处理负载的能力。 虽然在采取实质性行动之前必须始终检查数字签名, 但其他上游检查可以在透明的载荷上执行 — 例如基于标识符的速率限制、签名过期检查 或 nonce/challenge 检查 — 以拒绝明显不良的请求。
此外,如果开发者无法轻易查看系统中的数据, 轻易审计或调试系统正确性的能力就会受阻。例如, 要求应用开发者剪切粘贴 base 编码的应用数据 会使开发更具挑战性,并增加显而易见的 bug 被遗漏的机会,因为每条消息都需要通过手动操作的 base 解码工具。
然而,有时正确的设计决策是使数据不透明。 不需要被其他应用子系统处理的数据, 以及不需要被应用开发者修改或访问的数据, 都可以被序列化为不透明格式。示例包括数字签名 值、密码学密钥参数和其他仅需要由 密码库访问且不需要被应用开发者修改的 数据字段。还有一些示例,当底层子系统不会向应用 开发者暴露不透明数据的底层复杂性时,数据 不透明性也是合适的,例如执行静态加密的 数据库。在这些情况下,应用开发者继续 针对透明的应用数据格式进行开发,而数据库管理 将应用数据加密和解密到长期存储以及 从其中读取的复杂性。
本规范致力于提供一种架构,使应用数据 保留在其本机格式中而不被弄成不透明,同时其他密码学 数据(例如数字签名)保持其不透明的二进制编码形式。 敦促密码套件实现者在设计其套件时考虑 数据不透明性的适当使用,并在使应用数据 不透明与在应用层提供对密码学数据的访问之间 权衡设计折衷。
实现者通过从[=verification method=]的定义出发, 进入[=controlled identifier document=], 再确保该[=controlled identifier document=]中也包含对 [=verification method=]的引用,以此确保[=verification method=] 绑定到特定的控制者。该过程在以下算法中作了描述: 获取验证方法。
当实现在校验证明时,必须不仅校验 用于生成该证明的[=verification method=]列于 [=controlled identifier document=]中,还要校验该方法的预期用途 确实是生成正在被校验的那一项证明。该过程 被称为"验证关系校验"。
校验验证关系的过程在 以下小节中作了概述: 3.3 节 获取验证方法(属于 [[[CID]]] 规范)。
该过程用于确保密码学材料(如私有密码学密钥) 不会被应用程序误用于非预期用途。 密码学材料误用的一个例子是:本应用于发行 [=verifiable credential=]的私有密码学密钥 被改用于登录某网站(即用于身份认证)。不检查验证关系 是危险的,因为 某些密码学材料的限制与保护级别可能由其预期用途决定。例如, 某些应用程序可能仅被信任将密码学材料 用于单一目的;又或者某些密码学材料可能受到更高级别的保护, 比如存放在数据中心的硬件安全模块中, 而非作为未加密文件存放在笔记本电脑上。
当实现在校验证明时, 必须校验[=proof purpose=]与其预期用途相符。
该过程用于确保证明不会被应用程序误用于 非预期用途,因为这种误用对证明的创建者而言是危险的。误用的 一个例子是:一项声明其用途为保护 [=verifiable credentials=]中断言的证明,被改用于[=authentication=] 以登录某网站。在这种情况下,证明创建者将证明附加到了 任意数量的[=verifiable credentials=]上,并预期这些凭据会被分发给 数量不限的其他方。这些方中的任何一方都可能登录某网站 并冒充证明创建者,只要该网站错误地把此类证明接受为 [=authentication=]而非其原本预期的用途。
执行变换(例如规范化)的方式可能会 影响系统的安全特征。选择最佳的 规范化机制取决于具体用例。通常, 满足所需安全要求的最简单机制 即是最佳选择。本节尝试提供简单指导,帮助 实现者在本规范涉及的两种主要规范化机制之间作选择, 即 JSON Canonicalization Scheme [[RFC8785]] 与 RDF Dataset Canonicalization [[RDF-CANON]]。
如果应用程序仅使用 JSON 而不依赖任何形式的 RDF 语义,那么使用基于 JSON Canonicalization Scheme [[RFC8785]] 的密码套件是一种有吸引力的方案。
如果应用程序使用 JSON-LD 并需要保护 文档的语义,那么使用基于 RDF Dataset Canonicalization [[RDF-CANON]] 的密码套件是一种 有吸引力的方案。
还需告知实现者,存在其他不执行任何 变换的机制,它们通过将数据封装在 密码学信封中(而非将证明嵌入数据本身)来保护数据,例如 JWT [[?RFC7519]] 与 CWT [[?RFC8392]]。这些方案在某些用例下具有 简洁性方面的优势,代价是要放弃 本规范所详述方案的部分益处。
本规范所使用的算法过程之一是规范化, 它是一种[=transformation=]。规范化是这样一个过程: 将可能以多种语义等价方式表达的 信息作为输入,并以单一方式表达所有输出,称为 "规范形式"。
利用规范化所产生的[=data integrity proof=]的安全性 高度依赖于该算法的正确性。 例如,如果一个规范化算法将两个含义不同的输入 转换为相同的输出,那么作者的意图就可能 被错误地呈现给[=verifier=]。这可能被对手 用作攻击向量。
此外,如果输入中语义相关的信息没有出现在 输出中,那么攻击者就可以将此类信息插入到消息中 而不会导致证明校验失败。这与 在对消息进行密码学签名时常用的另一种变换类似: 密码学哈希。如果攻击者能够从不同的输入产生相同的密码学 哈希,那么该密码学哈希算法 不被视为安全。
强烈敦促实现者确保对任何用于将输入 变换为[=hashing=]过程的规范化算法 进行妥善审查。妥善审查至少应包括:与 经同行评审的算法正确性数学证明相关联;更佳的方式是拥有多种 实现并由某标准化组织中的专家进行审查。强烈敦促实现者 不要发明或使用新的机制,除非他们在 信息规范化方面接受过正式培训,和/或能够获得该领域内 有能力产出经同行评审的算法正确性 数学证明的专家的支持。
本规范的设计使得在 校验[=conforming secured document=]上的证明时不需要任何网络请求。 但读者可能注意到,JSON-LD 上下文 与[=verification methods=]中可能包含可经由网络连接获取的 URL。 对于在校验期间或之后可能从网络 加载的任何 URL,这种关切都同样存在。
在可能的范围内,敦促实现者永久或积极地 缓存此类信息,以减少在实现可能需要通过网络获取此类 URL 时 的攻击面。例如,JSON-LD 上下文的缓存技术 在以下小节中作了描述: [[[#contexts-and-vocabularies]]];并且某些[=verification methods=],例如 `did:key` [[?DID-KEY]],根本不需要从网络获取。
当无法使用缓存信息时(例如首次遇到某个基于特定 HTTP URL 的[=verification method=]实例时),告诫实现者使用 防御性措施,以在任何可能从网络获取资源的过程中缓解 拒绝服务攻击 。
由于本规范所描述的用于保护文档的技术 本质上是通用化的,其使用所带来的安全影响 对读者来说可能并非一目了然。要理解 在一个完整的软件系统中可能需要考虑的各类安全 问题,敦促实现者阅读关于该项技术 在[=verifiable credentials=]生态系统 [[?VC-DATA-MODEL-2.0]] 中如何使用的内容; 详情参见 可验证凭据安全考虑一节以获得更多信息。
以下章节描述了实现本规范的开发者 应当了解的隐私考虑事项,以便创建 增强隐私的软件。
当数字签名的载荷包含被多个验证者 看到的数据时,它就会成为关联点。此类数据的一个示例是 购物忠诚卡号。可关联的数据可被验证者用于追踪 目的,这有时会违反隐私预期。 某些数据可被用于追踪这一事实可能并非显而易见。 此类可关联数据的示例包括但不限于静态 数字签名或图像的密码学哈希。
可以创建不包含任何 可关联追踪数据的数字签名载荷,同时为给定交互 提供某种程度的可信保证。这一特性被称为 unlinkability,它确保 在数字签名载荷中不使用任何可关联数据,同时仍提供某种程度的 信任,其充分性必须由每个验证者自行确定。
重要的是要理解,并非所有用例都要求甚至允许 不可链接性。在某些用例中,由于监管或安全原因, 需要可链接性和关联,例如关联 运输和储存危险品的组织和个人。不可链接性 在特定交互存在隐私预期时是有用的。
至少有两种机制可以提供某种程度的不可链接性。 第一种方法是确保消息中使用的任何数据值都不会在 未来的消息中重复出现。第二种是确保任何重复的数据 值提供足够的群体隐私,使得在交互中 关联期望某种程度隐私的实体在实际上变得不可能。
可以使用多种方法来实现不可链接性。这些方法包括 确保消息是一次性使用的不记名令牌,且不包含 任何可用于关联目的的信息,使用确保 足够群体隐私级别的属性,以及使用能够使 出示消息的实体重新生成新签名而不损害 所出示消息可信性的密码套件。
选择性披露是一种使先前已签名消息(即 由其创建者签名的消息)的接收者能够仅披露 消息的部分内容而不破坏这些部分可验证性的技术。 例如,人们可能为了租车的目的而选择性披露 数字驾驶执照。这可能涉及仅披露 驾照中的发证机关、驾照号码、生日和授权机动车类别。 请注意,在这种情况下,驾照号码是可关联的 信息,但由于驾驶员的 全名和地址未被共享,因此保留了一定程度的隐私。
并非所有软件或密码套件都能够提供选择性披露。 如果消息的作者希望其能够被接收者选择性披露, 那么他们需要在特定消息上启用选择性披露, 并且双方都需要使用具备此能力的密码套件。作者还可以 强制要求披露消息的某些部分。希望选择性披露消息 部分内容的接收者需要使用 能够执行该技术的软件。一个支持 选择性披露的密码套件示例是 `bbs-2023`。
可以以不保留不可链接性的方式选择性披露信息。 例如,人们可能想要披露与货物相关的检验 结果,其中包括货物标识符或批 号,由于监管要求,这些可能必须是可关联的。 然而,可能不需要披露整个检验结果,因为 仅选择性披露通过/未通过状态可能就被视为足够。如需 更多关于在保护隐私的同时披露信息的信息,请参阅章节 [[[#unlinkability]]]。
当使用 [[[#proof-chains]]] 中定义的 `previousProof` 特性时, 实现需要对一个或多个先前证明进行数字签名, 以便将它们包含在受保护的载荷中。这不可避免地暴露了 与每个添加先前证明的实体相关的信息。
最少情况下,先前证明的[=verification method=],例如 公钥,会被证明链中下一个证明的创建者看到。 如果先前证明的创建者并不打算 被包含在证明链中,这可能引起隐私问题,但是 向任何类型的文档添加不可否认的数字签名时,这都是不可避免的结果。
可以使用更高级的密码学机制,例如 群签名, 来隐藏消息签名者的身份, 数据完整性密码套件 也可以缓解这一隐私问题。
对于任何可能在证明验证期间或之后从网络加载的 URL,都存在指纹识别问题。本 规范的设计方式使得在验证[=conforming secured document=]上的证明时 不需要任何网络请求。然而,读者可能会 注意到,JSON-LD 上下文和 [=verification methods=] 可能包含可能通过网络连接获取的资源 URL, 这会导致指纹识别问题。
例如,[=conforming secured documents=] 的创建者可能为 JSON-LD 上下文和 [=verification methods=] 制作每个文档独有的 URL。验证此类文档时, 从网络获取该信息的验证者会向文档的创建者 泄露他们对[=conforming secured document=]的兴趣,这可能导致 对于任何非文档创建者的实体而言出现 隐私预期上的不匹配。
强烈建议实现者遵循章节 [[[#network-requests]]] 中 关于 URL 缓存和在从网络获取 URL 时进行防御性实现的指导。 鼓励使用诸如 Oblivious HTTP 之类的技术从网络获取资源,而不暴露 发起请求的客户端。此外,可以使用启发式方法 来确定[=conforming secured documents=]的创建者 是否以可能违反隐私预期的方式使用指纹识别 URL。 这些启发式方法可用于向可能 处理包含疑似指纹识别 URL 文档的实体显示警告。
执行变换(即规范化)的方式会 影响系统的隐私特性。选择最佳的 规范化机制取决于用例。 本节尝试提供简单的指导,帮助 实现者从隐私角度在本规范中提到的两种主要规范化机制 之间进行选择,即 JSON 规范化方案 [[RFC8785]] 和 RDF 数据集规范化 [[RDF-CANON]]。
如果应用程序不需要对受保护文档中的信息执行选择性披露, 也不使用 JSON-LD,那么 JSON 规范化方案 [[RFC8785]] 是一个有吸引力的方法。
如果应用程序使用 JSON-LD 并且可能需要对受保护文档中的信息进行 选择性披露,那么使用基于 RDF 数据集规范化 [[RDF-CANON]] 的 密码套件是一种有吸引力的 方法。
还提醒实现者,存在其他不执行任何变换的 选择性披露机制,它们通过将数据包装在 密码学信封中来保护数据,而不是将证明嵌入数据中, 例如 SD-JWT [[?SD-JWT]]。这种方法在某些用例中具有简洁性优势, 但代价是失去本规范中详述的方法所提供的 某些好处。
由于本规范所描述的用于保护文档的技术 本质上是通用的,其使用的隐私影响可能不会 立即对读者显而易见。要理解在完整软件系统中 可能需要考虑的隐私问题类型,强烈建议实现者 阅读有关此技术如何在 [=verifiable credentials=] 生态系统 [[?VC-DATA-MODEL-2.0]] 中使用的内容;有关更多信息,请参阅 可验证凭证隐私考虑事项章节。
以下章节描述了无障碍考虑事项, 敦促实现本规范的开发者加以考虑, 以确保其软件能够被具有不同认知、运动和视觉 需求的人使用。作为一般规则,本规范由系统软件使用, 并不会直接将个人暴露于受无障碍 考虑事项约束的信息。然而,在某些情况下,个人可能会 间接接触到本规范所表达的信息,因此 针对这些情形提供以下指南。
本规范支持表达与密码学证明的 有效期相关的日期和时间。如果某项证明被处理并被检测到 超出允许的时间范围,该信息可能会间接 暴露给个人。在向个人展示这些日期和时间时, 实现者应当考虑 显示软件中表示日期和时间时的 文化规范和区域设置。除了这些 考虑事项之外,以减轻接收信息的个人认知负担的方式 呈现时间值是建议的最佳实践。
例如,在传达某组 数字签名信息的过期日期时,实现者应当使用更易于理解的 语言来呈现过期时间,而不是追求准确性 的语言。将过期时间呈现为 "此票据已于 三天前过期。" 优于诸如 "此票据于 2023 年 7 月 25 日下午 3:43 过期。" 之类的表述。前者提供的相对时间 比后者更容易理解;后者要求个人在脑海中进行 计算,并假定他们具备进行 此类计算的能力。
[[[#proof-sets]]] 节和 [[[#proof-chains]]] 节描述了如何在[=secured data document=]中 表达多个证明;即,与其在[=secured data document=]中包含单个[=proof=], 不如在[=list=]中表达多个证明, 如 [[[#example-a-proof-set-in-a-data-document]]] 和 [[[#example-a-proof-chain-in-a-data-document]]] 所示。 该[=list=]的元素是[=proof set=]的成员, 并可选地构成[=proof chain=]的成员。本节的目的是解释 这些特性中每一种的预期用途, 特别是它们不同的安全属性。这些不同的安全属性导致 [[[#add-proof-set-chain]]] 节中处理过程的差异。
本节以简略的方式表示[=secured data documents=], 包括其证明,以便可以观察到重要的 安全属性。
考虑一个有三位签署者的场景:一位 CEO、一位 CFO 和 一位工程副总裁(VP of Engineering)。每位签署者都需要 拥有一对公钥和私钥用于签署文档。我们分别使用 secretCEO/publicCEO、secretCFO/publicCFO 和 secretVPE/publicVPE 表示这些签署者各自的私钥/公钥。
当构造一个[=proof set=],其中每位签署者无须协调地签署一份 |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 set=]中的所有证明:
{
// Remainder of secured data document not shown (above)
"proof": [{
"verificationMethod": publicVPE,
"proofValue": signature(secretVPE, inputDocument)
}, {
"verificationMethod": publicCFO,
"proofValue": signature(secretCFO, inputDocument)
}, {
"verificationMethod": publicCEO,
"proofValue": signature(secretCEO, inputDocument)
}]
}
[=holder=]或任何其他中间方在收到包含[=proof set=]的 [=secured data document=]后,可以在将其传递给另一个实体之前 移除集合中任意 `proof` 值,而[=secured data document=]仍然能通过 验证。这可能符合也可能不符合预期。对于向重要员工发送 生日贺卡的签署者来说,使用[=proof set=]可能是合适的。 如果我们尝试对一种审批沿公司层级上升的业务流程建模, 这将不甚理想,因为任何中间方都可以从[=proof set=]中 移除签名而仍能通过验证;例如,在下面的示例中, 看起来 CFO 和 CEO 在没有工程副总裁认可的情况下 批准了某项内容。
{
// Remainder of secured data document not shown (above)
"proof": [{
"verificationMethod": publicCFO,
"proofValue": signature(secretCFO, inputDocument)
}, {
"verificationMethod": publicCEO,
"proofValue": signature(secretCEO, inputDocument)
}]
}
可以通过设置每个证明的 `id` 属性以便其他证明可以引用它, 从而在[=proof set=]中的[=proofs=]之间引入依赖关系。 换句话说,被依赖的证明将被其他依赖证明 通过 `previousProof` 属性引用。这种依赖链 可以具有任意深度。这种[=proof chain=]的意图 是对业务流程中的审批链或公证人见证模拟签名进行 建模。
下面的示例演示了在以下情形下如何构造[=proof chain=]: 工程副总裁首先签署文档;然后基于工程副总裁的签名和审阅, CFO 签署该文档;最后基于前述两个签名和一次审阅, CEO 签署该文档。由于其他人将引用工程副总裁的签名, 我们需要为该证明添加一个 `id`。首先, 工程副总裁签署 [=input document=]:
{
// Remainder of secured data document not shown (above)
"proof": {
"id": "urn:proof-1",
"verificationMethod": publicVPE,
"proofValue": signature(secretVPE, inputDocument)
}
}
接着,CFO 收到该文档,验证工程副总裁已签署它, 然后基于审阅和工程副总裁的签名签署它。为此, 我们需要通过指明对刚收到的文档中证明的依赖关系来 建立[=proof chain=]。我们通过将第二个证明的 `previousProof` 属性设置为 `urn:proof-1` 来做到这一点, 这将第二个证明与第一个证明 "绑定" 在一起,然后进行签署。 以下示例展示了如何创建对第一个证明的 依赖关系:
{
// Remainder of secured data document not shown (above)
"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 用上述[=proof chain=]验证收到的 [=secured data document=]时,他们会检查 CFO 是否基于 工程副总裁的签名进行签署。首先,他们会用工程副总裁的公钥 检查 `id` 属性值为 `urn:proof-1` 的证明。 请注意,该证明是针对原始文档的。
接下来,CEO 将用 CFO 的公钥检查 `id` 属性值为 `urn:proof-2` 的证明。然而,为了确保 CFO 在 工程副总裁已签署文档的证明之上签署,我们对文档与 `urn:proof-1` 的组合验证此证明。如果验证成功, CEO 进行签署,产生针对包含 `urn:proof-1` 和 `urn:proof-2` 的文档的证明。最终的[=proof chain=] 如下所示:
{
// Remainder of secured data document not shown (above)
"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)
}]
}
该[=secured data document=]的接收者随后以类似方式 对其进行校验,检查链中的每个证明。
本节包含本规范在过去所做的 实质性变更。
自 第二次候选推荐标准以来的变更:
自 第一次候选推荐标准以来的变更:
自 第一次公开工作草案以来的变更:
本规范的工作得到了 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 主持。 Internet Identity Workshop 的参与者们(由 Phil Windley、Kaliya Young、Doc Searls 和 Heidi Nobantu Saul 主持)也通过众多旨在普及、辩论和 改进本规范的工作会议, 支持了本工作的完善。
工作组还要感谢我们的主席 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 和 Brent Zundel。