受控标识符文档包含加密材料,并列出服务端点,用于验证来自标识符控制者的加密证明以及与其交互。

介绍

[=受控标识符文档=]用于标识一个[=主体=],并提供[=验证方法=],这些方法表达了公共加密材料,例如[=公钥=],用于验证代表[=主体=]为特定目的(如身份验证、证明、密钥协商(用于加密)以及能力调用和委托)创建的[=证明=]。[=受控标识符文档=]还列出了与[=标识符=]相关的[=服务=]端点;例如,用于请求额外的验证信息。

换句话说,受控标识符文档包含与[=标识符=]控制者通信和/或证明其执行特定操作所需的信息,包括用于[=证明=]的材料和用于额外通信的[=服务=]端点。

一个[=受控标识符文档=]为单个标识符指定[=验证关系=]和[=服务=]端点,并且当前的受控标识符文档被视为权威来源。

其他规范,例如[[[?DID]]]规范,基于本规范定义的功能进行配置,要求和/或推荐使用某些功能,同时禁止和/或弃用其他功能。

用例

以下用例说明了本规范的必要性。虽然还有许多其他相关用例,例如[[[DID-USE-CASES]]]和[[[VC-USE-CASES]]]中的用例,但以下描述的是本规范旨在解决的主要场景。

全局唯一标识符

Lemmy 运营着多个企业门户,这些门户管理着大量由各种组织的人员提交的敏感数据。他希望使用由客户提供的标识符来标识数据库中的实体,而不是依赖于容易被钓鱼的信息,例如电子邮件地址和密码。

加密验证

Lemmy 希望确保其客户能够证明对其标识符的控制权 — 例如,通过使用公钥/私钥加密 — 以提高与谁可以访问和更新每个组织数据相关的安全性。

加密用途

Stef 运营着一个高安全性的服务,他希望确保其客户使用的某些加密密钥只能用于特定目的(例如加密、授权和/或身份验证),以便为每种类型的加密密钥启用不同级别的访问和保护。

服务交互

Marge 是一名软件开发人员,她希望公开宣传其他人可以通过她使用的各种通信服务与她联系的方式,这些方式基于她的全局唯一标识符。

可扩展性

Cory 是一名系统架构师,他希望以一种提供新功能的方式扩展本节中描述的用例,而不会与其他人添加的扩展产生冲突。

发布和展示声明

Neru 希望代表她的公司发布包含关于其员工声明的数字凭证。这些声明需要使用可以加密地归因于 Neru 公司并允许这些凭证持有者在展示凭证时能够加密地验证自己的标识符。

需求

以下需求来源于本规范中先前描述的用例。其他可能导致更去中心化解决方案的需求可以在[[[DID-USE-CASES]]]中找到。

1. 保证唯一标识符
标识符是全局唯一的,不可能重复。
2. 控制权证明
能够证明声称控制标识符的实体确实是其控制者。
3. 关联的加密材料
标识符与加密材料紧密耦合,实体可以使用这些材料证明对标识符的控制权。
4. 简化的密钥轮换
指定标识符的实体可以在无需请求方直接干预的情况下,最小化个人交互来更新认证材料。
5. 服务端点发现
这些标识符允许请求方查找与标识符主体交互的可用服务端点。
6. 控制权委托
标识符的控制者能够将控制权全部或部分委托给第三方。
7. 加密的未来适应性
这些标识符及其相关信息能够随着技术的发展进行更新。当前的加密技术已知可能受到量子计算攻击的威胁。未来适应性的标识符提供了一种方式,可以使用更新的高级认证和/或授权技术继续使用相同的标识符。
8. 加密认证与通信
这些标识符支持使用加密技术来认证个人和/或保护与标识符主体的通信,通常使用公私钥对。
9. 法律认可的标识
这些标识符可以作为凭证和交易的基础,在一个或多个司法管辖区内被法律认可为有效。
10. 以人为中心的互操作性
去中心化标识符需要易于非技术专家或没有专业知识的人使用。

一个符合规范的受控标识符文档是任何遵循第[[[#data-model]]]和[[[#contexts-and-vocabularies]]]节中相关规范性要求的数据模型的具体表达。

一个符合规范的验证方法是任何遵循第[[[#verification-methods]]]和[[[#contexts-and-vocabularies]]]节中相关规范性要求的数据模型的具体表达。

一个符合规范的文档可以是[=符合规范的受控标识符文档=],也可以是[=符合规范的验证方法=]。

一个符合规范的处理器是任何以软件和/或硬件实现的算法,用于生成和/或消费[=符合规范的文档=],并遵循第[[[#algorithms]]]节中的相关规范性声明。符合规范的处理器在消费不符合规范的文档时必须产生错误。

术语

本节定义了本规范中使用的术语。每当这些术语出现在本规范中时,都会包含指向相关定义的链接。

公钥
可用于验证使用对应[=私钥=]创建的[=证明=]的加密材料。
私钥
可用于生成[=证明=]的加密材料。
认证
一种实体可以向验证者证明其拥有特定属性或控制特定秘密的过程。
证明
一种确认断言有效性的数学证明。该证明由一组属性和值组成,使验证者能够以加密方式验证该断言。 数字签名是一种证明。
授权
一种实体可以向验证者证明其被允许执行特定活动的过程。
加密套件
使用特定加密原语实现特定安全目标的方法。这些套件可以指定[=验证方法=]、数字签名类型、它们的标识符以及其他相关属性。
控制者

能够对特定资源执行操作的实体,例如更新[=受控标识符文档=]或使用[=验证方法=]生成[=证明=]。

受控标识符

一种可以证明由某个实体控制的标识符。

受控标识符文档

包含加密材料并列出服务端点的文档,可用于验证来自标识符[=控制者=]的[=证明=]并与其交互。

主体

一个实体,例如人、群体、组织、物理事物、数字事物或逻辑事物,由[=受控标识符文档=]中的`id`属性值引用。在[=受控标识符文档=]中标识的主体也可在其他上下文中使用,例如在[=认证=]或可验证凭证中。

验证方法

用于独立验证[=证明=]的方法及其参数。例如,加密公钥可以作为验证方法用于数字签名;在这种用法中,它验证签名者使用了关联的加密私钥。

验证关系

一种表达一个或多个[=验证方法=]被授权代表[=主体=]验证证明的方式。验证关系的一个示例是[[[#authentication]]]。

数据模型

[=受控标识符文档=]指定了[=标识符=]与一组[=验证方法=]和/或服务端点之间的一种或多种关系。[=受控标识符文档=]应包含[=验证关系=],明确允许将某些[=验证方法=]用于特定目的。

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example",
  "authentication": [{
      "id": "https://controller.example#authn-key-123",
      "type": "Multikey",
      "controller": "https://controller.example",
      "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }]
}

上面的示例展示了一个有效的 JSON-LD [=受控标识符文档=],它使用 Multikey 格式表达了一个可用于[=认证=]的[=公钥=]。

{
  "id": "https://controller.example/101",
  "verificationMethod": [{
    "id": "https://controller.example/101#key-20240828",
    "type": "JsonWebKey",
    "controller": "https://controller.example/101",
    "publicKeyJwk": {
      "kid": "key-20240828",
      "kty": "EC",
      "crv": "P-256",
      "alg": "ES256",
      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
    }
  }],
  "authentication": ["#key-20240828"]
}

上面的示例展示了一个有效的 JSON [=受控标识符文档=],它使用 JsonWebKey 格式表达了一个可用于[=认证=]的[=公钥=]。

属性名称 `id`、`type` 和 `controller` 可以出现在不同类型的映射中,并可能具有��同的约束。

受控标识符文档

以下部分定义了[=受控标识符文档=]中的属性,包括这些属性是必需的还是可选的。这些属性描述了[=主体=]与属性值之间的关系。

以下表格包含了本规范定义的核心属性的参考信息,包括预期值以及是否为必需属性。表格中的属性名称链接到规范性定义和每个属性的更详细描述。

属性 是否必需? 值约束 定义
`id` 一个符合 URL 语法的字符串 [[[#subjects]]]
`controller` 一个字符串或一组字符串,每个字符串都符合 URL 语法。 [[[#controllers]]]
`alsoKnownAs` 一组字符串,每个字符串都符合 URL 语法。 [[[#also-known-as]]]
`service` 一组[=服务=]映射 [[[#services]]]
`verificationMethod` 一组[=验证方法=]映射 [[[#verification-methods]]]
`authentication` 一组符合 URL 语法的字符串,或一组[=验证方法=]映射 [[[#authentication]]]
`assertionMethod` 一组符合 URL 语法的字符串,或一组[=验证方法=]映射 [[[#assertion]]]
`keyAgreement` 一组符合 URL 语法的字符串,或一组[=验证方法=]映射 [[[#key-agreement]]]
`capabilityInvocation` 一组符合 URL 语法的字符串,或一组[=验证方法=]映射 [[[#capability-invocation]]]
`capabilityDelegation` 一组符合 URL 语法的字符串,或一组[=验证方法=]映射 [[[#capability-delegation]]]

主体

[=主体=]通过[=受控标识符文档=]中的`id`属性表示。`id`属性的值被称为标识符

id
`id`属性的值必须是符合[[[URL]]]规则的字符串

[=受控标识符文档=]必须在最顶层映射中包含一个`id`值。

            {
              "@context": "https://www.w3.org/ns/cid/v1",
              "id": "https://controller.example",
              "authentication": [{
                  "id": "https://controller.example#authn-key-123",
                  "type": "Multikey",
                  "controller": "https://controller.example",
                  "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
              }]
            }
            

[=受控标识符文档=]的最顶层映射中的`id`属性值被称为[=受控标识符文档=]的基础标识符。用于检索给定[=标识符=]的当前权威[=受控标识符文档=]的URL被称为[=受控标识符文档=]的规范URL。解析[=规范URL=]必须返回当前权威[=受控标识符文档=]。返回文档的[=基础标识符=]必须与[=规范URL=]相同;如果不同,则返回的文档不是权威[=受控标识符文档=],并且[=标识符=]应被视为无效。每个受控标识符文档都根据文档的[=规范URL=]存储和检索,该URL也必须是文档的[=基础标识符=]。

预期[=受控标识符文档=]中`id`引用的[=主体=]在时间上保持一致,以便任何使用它们的可验证凭证都可以被解释为引用同一实体。例如,建议可验证凭证颁发者要求[=主体=]证明其对[=标识符=]的控制权,然后再颁发以该[=标识符=]为[=主体=]的凭证,从而确保每个以该[=标识符=]为[=主体=]的凭证的颁发都涉及同一实体。

然而,在某些情况下,这种做法可能是不可能或不合理的;例如,当父母为其子女请求可验证凭证时。此外,也可能存在颁发者犯错或故意发布虚假声明的情况。在评估对给定[=标识符=]的依赖的安全影响时,会考虑所有这种可能性。参见第[[[#identifier-ambiguity]]]节。

控制者

[=受控标识符文档=]的[=控制者=]是任何能够对该[=受控标识符文档=]进行更改的实体。能够更新通过解析控制者文档的规范 URL 返回的资源内容的任何人,按定义都是该文档及其规范标识符的控制者。满足[=受控标识符文档=]的[=验证方法=]的证明被视为对[=标识符=]的[=控制者=]创建这些[=证明=]的加密保证。

[=受控标识符文档=]的[=控制者=]被视为文档规范[=标识符=]的[=控制者=],也称为其 URL。也就是说,能够更新[=受控标识符文档=]的人既是文档的[=控制者=],也是[=标识符=]的[=控制者=]。更新文档是控制[=标识符=]的方式。这些术语可以互换使用。控制[=标识符=]的规范[=受控标识符文档=]等同于控制[=标识符=]。

controller
`controller`属性是可选的。如果可以将文档的合法控制者表示为 URL,则文档应列出标识这些控制者的 URL。

可以列出一个功能上由[=受控标识符文档=]的[=控制者=]以外的人控制的验证方法。例如,文档[=控制者=]可以将另一个方控制的公钥设置为身份验证验证方法。这将使另一方能够代表该[=标识符=]进行身份验证(因为他们的公钥列在身份验证验证方法中),而不会使该方能够更新[=受控标识符文档=]。然而,由于文档[=控制者=]明确列出了该密钥用于身份验证,因此该证明被视为由文档[=控制者=]创建,因为它是由其明确指定的受托人创建的。这在“另一方”是文档控制者控制的设备但具有独立的加密权限(即,它有自己的密钥存储并可以生成[=证明=])时特别有用。这种模式使不同的设备能够使用自己的加密材料生成可验证的[=证明=],这些[=证明=]被视为由标识符的控制者创建的。

如果存在,其值必须是一个字符串集合,其中每个字符串都符合[[[URL]]]规则。

`controller`属性中的每个条目必须标识一个能够更新[=受控标识符文档=]规范版本的实体。通过其规范位置对该[=受控标识符文档=]的后续请求将始终接收最新版本。

如果`controller`属性不存在,则文档的控制权完全由其存储位置决定。

  {
    "@context": "https://www.w3.org/ns/cid/v1",
    "id": "https://controllerA.example",
    "controller": "https://controllerB.example/abc",
    "authentication": [{
        "id": "https://controllerA.example#authn-key-123",
        "type": "Multikey",
        "controller": "https://controllerA.example",
        "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
    }]
  }
  

虽然用于[=控制者=]的[=标识符=]是明确的,但这并不意味着单个实体始终是[=控制者=],也不意味着[=控制者=]只有一个标识符。[=控制者=]可能是单个实体,也可能是多个实体的集合,例如合伙企业。[=控制者=]还可能使用多个标识符来引用自己,例如出于隐私目的或在组织内划分操作边界。同样,[=控制者=]可能控制许多[=验证方法=]。因此,不应假设[=控制者=]是单一实体,也不应假设其仅控制单一[=验证方法=]。

请注意,[=身份验证=]的定义与[=授权=]的定义不同。一般来说,[=身份验证=]回答“我们知道这是谁吗?”的问题,而[=授权=]回答“他们被允许执行此操作吗?”的问题。本规范中的`authentication`属性用于执行[=身份验证=],而其他[=验证关系=](例如`capabilityDelegation`和`capabilityInvocation`)用于执行[=授权=]。由于成功执行[=授权=]可能对系统产生更严重的影响,建议[=控制者=]在执行[=身份验证=]与[=授权=]时使用不同的[=验证方法=],并为用于[=授权=]的[=验证方法=]提供比用于[=身份验证=]的更强的访问保护。有关威胁模型和攻击向量的信息,请参见[[[#security-considerations]]]。

也称为

一个[=主体=]可以有多个标识符,这些标识符用于不同的目的或在不同的时间使用。可以使用`alsoKnownAs`属性声明两个或多个标识符(或其他类型的URI)指向同一个[=主体=]。

alsoKnownAs
`alsoKnownAs`属性是可选的。如果存在,其值必须是一个集合,其中每个项目都是符合[[RFC3986]]的URI。

此关系声明此标识符的主体也由一个或多个其他标识符标识。

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example",
  "alsoKnownAs": [
    "https://someOtherIdentifier.example/xyz",
    "https://yetAnotherIdentifier.example/987"
  ],
  "authentication": [{
      "id": "https://controller.example#authn-key-123",
      "type": "Multikey",
      "controller": "https://controller.example",
      "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }]
}
            

应用程序可能会选择将通过`alsoKnownAs`相关联的两个标识符视为等价,如果一个[=主体=]的受控标识符文档中表达的`alsoKnownAs`关系在另一个[=主体=]的受控标识符文档中也以相反方向表达(即,互为关系)。在没有这种互为关系的情况下,最好不要将它们视为等价。换句话说,`alsoKnownAs`声明的存在并不能证明该声明是真实的。因此,强烈建议请求方对`alsoKnownAs`声明进行独立验证。

鉴于[=主体=]可能出于不同目的使用不同的标识符,例如增强隐私保护,即使存在互为关系,也不一定适合期望两个标识符之间具有强等价性,或采取行动合并两个对应[=受控标识符文档=]中的信息。

服务

[=服务=]在[=受控标识符文档=]中用于表达与[=控制者=]或相关实体就受控[=标识符=]进行通信的方式。[=服务=]可以是[=控制者=]希望宣传以供进一步发现、认证、授权或交互的任何类型的服务。

由于隐私问题,不建议通过[=服务=]公开社交媒体账户、个人网站和电子邮件地址等公共信息。有关隐私问题的进一步探讨可以在[[[#keep-personal-data-private]]]和[[[#service-privacy]]]部分找到。与[=服务=]相关的信息通常是服务特定的。例如,与加密消息服务相关的信息可以表达如何在消息开始之前建立加密链接。

[=服务=]通过`service`属性表达,具体描述如下:

service

`service`属性是可选的。如果存在,关联的值必须是一个集合,其中每个服务由一个映射描述。每个[=服务=]映射必须包含`id`、`type`和`serviceEndpoint`属性。每个服务扩展可以包括其他属性,并可以进一步限制与扩展相关的属性。

id
`id`属性是可选的。如果存在,其值必须是符合[[[URL]]]的URL。一个[=符合规范的文档=]不得包含具有相同`id`的多个`service`条目。
type
`type`属性是必需的。其值必须是一个字符串集合,其中包含一个或多个字符串。为了最大化互操作性,[=服务=]类型及其相关属性应在[[[?VC-EXTENSIONS]]]中注册。
serviceEndpoint
`serviceEndpoint`属性是必需的。`serviceEndpoint`属性的值必须是单个字符串、单个映射,或由一个或多个字符串和/或映射组成的集合。每个字符串值必须是符合[[[URL]]]的有效URL。

有关与[=服务=]相关的隐私和安全注意事项的更多信息,请参见[[[#service-privacy]]]、[[[#keep-personal-data-private]]]、[[[#controlled-identifier-document-correlation-risks]]]和[[[#service-endpoints-for-authentication-and-authorization]]]。

{
  "id": "https://controller.example",
  "authentication": [{
      "id": "https://controller.example#authn-key-123",
      "type": "Multikey",
      "controller": "https://controller.example",
      "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }],
  "service": [{
    "type": "https://social.example/ExampleSocialMediaService",
    "serviceEndpoint": "https://warbler.example/sal674"
  }]
}
      

验证方法

一个[=受控标识符文档=]可以表达[=验证方法=],例如加密[=公钥=],这些方法可用于验证[=证明=],例如用于[=认证=]或授权与[=控制者=]或相关方的交互。例如,加密[=公钥=]可以作为验证方法用于数字签名;在这种用法中,它验证签名者能够使用关联的加密私钥。验证方法可能包含许多参数。例如,一组五个加密密钥中,任何三个都需要参与生成加密门限签名。

“验证”和“证明”被广泛应用。例如,在 Diffie-Hellman 密钥交换期间,加密公钥可能被用来协商对称加密密钥以进行加密。这保证了密钥协商过程的完整性。因此,这也是一种验证方法,即使描述该过程时可能未使用“验证”或“证明”这些词。

在[=受控标识符文档=]中使用以下映射定义[=验证方法=],并称之为验证方法定义

verificationMethod

`verificationMethod`属性是可选的。如果存在,其值必须是集合,其中每个[=验证方法=]都使用映射表达。[=验证方法=]映射必须包含`id`、`type`、`controller`以及由`type`值决定并在[[[#verification-material]]]中定义的特定验证材料属性。[=验证方法=]可以包含其他属性。

id

`id`属性的值必须是符合[[URL]]语法的字符串。该值被称为验证方法标识符,也可以在[=证明=]中用来引用特定的[=验证方法=]实例,这被称为[=验证方法定义=]。

type
`type`属性的值必须是一个字符串,引用一个且仅一个验证方法类型。本规范定义了`JsonWebKey`(见第[[[#JsonWebKey]]]节)和`Multikey`(见第[[[#Multikey]]]节)类型。
controller
`controller`属性的值必须是符合[[URL]]语法的字符串
expires
`expires`属性是可选的。 如果提供,其值必须是[[XMLSCHEMA11-2]] `dateTimeStamp`字符串,指定该[=验证方法=]应停止使用的时间。一旦设置,该值通常不应更新,依赖该值的系统应在到期时间或之后不再验证与该[=验证方法=]相关的任何[=证明=]。
revoked
`revoked`属性是可选的。 如果存在,其值必须是[[XMLSCHEMA11-2]] `dateTimeStamp`字符串,指定该[=验证方法=]必须停止使用的时间。一旦设置,该值通常不应更新,依赖该值的系统应在撤销时间或之后不再验证与该[=验证方法=]相关的任何[=证明=]。
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example",
  "verificationMethod": [{
    "id": "https://controller.example#authn-key-123",
    "type": "Multikey",
    "controller": "https://controller.example",
    "expires": "2025-12-01T00:00:00Z",
    "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }, {
    "id": "https://controller.example/101#key-20240828",
    "type": "JsonWebKey",
    "controller": "https://controller.example/101",
    "revoked": "2024-12-10T15:28:32Z",
    "publicKeyJwk": {
      "kid": "key-20240828",
      "kty": "EC",
      "crv": "P-256",
      "alg": "ES256",
      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
    }
  }]
}
          

`controller` 属性被 [=受控标识符文档=] 使用(详见第[[[#controlled-identifier-documents]]]节)以及 [=验证方法=] 使用(详见第[[[#verification-methods]]]节)。在这两种情况下,其目的基本相同,即表示一个或多个被授权执行与其关联资源相关的某些操作的实体。 然而,请注意,对于 [=验证方法=],这仅是由 [=受控标识符文档=] 的 `controller` 声明的断言,而该断言不一定是真实的,即可能是错误的。 为了确保 [=验证方法=] 与特定 [=controller=] 的绑定,必须从 [=验证方法=] 的表达式出发,找到其 [=受控标识符文档=],然后验证后者是否同时包含对 [=验证方法=] 和其 [=controller=] 的引用。有关确保正确绑定的算法,请参见第[[[#retrieve-verification-method]]]节。

[=受控标识符文档=] 的 [=controller=] 可以更新文档的内容。只有 [=验证方法=] 的实际 [=controller=](无论谁被声明为 controller)可以生成满足该方法的证明。

为了确保明确的安全保证,[=验证方法=] 的 [=controller=] 不能从 [=受控标识符文档=] 中推断出来。必须明确表达密钥的 controller 标识符,因为 [=验证方法=] 的 `controller` 值不一定是 [=受控标识符文档=] 的 `controller` 值。

验证材料

验证材料是任何用于应用[=验证方法=]的过程的信息。[=验证方法=]的`type`通常用于确定其与这些过程的兼容性。[=验证方法=]的示例包括`JsonWebKey`和`Multikey`。[=加密套件=]规范负责指定[=验证方法=]的`type`及其相关的验证材料格式。有关使用验证材料的示例,请参见 使用 JOSE 和 COSE 保护可验证凭证数据完整性 ECDSA 加密套件以及 数据完整性 EdDSA 加密套件

为了提高互操作性实现的可能性,本规范限制了在[=受控标识符文档=]中表达验证材料的格式数量。实现者需要选择的格式越少,实现互操作性的可能性就越大。这种方法试图在简化实现和支持历史上广泛部署的格式之间取得微妙的平衡。

[=验证方法=]不得包含针对同一材料的多个验证材料属性。例如,在[=验证方法=]中同时使用`publicKeyJwk`和`publicKeyMultibase`表达密钥材料是被禁止的。

实现可以根据操作需要或与加密库接口的需要,在格式之间转换密钥。作为内部实现细节,这种转换不得影响密钥材料的外部表示。

以下示例展示了一个包含使用上述两种属性的验证方法的[=受控标识符文档=]。

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example",
  "verificationMethod": [{
    "id": "https://controller.example#authn-key-123",
    "type": "Multikey",
    "controller": "https://controller.example",
    "expires": "2025-12-01T00:00:00Z",
    "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }, {
    "id": "https://controller.example/101#key-20240828",
    "type": "JsonWebKey",
    "controller": "https://controller.example/101",
    "revoked": "2024-12-10T15:28:32Z",
    "publicKeyJwk": {
      "kid": "key-20240828",
      "kty": "EC",
      "crv": "P-256",
      "alg": "ES256",
      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
    }
  }]
}
            

多密钥 (Multikey)

多密钥数据模型是一种特定类型的[=验证方法=],它将密钥类型编码为单个二进制流,然后按照第[[[#multibase-0]]]节的描述将其编码为多基值 (Multibase)。

当指定一个`Multikey`时,该对象的形式如下:

type
`type`属性的值必须是一个字符串,并设置为`Multikey`。
publicKeyMultibase
`publicKeyMultibase`属性是可选的。如果存在,其值必须是按照第[[[#multibase-0]]]节描述的多基值编码值。
secretKeyMultibase
`secretKeyMultibase`属性是可选的。如果存在,其值必须是按照第[[[#multibase-0]]]节描述的多基值编码值。

下面的示例使用上述的格式表示了一个Ed25519公钥:

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example/123456789abcdefghi#keys-1",
  "type": "Multikey",
  "controller": "https://controller.example/123456789abcdefghi",
  "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
}
  

公钥值的表达规则如下表所示:

密钥类型 描述
ECDSA 256位公钥 P-256公钥的多密钥编码必须以两字节前缀`0x8024`(`0x1200`的变长整数表示)开头,后跟33字节的压缩公钥数据。生成的35字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
ECDSA 384位公钥 P-384公钥的编码必须以两字节前缀`0x8124`(`0x1201`的变长整数表示)开头,后跟49字节的压缩公钥数据。生成的51字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
Ed25519 256位公钥 Ed25519公钥的编码必须以两字节前缀`0xed01`(`0xed`的变长整数表示)开头,后跟32字节的公钥数据。生成的34字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
BLS12-381 381位公钥 G2组中的BLS12-381公钥的编码必须以两字节前缀`0xeb01`(`0xeb`的变长整数表示)开头,后跟96字节的压缩公钥数据。生成的98字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
SM2 256位公钥 SM2公钥的编码必须以两字节前缀`0x8624`(`0x1206`的变长整数表示)开头,后跟33字节的压缩公钥数据。生成的35字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。

私钥值的表达规则如下表所示:

密钥类型 描述
ECDSA 256位私钥 P-256私钥的多密钥编码必须以两字节前缀`0x8626`(`0x1306`的变长整数表示)开头,后跟32字节的私钥数据。生成的34字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
ECDSA 384位私钥 P-384私钥的编码必须以两字节前缀`0x8726`(`0x1307`的变长整数表示)开头,后跟48字节的私钥数据。生成的50字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
Ed25519 256位私钥 Ed25519私钥的编码必须以两字节前缀`0x8026`(`0x1300`的变长整数表示)开头,后跟32字节的私钥数据。生成的34字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
BLS12-381 381位私钥 G2组中的BLS12-381私钥的编码必须以两字节前缀`0x8030`(`0x130a`的变长整数表示)开头,后跟96字节的压缩公钥数据。生成的98字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。
SM2 256位私钥 SM2私钥的编码必须以两字节前缀`0x9026`(`0x1310`的变长整数表示)开头,后跟32字节的私钥数据。生成的34字节值必须按照第[[[#multibase-0]]]节的描述使用base-58-btc字母表编码,并在前面加上base-58-btc多基值头部(`z`)。

开发者应注意不要意外发布私钥的表示形式。遵循本规范的实现将在遇到非上述公钥头部表中的多密钥头部值时,或在读取预期为公钥的多密钥值(例如发布在受控标识符文档中的公钥)但未以已知公钥头部开头时引发错误。

在为`publicKeyMultibase`和`secretKeyMultibase`定义值时,规范作者可以为其他规范中的其他密钥类型定义额外的头部值,但不得为本规范已定义的密钥类型定义替代编码。

JsonWebKey

JSON Web Key (JWK) 数据模型是一种特定类型的[=验证方法=],它使用 JWK 规范 [[RFC7517]] 将密钥类型编码为一组参数。

当指定一个 `JsonWebKey` 时,该对象的形式如下:

type
`type` 属性的值必须是一个字符串,并设置为 `JsonWebKey`。
publicKeyJwk

`publicKeyJwk` 属性是可选的。如果存在,其值必须是一个符合 [[RFC7517]] 的 JSON Web Key 的映射。该映射不得包含任何属于私密信息类别的成员,例如 `d`,详见 JWK 注册模板。建议使用 JWKs [[RFC7517]] 表示其[=公钥=]的验证方法将 `kid` 的值用作其片段标识符。建议将 JWK 的 `kid` 值设置为使用 SHA-256 (SHA2-256) 哈希函数计算的 JWK 指纹 [[RFC7638]]。 有关包含复合密钥标识符的公钥示例,请参见 [[[#example-various-verification-method-types]]] 中的第一个密钥。

JWK 规范第 4.4 节所述,可选的 `alg` 属性标识了公钥预期使用的算法,建议包含该属性以防止在同一密钥与多个算法一起使用时可能出现的安全问题。如 JWA 规范第 6.2.1.1 节所述,描述使用椭圆曲线的密钥时,必需的 `crv` 属性用于标识公钥的特定曲线类型。如 JWS 规范第 4.1.4 节所述,可选的 `kid` 属性是一个提示,用于帮助发现密钥;如果存在,`kid` 值应与封装 `JsonWebKey` 对象的 `id` 属性匹配,或包含在其路径、查询或片段中。

secretKeyJwk
`secretKeyJwk` 属性是可选的。如果存在,其值必须是一个符合 [[RFC7517]] 的 JSON Web Key 的映射。 如果包含该数据结构的内容是公开的或可能被合法密钥持有者以外的方披露,则不得使用该属性。

以下是符合 `JsonWebKey` 的对象示例:

{
  "id": "https://controller.example/123456789abcdefghi#key-1",
  "type": "JsonWebKey",
  "controller": "https://controller.example/123456789abcdefghi",
  "publicKeyJwk": {
      "kid": "key-1",
      "kty": "EC",
      "crv": "P-384",
      "alg": "ES384",
      "x": "1F14JSzKbwxO-Heqew5HzEt-0NZXAjCu8w-RiuV8_9tMiXrSZdjsWqi4y86OFb5d",
      "y": "dnd8yoq-NOJcBuEYgdVVMmSxonXg-DU90d7C4uPWb_Lkd4WIQQEH0DyeC2KUDMIU"
    }
}
  

在上面的示例中,`publicKeyJwk` 值包含 JSON Web Key。`kty` 属性编码了密钥类型 "EC",表示 "椭圆曲线"。`alg` 属性标识了公钥预期使用的算法,在此示例中为 `ES384`。`crv` 属性标识了公钥的特定曲线类型 `P-384`。`x` 和 `y` 属性指定了与公钥关联的 `P-384` 曲线上的点。

`publicKeyJwk` 属性不得包含在 JOSE 注册表 [[JOSE-REGISTRIES]] 中标记为 "Private" 或 "Secret" 的任何属性,包括 "d"。

JSON Web Key 数据模型还能够编码私钥,有时也称为秘密密钥

{
  "id": "https://controller.example/123456789abcdefghi#key-1",
  "type": "JsonWebKey",
  "controller": "https://controller.example/123456789abcdefghi",
  "secretKeyJwk": {
      "id": "secret-1",
      "kty": "EC",
      "crv": "P-384",
      "alg": "ES384",
      "d": "fGwges0SX1mj4eZamUCL4qtZijy9uT15fI4gKTuRvre4Kkoju2SHM4rlFOeKVraH",
      "x": "1F14JSzKbwxO-Heqew5HzEt-0NZXAjCu8w-RiuV8_9tMiXrSZdjsWqi4y86OFb5d",
      "y": "dnd8yoq-NOJcBuEYgdVVMmSxonXg-DU90d7C4uPWb_Lkd4WIQQEH0DyeC2KUDMIU"
    }
}
  

上面的私钥示例与前面的公钥示例几乎相同,不同之处在于信息存储在 `secretKeyJwk` 属性中(而不是 `publicKeyJwk`),并且私钥值编码在其 `d` 属性中(同时 `x` 和 `y` 属性仍然指定了与公钥关联的 `P-384` 曲线上的点)。

引用验证方法

[=验证方法=] 可以嵌入到或从与各种 [=验证关系=] 相关的属性中引用,如第[[[#verification-relationships]]]节所述。引用 [=验证方法=] 允许它们被多个 [=验证关系=] 使用。

如果 [=验证方法=] 属性的值是一个 映射,则 [=验证方法=] 已被嵌入,其属性可以直接访问。然而,如果值是一个 URL 字符串,则 [=验证方法=] 是通过引用包含的,其属性需要从 [=受控标识符文档=] 的其他部分或另一个 [=受控标识符文档=] 中检索。这是通过取消引用 URL 并在结果资源中搜索具有 `id` 属性且值与 URL 匹配的 [=验证方法=] 映射来完成的。

          {
            "@context": "https://www.w3.org/ns/cid/v1",
            "id": "https://controller.example",
            "authentication": [
              // 此密钥是引用的,可能被多个验证关系使用
              "https://controllerB.example/123456789abcdefghi#keys-1",
              // 此密钥是嵌入的,仅可用于身份验证
              {
                "id": "https://controllerA.example/123456789abcdefghi#keys-2",
                "type": "Multikey",
                "controller": "https://controller.example/123456789abcdefghi",
                "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
              }
            ]
          }
            

验证关系

[=验证关系=] 是一种表达方式,用于指定一个或多个 [=验证方法=] 被授权代表主体验证 [=证明=]。

不同的 [=验证关系=] 允许关联的 [=验证方法=] 用于不同的目的。由 验证者 确定验证尝试的有效性,方法是检查所使用的 [=验证方法=] 是否在 [=受控标识符文档=] 中被适当的 [=验证关系=] 属性引用。

[=受控标识符文档=] 中明确表达了 [=主体=] 和 [=验证方法=] 之间的 [=验证关系=]。未与特定 [=验证关系=] 关联的 [=验证方法=] 不能用于该 [=验证关系=]。例如,与 `authentication` 属性关联的 [=验证方法=] 不能用于密钥协商协议 — 必须使用 keyAgreement 属性的值。

如果引用的 [=验证方法定义=] 不在用于解析它的最新 [=受控标识符文档=] 中,则该 [=验证方法=] 被视为无效或已撤销。

以下部分定义了几种有用的 [=验证关系=]。一个 [=受控标识符文档=] 可以包含这些或其他属性,以表达特定的 [=验证关系=]。为了最大化互操作性,所使用的任何此类属性应在 [[DID-EXTENSIONS-PROPERTIES]] 中注册。

身份验证

`authentication` [=验证关系=] 用于指定如何对 [=主体=] 进行 [=身份验证=],例如登录网站或参与任何形式的质询-响应协议。身份验证后的处理是特定于应用程序的。

authentication
`authentication` 属性是可选的。如果存在,其值必须是一个 集合,包含一个或多个 [=验证方法=]。每个 [=验证方法=] 可以是嵌入的或引用的。
          {
            "@context": "https://www.w3.org/ns/cid/v1",
            "id": "https://controller.example/123456789abcdefghi",
            ...
            "authentication": [
              // 此方法可用于身份验证
              "https://controller.example/123456789abcdefghi#keys-1",
              // 此方法仅被批准用于身份验证,因此其完整描述嵌入在此而不是仅使用引用
              {
                "id": "https://controller.example/123456789abcdefghi#keys-2",
                "type": "JsonWebKey",
                "controller": "https://controller.example/123456789abcdefghi",
                "publicKeyJwk": {
                  "crv": "Ed25519",
                  "x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ",
                  "kty": "OKP",
                  "kid": "_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A"
                }
              },
              {
                "id": "https://controller.example/123456789abcdefghi#keys-3",
                "type": "Multikey",
                "controller": "https://controller.example/123456789abcdefghi",
                "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
              }
            ]
            ...
          }
            

这对任何验证身份验证的实体都很有用,这些实体需要检查试图 [=身份验证=] 的实体是否提供了有效的身份验证证明。当这样的身份验证验证实体接收到一些数据(以某种协议特定的格式)包含为 "authentication" 目的而制作的 [=证明=],并且表明某个实体由 `id` 标识时,该 验证者 检查以确保 [=证明=] 可以使用 [=受控标识符文档=] 中 `authentication` 下列出的 [=验证方法=](例如,[=公钥=])进行验证。

请注意,[=受控标识符文档=] 的 `authentication` 属性所指示的 [=验证方法=] 只能用于代表 [=受控标识符文档=] 的 [=基础标识符=] 进行 [=身份验证=]。

断言

`assertionMethod` [=验证关系=] 用于指定 [=控制者=] 授权用于表达断言或声明(例如在可验证凭证中)的 [=验证方法=]。

assertionMethod
`assertionMethod` 属性是可选的。如果存在,其关联值必须是一个 集合,包含一个或多个 [=验证方法=]。每个 [=验证方法=] 可以是嵌入的或引用的。

例如,在验证者处理可验证凭证时,此属性非常有用。

          {
            "@context": "https://www.w3.org/ns/cid/v1",
            "id": "https://controller.example/123456789abcdefghi",
            ...
            "assertionMethod": [
              // 此方法可用于断言声明
              "https://controller.example/123456789abcdefghi#keys-1",
              // 此方法仅被批准用于断言声明,不用于任何其他验证关系,因此其完整描述嵌入在此而不是仅使用引用
              {
                "id": "https://controller.example/123456789abcdefghi#keys-2",
                "type": "Multikey", // 外部(属性值)
                "controller": "https://controller.example/123456789abcdefghi",
                "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
              }
            ]
            ...
          }
            

密钥协商

`keyAgreement` [=验证关系=] 用于指定实体如何执行加密,以便传输给 [=控制者=] 的机密信息,例如用于与接收方建立安全通信通道。

keyAgreement
`keyAgreement` 属性是可选的。如果存在,其关联值必须是一个 集合,包含一个或多个 [=验证方法=]。每个 [=验证方法=] 可以是嵌入的或引用的。

此属性的一个有用场景是加密发送给 [=控制者=] 的消息。在这种情况下,对方使用 [=验证方法=] 中的加密 [=公钥=] 信息来包装接收方的解密密钥。

   {
     "@context": "https://www.w3.org/ns/cid/v1",
     "id": "https://controller.example/123456789abcdefghi",
     ...
     "keyAgreement": [
       "https://controller.example/123456789abcdefghi#keys-1",
       // 以下方法仅被批准用于密钥协商用途
       // 它们不会用于任何其他验证关系
       // 完整值嵌入在此,而不是仅使用引用
       {
         "id": "https://controller.example/123#keys-2",
         "type": "Multikey",
         "controller": "https://controller.example/123",
         "publicKeyMultibase": "zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
       },
       {
         "id": "https://controller.example/123#keys-3",
         "type": "JsonWebKey",
         "controller": "https://controller.example/123",
         "publicKeyJwk": {
           "kty": "OKP",
           "crv": "X25519",
           "x": "W_Vcc7guviK-gPNDBmevVw-uJVamQV5rMNQGUwCqlH0"
         }
       }
     ]
     ...
   }
     

能力调用

`capabilityInvocation` [=验证关系=] 用于指定 [=控制者=] 可能使用的 [=验证方法=],以调用加密能力,例如授权更新 [=受控标识符文档=]。

capabilityInvocation
`capabilityInvocation` 属性是可选的。如果存在,其关联值必须是一个 集合,包含一个或多个 [=验证方法=]。每个 [=验证方法=] 可以是嵌入的或引用的。

此属性的一个有用场景是 [=控制者=] 需要访问受保护的 HTTP API,该 API 需要授权才能使用。在使用 HTTP API 进行授权时,[=控制者=] 使用与特定 URL 相关联的能力,该 URL 通过 HTTP API 暴露。能力的调用可以通过多种方式表达,例如放置在 HTTP 头中的数字签名消息。

提供 HTTP API 的服务器是能力的 验证者,它需要验证被调用的能力所引用的 [=验证方法=] 是否存在于 [=受控标识符文档=] 的 `capabilityInvocation` 属性中。验证者还需要检查所执行的操作是否有效,以及能力是否适用于正在访问的资源。如果验证成功,服务器已通过加密方式确定调用者被授权访问受保护的资源。

   {
     "@context": "https://www.w3.org/ns/cid/v1",
     "id": "https://controller.example/123456789abcdefghi",
     ...
     "capabilityInvocation": [
       // 此方法可用于调用能力 https:...fghi
       "https://controller.example/123456789abcdefghi#keys-1",
       // 此方法仅被批准用于能力调用;它不会
       // 用于任何其他验证关系,因此其完整描述
       // 嵌入在此,而不是仅使用引用
       {
         "id": "https://controller.example/123456789abcdefghi#keys-2",
         "type": "Multikey", // 外部(属性值)
         "controller": "https://controller.example/123456789abcdefghi",
         "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
       }
     ]
     ...
   }
     

能力委托

`capabilityDelegation` [=验证关系=] 用于指定一种机制,该机制可能被用来将加密能力委托给其他方。此机制(例如授权能力UCAN)以及委托后的处理(例如访问特定的 HTTP API)是特定于应用程序的。

capabilityDelegation
`capabilityDelegation` 属性是可选的。如果存在,其关联值必须是一个集合,包含一个或多个 [=验证方法=]。每个 [=验证方法=] 可以是嵌入的或引用的。

此属性的一个有用场景是当 [=控制者=] 选择将其访问受保护 HTTP API 的能力委托给其他方时。为了委托能力,[=控制者=] 将使用与 `capabilityDelegation` [=验证关系=] 相关联的 [=验证方法=] 对能力进行加密签名并转交给另一个 [=控制者=]。受委托方随后可以以类似于[[[#capability-invocation]]]中描述的方式使用该能力。

            {
              "@context": "https://www.w3.org/ns/cid/v1",
              "id": "https://controller.example/123456789abcdefghi",
              ...
              "capabilityDelegation": [
                // 此方法可用于执行能力委托
                "https://controller.example/123456789abcdefghi#keys-1",
                // 此方法仅被批准用于授予能力;它不会
                // 用于任何其他验证关系,因此其完整描述
                // 嵌入在此,而不是仅使用引用
                {
                  "id": "https://controller.example/123456789abcdefghi#keys-2",
                  "type": "JsonWebKey", // 外部(属性值)
                  "controller": "https://controller.example/123456789abcdefghi",
                  "publicKeyJwk": {
                    "kty": "OKP",
                    "crv": "Ed25519",
                    "x": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
                  }
                },
                {
                  "id": "https://controller.example/123456789abcdefghi#keys-3",
                  "type": "Multikey", // 外部(属性值)
                  "controller": "https://controller.example/123456789abcdefghi",
                  "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
                }
              ]
              ...
            }
            

多基值编码 (Multibase)

多基值编码 (Multibase) 值将二进制值编码为 基于文本的编码字符串。 该值以单个字符的头部开始,用于标识编码的基数和用于编码二进制值的字母表, 后跟使用该基数和字母表编码的二进制值。以下列出的常见 Multibase头值及其相关的基数编码字母表是规范性的:

Multibase头 描述
`u` 使用 base-64-url-no-pad 字母表对字节进行编码。基数字母表按以下顺序包含以下字符: `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_`
`z` 使用 base-58-btc 字母表对字节进行编码。基数字母表按以下顺序包含以下字符: `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`

可以使用其他 Multibase 编码值,但不同实现之间的互操作性无法得到保证。

要将二进制值基于编码为 Multibase 字符串,实现必须对二进制值应用第[[[#base-encode]]]节中的算法, 使用上表中所需的基数编码和字母表,并确保在结果前添加上表中关联的 Multibase头。 可以使用任何具有等效输出的算法。

要对 Multibase 字符串进行基于解码,实现必须对第一个字符(Multibase头)之后的字符串应用第[[[#base-decode]]]节中的算法, 使用与 Multibase头关联的字符表。可以使用任何具有等效输出的算法。

多哈希 (Multihash)

多哈希值以一个二进制头部开始,其中包括:1)特定加密哈希算法的标识符,2)加密摘要的字节长度,以及 3)加密摘要的值。本规范定义的规范性多哈希头部值及其相关的输出大小和关联规范如下:

多哈希标识符 多哈希头部 描述
`sha2-256` `0x12` SHA-2,输出 256 位(32 字节),定义于 [[RFC6234]]。
`sha2-384` `0x20` SHA-2,输出 384 位(48 字节),定义于 [[RFC6234]]。
`sha3-256` `0x16` SHA-3,输出 256 位(32 字节),定义于 [[SHA3]]。
`sha3-384` `0x15` SHA-3,输出 384 位(48 字节),定义于 [[SHA3]]。

可以使用其他多哈希编码值,但不同实现之间的互操作性无法得到保证。

要编码为多哈希值,实现必须按顺序连接关联的多哈希头部(以 varint 编码)、加密摘要的字节长度(以 varint 编码)以及加密摘要值。

要解码多哈希值,实现必须:1)移除前置的多哈希头部值,该值标识加密哈希算法的类型;2)移除加密摘要的字节长度;3)提取原始加密摘要值,该值必须与多哈希头部关联的预期输出长度以及多哈希值本身提供的输出长度相匹配。

算法

本节定义了本规范使用的算法,包括以下内容的说明:如何进行基值编码和解码;如何安全地检索验证方法;如何检索文档片段;以及如何通过 HTTP 通道生成处理错误的描述。可以使用本节提供的算法的替代方法,只要替代算法的输出保持一致即可。

基值编码

以下算法指定了如何将字节数组(其中每个字节表示一个基于 256 的值)编码为使用特定基数字母表的不同基数表示形式,例如 base-64-url-no-pad 或 base-58-btc。所需的输入包括 bytestargetBasebaseAlphabet。输出是一个包含基值编码结果的字符串。所有数学运算必须使用整数算术执行。

  1. 初始化以下变量:将 zeroes 设置为 `0`,length 设置为 `0`,begin 设置为 `0`,end 设置为 bytes 的长度。
  2. beginzeroes 设置为 bytes 中前导 `0` 字节值的数量。
  3. baseValue 设置成空字节数组,其大小为最终基值扩展值的大小。通过将 log(256) 除以 log(targetBase),然后乘以 bytes 的长度减去前导 zeroes 来计算 baseValue 的最终 size。将 `1` 添加到 size 的值中。
  4. 从偏移量 begin 开始,按顺序处理 bytes 中的每个字节 byte
    1. carry 值设置为 byte
    2. baseValue 数组的末尾开始执行基值扩展。初始化迭代器 i 为 `0`。将 basePosition 设置为 size 减去 `1`。只要 carry 不等于 `0` 或 i 小于 length,并且 basePosition 不等于 `-1`,执行以下循环:
      1. baseValue[basePosition] 的值乘以 `256` 并将其加到 carry
      2. carry 除以 targetBase 的余数设置为 baseValue[basePosition] 的值。
      3. carry 的值设置为 carry 除以 targetBase 的商,确保使用整数除法执行该操作。
      4. basePosition 减 `1`,并将 i 加 `1`。
    3. length 设置为 i,并将 begin 加 `1`。
  5. baseEncodingPosition 设置为 size 减去 length。当 baseEncodingPosition 不等于 sizebaseValue[baseEncodingPosition] 不等于 `0` 时,递增 baseEncodingPosition。此步骤跳过基值编码结果中的前导零。
  6. 通过重复 baseAlphabet 中的第一个条目 zeroes 次(即 bytes 中前导零的数量)来初始化 baseEncoding
  7. 将其余的 baseValue 转换为基值编码。当 baseEncodingPosition 小于 size 时,递增 baseEncodingPosition:将 baseValue[baseEncodingPosition] 设置为 baseEncodedValue。将 baseAlphabet[baseEncodedValue] 附加到 baseEncoding
  8. 返回 baseEncoding 作为基值编码结果。
        function baseEncode(bytes, targetBase, baseAlphabet) {
          let zeroes = 0;
          let length = 0;
          let begin = 0;
          let end = bytes.length;

          // 计算前导零字节的数量
          while(begin !== end && bytes[begin] === 0) {
            begin++;
            zeroes++;
          }

          // 分配足够的空间来存储目标基值
          const baseExpansionFactor = Math.log(256) / Math.log(targetBase);
          let size = Math.floor((end - begin) * baseExpansionFactor + 1);
          let baseValue = new Uint8Array(size);

          // 处理整个输入字节数组
          while(begin !== end) {
            let carry = bytes[begin];

            // 对数组中的每个字节执行基值扩展
            let i = 0;
            for(let basePosition = size - 1;
                (carry !== 0 || i < length) && (basePosition !== -1);
                basePosition--, i++) {
              carry += Math.floor(256 * baseValue[basePosition]);
              baseValue[basePosition] = Math.floor(carry % targetBase);
              carry = Math.floor(carry / targetBase);
            }

            length = i;
            begin++;
          }

          // 跳过基值编码结果中的前导零
          let baseEncodingPosition = size - length;
          while(baseEncodingPosition !== size &&
                baseValue[baseEncodingPosition] === 0) {
            baseEncodingPosition++;
          }

          // 将基值转换为基值编码
          let baseEncoding = baseAlphabet.charAt(0).repeat(zeroes)
          for(; baseEncodingPosition < size; ++baseEncodingPosition) {
            baseEncoding += baseAlphabet.charAt(baseValue[baseEncodingPosition])
          }

          return baseEncoding;
        }
        

基值解码

以下算法指定了如何将字节数组解码为不同的基数表示形式,其中每个字节表示一个基值编码的值,并使用特定的基数字母表,例如 base-64-url-no-pad 或 base-58-btc所需的输入包括 sourceEncodingsourceBasebaseAlphabet。输出是一个包含基值解码结果的字节数组。所有数学运算必须使用整数算术执行。

  1. 初始化 baseMap 映射,将 baseAlphabet 中的每个字符与其在 baseAlphabet 字符串中的整数位置关联。
  2. 初始化以下变量:将 sourceOffset 设置为 `0`,zeroes 设置为 `0`,decodedLength 设置为 `0`。
  3. zeroessourceOffset 设置为 sourceEncoding 中前导 baseAlphabet[0] 值的数量。
  4. decodedBytes 设置为空字节数组,其大小为最终基值转换值的大小。通过将 log(sourceBase) 除以 log(`256`),然后乘以 sourceEncoding 的长度减去前导零的数量来计算 decodedBytes 的大小。将 `1` 添加到大小值中。
  5. 从偏移量 sourceOffset 开始,按顺序处理 sourceEncoding 中的每个字符 character
    1. carry 值设置为 baseMap 中与 character 关联的整数值。
    2. decodedBytes 数组的末尾开始执行基值解码。初始化迭代器 i 为 `0`。将 byteOffset 设置为 decodedSize 减去 `1`。只要 carry 不等于 `0` 或 i 小于 decodedLength,并且 byteOffset 不等于 `-1`,执行以下循环:
      1. sourceBase 乘以 decodedBytes[byteOffset] 的结果加到 carry
      2. carry 除以 `256` 的余数设置为 decodedBytes[byteOffset] 的值。
      3. carry 设置为 carry 除以 `256` 的商,确保使用整数除法执行该操作。
      4. byteOffset 减 `1`,并将 i 加 `1`。
    3. decodedLength 设置为 i,并将 sourceOffset 加 `1`。
  6. decodedOffset 设置为 decodedSize 减去 decodedLength。当 decodedOffset 不等于 decodedSizedecodedBytes[decodedOffset] 等于 `0` 时,递增 decodedOffset。此步骤跳过最终基值解码字节数组中的前导零。
  7. finalBytes 数组的大小设置为 zeroes 加上 decodedSize 减去 decodedOffset。将 finalBytes 中的前 zeroes 个字节初始化为 `0`。
  8. 从偏移量等于 finalByteszeroes 的数量加 `1` 开始,复制 decodedBytes 中的所有字节,从 decodedOffset 开始到 decodedSize,到 finalBytes
function baseDecode(sourceEncoding, sourceBase, baseAlphabet) {
  // 构建基数字母表到整数值的映射
  baseMap = {};
  for(let i = 0; i < baseAlphabet.length; i++) {
    baseMap[baseAlphabet[i]] = i;
  }

  // 跳过并统计 sourceEncoding 中的零字节值
  let sourceOffset = 0;
  let zeroes = 0;
  let decodedLength = 0;
  while(sourceEncoding[sourceOffset] === baseAlphabet[0]) {
    zeroes++;
    sourceOffset++;
  }

  // 分配解码后的字节数组
  const baseContractionFactor = Math.log(sourceBase) / Math.log(256);
  let decodedSize = Math.floor((
    (sourceEncoding.length - sourceOffset) * baseContractionFactor) + 1);
  let decodedBytes = new Uint8Array(decodedSize);

  // 对 sourceEncoding 执行基值转换
  while(sourceEncoding[sourceOffset]) {
    // 处理每个基值编码的数字
    let carry = baseMap[sourceEncoding[sourceOffset]];

    // 通过执行基值扩展转换基值编码的数字
    let i = 0
    for(let byteOffset = decodedSize - 1;
      (carry !== 0 || i < decodedLength) && (byteOffset !== -1);
      byteOffset--, i++) {
      carry += Math.floor(sourceBase * decodedBytes[byteOffset]);
      decodedBytes[byteOffset] = Math.floor(carry % 256);
      carry = Math.floor(carry / 256);
    }

    decodedLength = i;
    sourceOffset++;
  }

  // 跳过解码字节数组中的前导零
  let decodedOffset = decodedSize - decodedLength;
  while(decodedOffset !== decodedSize && decodedBytes[decodedOffset] === 0) {
    decodedOffset++;
  }

  // 创建最终的基值解码字节数组
  let finalBytes = new Uint8Array(zeroes + (decodedSize - decodedOffset));
  let j = zeroes;
  while(decodedOffset !== decodedSize) {
    finalBytes[j++] = decodedBytes[decodedOffset++];
  }

  return finalBytes;
}
  

检索验证方法

以下算法指定了如何通过使用[=验证方法标识符=]安全地检索验证方法,例如加密[=公钥=]。所需的输入包括一个[=验证方法标识符=]vmIdentifier)、一个[=验证关系=]verificationRelationship)以及一组解引用选项options)。输出是一个[=验证方法=]

  1. 如果 vmIdentifier 不是有效的 URL,则必须引发错误,并应传递错误类型 INVALID_VERIFICATION_METHOD_URL
  2. controllerDocumentUrl 设置为根据 URL 方案的规则解析 vmIdentifier 并提取主资源标识符(不包括片段标识符)的结果。
  3. vmFragment 设置为根据 URL 方案的规则解析 vmIdentifier 并提取次级资源标识符(片段标识符)的结果。
  4. controllerDocument 设置为根据 URL 方案的规则并使用提供的 options 解引用 controllerDocumentUrl 的结果。
  5. 如果 controllerDocument 不是[=符合规范的受控标识符文档=],则必须引发错误,并应传递错误类型 INVALID_CONTROLLED_IDENTIFIER_DOCUMENT
  6. 如果 controllerDocument.idcontrollerDocumentUrl 不匹配,则必须引发错误,并应传递错误类型 INVALID_CONTROLLED_IDENTIFIER_DOCUMENT_ID
  7. verificationMethod 设置为根据 controllerDocument 的媒体类型的规则从 controllerDocument 解引用 vmFragment 的结果。
  8. 如果 verificationMethod 不是[=符合规范的验证方法=],则必须引发错误,并应传递错误类型 INVALID_VERIFICATION_METHOD
  9. 如果 verificationMethod.id 的绝对 URL 值不等于 vmIdentifier,则必须引发错误,并应传递错误类型 INVALID_VERIFICATION_METHOD
  10. 如果 verificationMethod.controller 的绝对 URL 值不等于 controllerDocumentUrl,则必须引发错误,并应传递错误类型 INVALID_VERIFICATION_METHOD
  11. 如果 verificationMethod 未通过引用(URL)或值(对象)与 controllerDocument 中由 verificationRelationship 标识的[=验证关系=]数组相关联,则必须引发错误,并应传递错误类型 INVALID_RELATIONSHIP_FOR_VERIFICATION_METHOD
  12. 返回 verificationMethod 作为[=验证方法=]

以下示例提供了一个最低限度符合规范的[=受控标识符文档=],其中包含一个最低限度符合规范的[=验证方法=],以满足本节算法的要求:

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example/123",
  "verificationMethod": [{
    "id": "https://controller.example/123#key-456",
    "type": "Multikey",
    "controller": "https://controller.example/123",
    "publicKeyMultibase": "z6MkmM42vxfqZQsv4ehtTjFFxQ4sQKS2w6WR7emozFAn5cxu"
  }],
  "authentication": ["https://controller.example/123#key-456"]
}
  

[=验证方法标识符=]可以通过 URL 字符串或 `id` 属性(其值为 URL)表示。[=受控标识符文档=]可以通过[=验证关系=]表达一个[=验证方法=],该方法存在于[=受控标识符文档=]之外的地方。正如[[[#integrity-protection-of-controllers]]]部分所述,指定一个外部的[=验证方法=]是本规范的有效用法。确保从外部[=受控标识符文档=]检索此[=验证方法=]至关重要。

在检索任何[=验证方法=]时,使用上述算法以确保从正确的[=受控标识符文档=]中检索[=验证方法=]。该算法还确保此[=受控标识符文档=]引用了[=验证方法=](通过[=验证关系=]),并且[=验证方法=]引用了[=受控标识符文档=](通过[=验证方法=]的 `controller` 属性)。未使用此算法或等效算法可能导致安全漏洞,例如攻击者通过声称控制受害者的[=验证方法=]来污染缓存。

{
  "id": "https://controller.example/123",
  "capabilityInvocation": ["https://external.example/xyz#key-789"]
}
  

在上述示例中,本节描述的算法将使用 `https://external.example/xyz#key-789` URL 值作为[=验证方法标识符=]。然后,算法将确认[=验证方法=]存在于外部[=受控标识符文档=]中,并且存在前面描述的适当关系。

在检索[=验证方法=]时,片段标识符的处理规则取决于[=受控标识符文档=]的媒体类型。尽管本节中的算法试图遵循相应[=受控标识符文档=]的媒体类型(例如 `application/did`),并且[[[#fragment-resolution]]]部分定义了如何根据 `application/cid` 媒体类型解析片段标识符,但实现者��注意,其他媒体类型的[=受控标识符文档=]可能需要不同的片段处理规则,并可能产生不同的结果。

片段解析

以下算法指定了如何检索包含给定片段标识符的文档部分。所需的输入是一个[=受控标识符文档=](映射 document)和一个片段标识符(字符串 fragmentIdentifier)。输出是一个包含文档片段的映射

  1. documentFragment 设置为 `null`。
  2. canonicalDocumentUrl 设置为 document.id 的值。
  3. fullyQualifiedFragment 设置为 canonicalDocumentUrl 加上 fragmentIdentifier 的值。
  4. 递归处理 document 中的每个映射,检查其 `id` 值是否等于 fullyQualifiedFragmentfragmentIdentifier。如果找到匹配项,将 documentFragment 设置为匹配的映射,并停止递归处理。
  5. 返回 documentFragment

虽然可以表达包含使用相同标识符的多个片段的文档,但由于互操作性问题,应避免这样做,其行为未定义。

处理错误

本规范中描述的算法会抛出特定类型的错误。实现者可能会发现将这些错误传递给其他库或软件系统很有用。本节提供了这些错误的具体 URL 和描述,以便实现本规范所述技术的生态系统在发生错误时能够更有效地互操作。

当通过 HTTP 接口公开这些错误时,实施者应使用 [[RFC9457]] 编码错误数据结构。如果使用 [[RFC9457]]:

INVALID_VERIFICATION_METHOD_URL
[=证明=] 中的 `verificationMethod` 值格式错误。参见第 [[[#retrieve-verification-method]]] 节。
INVALID_CONTROLLED_IDENTIFIER_DOCUMENT_ID
[=受控标识符文档=] 中的 `id` 值格式错误。参见第 [[[#retrieve-verification-method]]] 节。
INVALID_CONTROLLED_IDENTIFIER_DOCUMENT
[=受控标识符文档=] 格式错误。参见第 [[[#retrieve-verification-method]]] 节。
INVALID_VERIFICATION_METHOD
[=受控标识符文档=] 中的 [=验证方法=] 格式错误。参见第 [[[#retrieve-verification-method]]] 节。
INVALID_RELATIONSHIP_FOR_VERIFICATION_METHOD
[=受控标识符文档=] 中的 [=验证方法=] 未使用 `proofPurpose` 属性中表达的预期[=验证关系=]关联。参见第 [[[#retrieve-verification-method]]] 节。

上下文和词汇表

词汇表

本规范中定义的术语也是 RDF 词汇命名空间 [[RDF-CONCEPTS]] https://w3id.org/security# 的一部分。 对于任何 `TERM`,相关的 URL 形式为 `https://w3id.org/security#TERM` 或 `https://w3id.org/security#TERMmethod`。 使用 RDF 处理并依赖本规范的实现必须使用这些 URL。

当解引用 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 上下文 URL 视为已解析, 解析后的文档与以下对应的哈希值匹配:

上下文 URL 和哈希值
URL: https://www.w3.org/ns/cid/v1
SHA2-256 摘要:

可以通过运行以下命令确认上述加密摘要,在现代类 UNIX 操作系统的命令行界面中执行: `curl -sL -H "Accept: application/ld+json" https://www.w3.org/ns/cid/v1 | openssl dgst -sha256`

上述 JSON-LD 上下文解析的安全词汇表术语位于 https://w3id.org/security# 命名空间中。有关更多详细信息,请参见 [[[#vocabulary]]]。

应用程序或规范可以使用其自己的 JSON-LD 上下文定义到词汇表 URL 的映射。 例如,这些映射是 `https://w3id.org/security/data-integrity/v2` 上下文的一部分, 由 [[[?VC-DATA-INTEGRITY]]] 规范定义,或 `https://www.w3.org/ns/did/v1` 上下文的一部分, 由 [[[?DID-CORE]]] 规范定义。

上下文注入

`@context` 属性用于确保实现处理本规范中的术语时使用相同的语义。 例如,当处理 `authentication` 属性及其值(如 `Multikey` 或 `JsonWebKey`)时,这一点很重要。

当应用程序处理 [=受控标识符文档=] 时,如果文档中未提供 `@context` 属性, 或者文档中使用的术语未被 `@context` 属性中的现有值映射, 实现必须注入或追加一个值为 `https://www.w3.org/ns/cid/v1` 的 `@context` 属性, 或一个或多个具有至少相同声明的上下文,例如去中心化标识符 v1.1 上下文 (`https://www.w3.org/ns/did/v1`)。

  {
    // 此受控标识符文档中缺少 @context 声明
    "id": "https://controller.example/101",
    "verificationMethod": [{
      "id": "https://controller.example/101#key-203947",
      "type": "JsonWebKey",
      "controller": "https://controller.example/101",
      "publicKeyJwk": {
        "kid": "key-203947",
        "kty": "EC",
        "crv": "P-256",
        "alg": "ES256",
        "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
        "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
      }
    }],
    "authentication": ["#key-203947"]
  }
            

不打算使用 JSON-LD 的实现可以选择不在文档的顶层包含 `@context` 声明。 无论是否使用 `@context` 值或 JSON-LD 处理器, [=符合规范的文档=] 中表达的所有属性和值的语义在 [=符合规范的处理器=] 中是相同的。 在两种模式下处理的文档之间的语义差异是实现或规范的错误。

数据类型

本节定义了本规范使用的数据类型。

`multibase` 数据类型

Multibase 编码的字符串用于将二进制数据编码为可打印格式, 例如 ASCII,这在无法直接表示二进制值的环境中非常有用。 本规范使用了这种编码。在支持字符串值数据类型的环境中,例如 RDF [[?RDF-CONCEPTS]], Multibase 编码的内容通过数据类型值 `https://w3id.org/security#multibase` 表示。

`multibase` 数据类型定义如下:

表示此数据类型的 URL
`https://w3id.org/security#multibase`
词汇空间
任何以 [=Multibase头=] 开头且其余字符由相应基数编码字母表中的允许字符组成的字符串。
值空间
所有整数的标准数学概念。
词汇到值的映射
词汇空间的任何元素通过基于词汇字符串中第一个 [=Multibase头=] 关联的基数解码字母表进行基数解码映射到值空间。
规范映射
规范映射由词汇到值的映射组成。

安全注意事项

本节包含了一些使用本规范的人员在将此技术部署到生产环境之前需要考虑的安全注意事项。本文件中描述的技术设计旨在符合许多 IETF 标准使用的威胁模型,并记录在 [[?RFC3552]] 中。本节详细说明了 [[?RFC3552]] 中的一些注意事项,以及本规范独有的其他注意事项。

控制与绑定的证明

将数字世界或物理世界中的实体绑定到标识符、[=受控标识符文档=]或加密材料需要使用本规范中考虑的安全协议。以下部分描述了一些可能的场景,以及其中的实体如何证明对标识符或[=受控标识符文档=]的控制,以用于认证或授权目的。

证明对标识符和/或受控标识符文档的控制

证明对标识符和/或[=受控标识符文档=]的控制在访问远程系统时非常有用。加密数字签名使与[=受控标识符文档=]相关的某些安全协议能够通过加密方式验证。为此,本规范在和[[[#capability-invocation]]]中定义了有用的[=验证关系=]。与[=验证方法=]相关的秘密加密材料可用于生成加密数字签名,作为认证或授权安全协议的一部分。

绑定到物理身份

标识符或[=受控标识符文档=]本身并不固有地携带任何个人数据,并且强烈建议非公共实体不要在[=受控标识符文档=]中发布个人数据。

将标识符绑定到个人或组织的物理身份,并由可信机构(如政府)可验证地声明可能是有用的。本规范为此目的提供了[[[#assertion]]] [=验证关系=]。此功能可以实现私密的交互,并可能在一个或多个司法管辖区内被视为具有法律约束力;建立此类绑定时必须在隐私考虑(参见[[[#privacy-considerations]]]])与功能需求之间进行谨慎平衡。

将标识符绑定到物理世界中的某些事物(例如个人或组织)的过程,例如通过使用与该标识符具有相同主体的可验证凭证,在本规范中有所考虑,并在[[[?VC-DATA-MODEL-2.0]]]中进一步定义。

标识符的歧义性

即使由[=标识符=]引用的[=主体=]证明了控制权,对[=主体=]的解释仍然是上下文相关的,并且可能存在歧义。

例如,一所学校可能会为《计算机科学导论》的教师颁发一份可验证凭证,使用 `https://controller.example/abc` 作为[=主体=]的[=标识符=],声明 "`https://controller.example/abc` 是《计算机科学导论》的教师", 并且 "`https://controller.example/abc` 控制对学校计算机实验室的访问。请找他们申请访问权限"。

在���种用法中,`https://controller.example/abc` 是指某个特定的教师,还是指当前的教师,这一点是模糊的。只有通过进一步的声明,我们才能辨别出差异。但这仍然很棘手。例如,以下声明中的主体仍然是模糊的:

<https://controller.example/abc>
  <https://schema.org/name>
    "Bob Smith" .
  

如果 `https://controller.example/abc` 指的是一个特定的人类个体,那么该声明被视为对该名字所标识的特定人类的证明。然而,如果 `https://controller.example/abc` 被用来指代“当前的”教师,那么只要当前的教师确实有这个名字,这也是有效的。在这种情况下,这种歧义是无关紧要的。

然而,在如下声明中,这种差异变得至关重要。

<https://controller.example/abc>
  <http://law.example/convicted>
    <http://calaw.example/PenalCode647b> .
  

该声明的英文含义可能是:“`https://controller.example/abc` 所指代的人因违反加州刑法第 647b 条而被定罪。”但我们指的是哪一个人?我们是想说学校的计算机科学教师中有一个、一些或全部因违反 `PenalCode647b` 而被定罪?还是说某个特定的教师,也许是名为“Bob Smith”的那位教师,被定罪了?

这种挑战在[=主体=]在颁发可验证凭证的过程中基本上未参与的情况下尤其困难。例如,学校可能使用一个[=标识符=]来指代一位教师,而学生或家长可能使用该[=标识符=]来对教师发表声明,而教师或学校都未参与其中。在这些情况下,很容易想象学校的意图(例如“任何当前的计算机科学课程教师”)的细微差别被忽略,而[=标识符=]被家长和学生误用来指代某个特定的教师,很可能是在学校或教师都不知道的情况下进行的对话中。

在自然语言中,这些歧义通常很容易被忽略或纠正。在数字媒体中,评估上下文以确定预期的指代对象至关重要,特别是当[=标识符=]在不同的上下文中被不同的颁发者使用时,例如在学校的官方网页上由学校使用,但在非官方的社交网络应用中由家长和学生使用。

简而言之,在依赖对任何特定[=标识符=]的[=主体=]的任何特定解释时,必须考虑[=标识符=]创建和使用的上下文。

密钥和签名的过期

在去中心化架构中,可能没有集中式机构来强制执行加密材料或加密数字签名的过期策略。因此,支持软件(如验证库)需要由请求方验证加密材料在使用时是否未过期。请求方可能会在其验证过��中采用自己的过期策略作为输入。例如,一些请求方可能接受过去五分钟内的认证,而另一些具有高精度时间源的请求方可能要求认证的时间戳在最近的500毫秒内。

某些请求方可能有合法需求延长已过期加密材料的使用,例如验证遗留的加密数字签名。在这些场景中,请求方可能会指示其验证软件忽略加密密钥材料的过期,或者确定加密密钥材料在使用时是否已过期。

验证方法的轮换

轮换是一种管理过程,它允许与现有[=验证方法=]相关联的秘密加密材料在添加新的[=验证方法=]到[=受控标识符文档=]后被停用或销毁。从此以后,任何[=控制者=]使用旧的秘密加密材料生成的[=证明=],现在都可以改为使用新的加密材料生成,并可以通过新的[=验证方法=]进行验证。

轮换是保护验证方法免受泄露的有用机制,因为控制者频繁轮换验证方法会降低单个泄露验证方法对攻击者的价值。在轮换后立即执行撤销对于控制者指定用于短期验证的验证方法(例如加密消息和认证)是有用的。

以下是关于使用[=验证方法=]轮换时可能有用的一些考虑:

验证方法的撤销

撤销是一种管理过程,它使与现有[=验证方法=]相关联的秘密加密材料失效,从而不再能够用于创建新的证明。

撤销是应对验证方法泄露的有用机制。在轮换之后立即执行撤销,对于控制者指定用于短期验���的验证方法(例如加密消息和身份验证)非常有用。

与[=验证方法=]相关的秘密被泄露后,攻击者可以根据[=控制者=]在[=受控标识符文档=]中表达的[=验证关系=]使用这些秘密,例如用于身份验证。从[=验证方法=]注册到撤销的时间段内,攻击者的使用可能与合法[=控制者=]的使用无法区分。

以下是关于使用[=验证方法=]撤销时可能有用的一些考虑:

撤销语义

尽管验证者可能选择不接受来自已撤销验证方法的[=证明=]或签名,但确定某次验证是否使用了已撤销的[=验证方法=]比看起来更复杂。一些审计系统提供了回溯到某一时间点或某一特定版本的[=受控标识符文档=]状态的能力。当此功能与可靠的方式结合使用以确定进行加密可验证声明时的时间或标识符版本,则撤销不会撤销该声明。这可以成为使用数字签名进行具有约束力的承诺的基础,例如签署抵押贷款。

如果满足这些条件,撤销不是追溯性的;它仅使该方法的未来使用无效。

然而,为了使这种语义安全,第二个条件——能够知道在进行声明时[=受控标识符文档=]的状态——被认为是必要的。如果没有这种保证,有人可能会发现一个已撤销的密钥并使用它创建带有模拟过去日期的加密可验证声明。

一些审计系统仅允许检索标识符的当前状态。当这种情况发生时,或者当无法可靠地确定加密可验证声明时标识符的状态时,唯一安全的做法是禁止考虑与时间相关的状态,除了当前时刻。采用这种方法的标识符生态系统本质上将加密可验证声明作为临时令牌,可以随时由[=控制者=]使其失效。

选择一种多格式

多格式使数据具有自描述性;如果数据被认为是多格式,可以通过读取数据开头表达的几个紧凑的头字节来确定其确切类型。 多基值编码 (Multibase)多哈希 (Multihash)多密钥 (Multikey) 是本规范定义的多格式类型。

多格式规范的存在是因为应用程序开发者会根据不同的用例及其需求,适当地选择不同的基���编码函数、加密哈希函数和加密密钥格式等。世界上没有任何单一的基值编码函数、加密哈希函数或加密密钥格式能够满足所有需求集。多格式提供了一种替代方法,可以在自描述的数据和文档中对任何基值编码、加密哈希或加密密钥格式进行编码和/或检测。

为了提高互操作性,建议规范作者尽量减少所使用的多格式数量——理想情况下,仅选择一种——用于任何特定的��用程序或生态系统。

受控标识符文档中的加密数据

由于密码学和计算能力的进步,加密算法可能会失效。建议实现者假设放置在[=受控标识符文档=]中的任何加密数据最终可能会以明文形式提供给与加密数据可访问的相同受众。这一点在[=受控标识符文档=]是公开的情况下尤为重要。

对[=受控标识符文档=]的全部或部分进行加密并不是一种长期保护数据的适当手段。同样,将加密数据放置在[=受控标识符文档=]中也不是保护个人数据的适��手段。

鉴于上述警告,如果在[=受控标识符文档=]中包含加密数据,建议实现者不要关联任何可关联的信息,这些信息可能被用来推断加密数据与相关方之间的关系。可关联信息的示例包括接收方的公钥、已知由接收方控制的数字资产标识符或接收方的可读描述。

内容完整性保护

[=受控标识符文档=]中包含的指向外部机器可读内容(如图像、网页或模式)的链接容易受到篡改的影响。强烈建议使用机制保护相关资源的完整性,例如[[[?VC-DATA-MODEL-2.0]]]规范中描述的那些。如果无法保护外部链接的完整性,并且[=受控标识符文档=]的完整性依赖于外部链接,则应避免使用外部链接。

一个可能影响[=受控标识符文档=]自身完整性的外部链接示例是 JSON-LD 上下文 [[JSON-LD11]](如果存在)。为了防止被破坏,使用 JSON-LD 的[=受控标识符文档=]消费者被建议缓存 JSON-LD 上下文的本地静态副本和/或根据已知与安全版本的外部 JSON-LD 上下文相关联的加密哈希验证外部上下文的完整性。

控制者的完整性保护

如[[[#controllers]]]部分所述,本规范包括一种机制,通过使用 `controller` 属性,将[=受控标识符文档=]的更改控制权委托给在外部[=受控标识符文档=]中描述的实体。

委托更改控制解决了许多用例,包括某个实体的照护责任由其他实体或多个实体负责的情况,以及某些实体希望其他实体提供账户恢复服务的情况等。在这些场景中,允许监护人管理其自己的密钥材料轮换可能是有益的。对于委托方来说,将远程[=受控标识符文档=]的加密哈希关联起来以“固定”远程文档到已知的良好值也可能是有益的。

虽然本文档未指定特定的��密保护 URL 的机制,但[[[VC-DATA-MODEL-2.0]]]中的 `relatedResource` 属性和[[[VC-DATA-INTEGRITY]]]中的 `digestMultibase` 属性可以被用于提供此类保护的机制。

保证级别

在合规性要求方面,特别是在金融和公共部门等受监管领域,通常需要提供关于身份验证事件安全上下文的额外信息。这些信息通常被称为保证级别(LOA)。示例包括加密材料的保护、身份[=证明=]过程以及认证器的形式因素。

支付服务指令(PSD 2) eIDAS向安全上下文引入了此类要求。保证级别框架由法规和标准分类和定义,如 eIDASNIST 800-63-3ISO/IEC 29115:2013,包括它们对安全上下文的要求,并提供如何实现这些要求的建议。这可能包括强用户身份验证,其中FIDO2/WebAuthn可以满足要求。

某些受监管场景要求实施特定的保证级别。由于用于执行断言和[=认证=]的[=验证关系=]可能在这些情况下使用,因此可能需要表达并向验证者提供有关所应用���全上下文的信息。是否以及如何在[=受控标识符文档=]数据模型中编码此信息超出了本规范的范围。感兴趣的读者可能会注意到:1)这些信息可以使用可验证凭证[[?VC-DATA-MODEL-2.0]]传输,以及2)[=受控标识符文档=]数据模型可以扩展以包含这些信息。

用于认证和授权的服务端点

如果[=受控标识符文档=]发布了用于[=主体=]认证或授权的[=服务=](参见[[[#services]]]节),那么[=服务=]提供者、[=主体=]和/或请求方有责任遵守该[=服务=]端点支持的认证和/或授权协议的要求。

隐私注意事项

由于[=受控标识符文档=]旨在由[=控制者=]直接管理,因此将隐私设计原则[[PRIVACY-BY-DESIGN]]应用于[=受控标识符文档=]的所有方面至关重要。这些原则的全部七项已贯穿本规范的开发过程。本规范的设计不假设存在注册机构、托管公司或其他中介服务提供商来推荐或应用额外的隐私保护措施。本规范中的隐私是预防性的,而非补救性的,并且是嵌入式的默认设置。以下部分涵盖了实施者在构建利用[=受控标识符文档=]的系统时可能发现有用的隐私注意事项。

保持个人数据的私密性

如果[=受控标识符文档=]是关于特定个人且面向公众的,那么确保[=受控标识符文档=]中不包含任何个人生物特征或传记数据是至关重要的。虽然个人数据可能包括伪匿名信息,例如公开的加密密钥或IP地址,但发布此类信息不会像在[=受控标识符文档=]中发布个人的全名、个人照片或社交媒体账户那样立即带来隐私风险。更好的替代方案是通过其他方式传输此类个人数据,例如可验证凭证[[?VC-DATA-MODEL-2.0]]或通过私密和安全的通信渠道发送的其他数据格式。

与同源策略的关系

同源策略是一种安全和隐私概念,默认情况下将信息限制在同一Web域中。有一些机制,例如[[[?WEBAUTHN]]],将此策略扩展到加密密钥。当加密密钥绑定到特定域时,有时称为配对标识符

同源策略可以为多种���例被覆盖,例如跨域资源共享(CORS)。本规范允许验证方法和服务端点的跨域资源共享,这意味着可关联的标识符可能会在不同来源之间共享。虽然资源共享可能带来积极的安全结果(减少加密密钥注册负担),但也可能导致负面的隐私结果(跟踪)。使用本规范的用户被警告,每种方法都有权衡,应根据个人或组织的需求使用最大化安全性和隐私性的机制。在所有用例中使用[=受控标识符文档=]并不总是有利的,当同源绑定的加密密钥足够时可能更为合适。

标识符关联风险

标识符可能被用于不必要的关联。[=控制者=]可以通过使用[=配对标识符=]来减轻这种隐私风险,这些标识符对每个关系或交互域都是唯一的;实际上,每个标识符都充当了一个化名。只有在明确需要跨上下文关联时,[=配对标识符=]才需要与多个方共享。如果[=配对标识符=]是默认设置,那么只有在[=控制者=]和/或[=主体=]明确希望在交互域之间进行公开标识和关联时,才需要公开发布标识符或与多个方共享。

受控标识符文档的关联风险

如果相应[=受控标识符文档=]中的数据可以被关联,[=配对标识符=]的反关联保护将很容易被破坏。例如,在多个[=受控标识符文档=]中使用相同的[=验证方法=]可能提供与使用相同标识符一样多的关联信息。因此,[=配对标识符=]的[=受控标识符文档=]也需要使用配对唯一的信息,例如确保[=验证方法=]对配对关系是唯一的。

主体分类

在[=受控标识符文档=]中添加可以明确或通过推断指示[=主体=]的类型或性质的属性是危险的,特别是当[=主体=]是一个人时。

此类属性不仅可能导致[=受控标识符文档=]中存在个人数据(参见[[[#keep-personal-data-private]]])或可关联数据(参见 和[[[#controlled-identifier-document-correlation-risks]]]),还可能被用于以某种方式对特定标识符进行分组,从而将其包括或排除在某些操作或功能之外。

在[=受控标识符文档=]中包含类型信息,即使是针对非人类实体(如物联网设备)的[=主体=],��可能导致个人隐私危害。围绕[=控制者=]聚合此类信息可能形成一���数字指纹,最好避免这种情况。

为了最大限度地降低这些风险,[=受控标识符文档=]中的所有属性都应用于表达[=验证方法=]和验证关系,以便使用标识符。

服务隐私

[=控制者=]能够在[=受控标识符文档=]中选择性地表达至少一个[=服务=],这增加了他们的控制权和自主权。[=受控标识符文档=]中的每个额外端点都会增加隐私风险,这可能是由于端点描述之间的关联,或者因为[=服务=]未受授权机制保护,或者两者兼而有之。

[=受控标识符文档=]通常是公开的,并且由于它们是标准化的,将被高效地存储和索引。如果[=受控标识符文档=]发布到不可变的可验证数据注册表中,这种风险会增加。通过URL引用的[=受控标识符文档=]历史记录的访问使得通过标准化的使用进行流量分析变得更加高效。

在一个[=受控标识符文档=]中包含多个[=服务=]所导致的额外隐私风险的程度可能难以估计。隐私危害通常是意外后果。URL可以引用文档、[=服务=]、模式以及可能与个人、家庭、俱乐部和雇主相关的其他内容——它们的[=服务=]之间的关联可能成为一种强大的监控和推断工具。这种潜在危害的一个例子是,当多个常见的国家级顶级域名(如`https://example.co.uk`)被使用时,可能以更高的概率推断出[=主体=]的大致位置。

可访问性注意事项

以下部分描述了开发人员在实现本规范时应考虑的可访问性注意事项,以确保其软件能够被具有不同认知、运动和视觉需求的人使用。通常情况下,本规范由系统软件使用,并不会直接向个人暴露需要考虑可访问性的信息。然而,在某些情况下,个人可能会间接接触到由本规范表达的信息,因此以下指导适用于这些情况。

呈现时间值

本规范支持表达与[=证明=]有效期相关的日期和时间。如果处理[=证明=]时发现其超出了允许的时间范围,这些信息可能会间接暴露给个人。在向个人展示这些日期和时间时,建议实现者考虑 文化规范和区域设置,以便在显示软件中表示日期和时间。除了这些考虑之外,以减轻接收信息的个人的认知负担的方式呈现时间值是一种建议的最佳实践。

例如,在传达某组数字签名信息的过期时间时,建议实现者使用更易于理解的语言,而不是优化准确性的语言。将过期时间表示为“此票据已于三天前过期。”比“此票据于 2023 年 7 月 25 日下午 3:43 过期。”更为优选。前者提供了一个相对时间,更容易理解,而后者需要个人在脑海中进行计算,并假设他们能够进行这样的计算。

IANA 注意事项

本节将提交给互联网工程指导组(IESG)进行审查、批准,并在 IANA 注册。

application/cid

本规范注册了 `application/cid` 媒体类型,专门用于标识符合 [=受控标识符文档=] 格式的文档。

类型名称: application
子类型名称: cid
必需参数:
片段标识符注意事项: 如 [[[CID]]] 规范中第 [[[#fragment-resolution]]] 节所定义。
编码注意事项: 使用 `application/cid` 媒体类型的资源必须符合 `application/json` 媒体类型的所有要求,因此需遵守 [[[RFC7159]]] 第 11 节中规定的编码注意事项。
安全注意事项: 如 [[[CID]]] 规范中第 [[[#security-considerations]]] 节所定义。
联系人: W3C 可验证凭证工作组 public-vc-wg@w3.org

示例

本节包含了规范中引入的概念的更详细示例。

Multikey 示例

本节包含了各种 Multikey 示例,这些示例可能对开发人员寻找测试值有用。

{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://multikey.example/issuer/123#key-0",
  "type": "Multikey",
  "controller": "https://multikey.example/issuer/123",
  "publicKeyMultibase": "zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
}
          
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://multikey.example/issuer/123#key-0",
  "type": "Multikey",
  "controller": "https://multikey.example/issuer/123",
  "publicKeyMultibase": "z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
}
          
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://multikey.example/issuer/123#key-0",
  "type": "Multikey",
  "controller": "https://multikey.example/issuer/123",
  "publicKeyMultibase": "z6Mkf5rGMoatrSj1f4CyvuHBeXJELe9RPdzo2PKGNCKVtZxP"
}
          
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://multikey.example/issuer/123#key-0",
  "type": "Multikey",
  "controller": "https://multikey.example/issuer/123",
  "publicKeyMultibase": "zUC7EK3ZakmukHhuncwkbySmomv3FmrkmS36E4Ks5rsb6VQSRpoCrx6Hb8e2Nk6UvJFSdyw9NK1scFXJp21gNNYFjVWNgaqyGnkyhtagagCpQb5B7tagJu3HDbjQ8h5ypoHjwBb"
}
          
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example/123",
  "verificationMethod": [{
    "id": "https://multikey.example/issuer/123#key-1",
    "type": "Multikey",
    "controller": "https://multikey.example/issuer/123",
    "publicKeyMultibase": "zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
  }, {
    "id": "https://multikey.example/issuer/123#key-2",
    "type": "Multikey",
    "controller": "https://multikey.example/issuer/123",
    "publicKeyMultibase": "z6Mkf5rGMoatrSj1f4CyvuHBeXJELe9RPdzo2PKGNCKVtZxP"
  }, {
    "id": "https://multikey.example/issuer/123#key-3",
    "type": "Multikey",
    "controller": "https://multikey.example/issuer/123",
    "publicKeyMultibase": "zUC7EK3ZakmukHhuncwkbySmomv3FmrkmS36E4Ks5rsb6VQSRpoCrx6Hb8e2Nk6UvJFSdyw9NK1scFXJp21gNNYFjVWNgaqyGnkyhtagagCpQb5B7tagJu3HDbjQ8h5ypoHjwBb"
  }],
  "authentication": [
    "https://controller.example/123#key-1"
  ],
  "assertionMethod": [
    "https://controller.example/123#key-2"
    "https://controller.example/123#key-3"
  ],
  "capabilityDelegation": [
    "https://controller.example/123#key-2"
  ],
  "capabilityInvocation": [
    "https://controller.example/123#key-2"
  ]
}
          
{
  "id": "https://multikey.example/issuer/123#key-0",
  "type": "Multikey",
  "controller": "https://multikey.example/issuer/123",
  "publicKeyMultibase": "zEPJc1vCfbG2aoZn8f3U8ggYRL4ZFfF63ZA3qFSk81WJxnCQr"
}
          

JsonWebKey 示例

本节包含了各种 JsonWebKey 示例,这些示例可能对开发人员寻找测试值有用。

{
  "id": "https://jsonwebkey.example/issuer/123#key-0",
  "type": "JsonWebKey",
  "controller": "https://jsonwebkey.example/issuer/123",
  "publicKeyJwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "Ums5WVgwRkRTVVFnU3k5c2xvZllMbEcwM3NPRW91ZzN",
    "y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4"
  }
}
          
{
  "id": "https://jsonwebkey.example/issuer/123#key-0",
  "type": "JsonWebKey",
  "controller": "https://jsonwebkey.example/issuer/123",
  "publicKeyJwk": {
    "kty": "EC",
    "crv": "P-384",
    "x": "VUZKSlUwMGdpSXplekRwODhzX2N4U1BYdHVYWUZsaXVDR25kZ1U0UXA4bDkxeHpE",
    "y": "jq4QoAHKiIzezDp88s_cxSPXtuXYFliuCGndgU4Qp8l91xzD1spCmFIzQgVjqvcP"
  }
}
          
{
  "id": "https://jsonwebkey.example/issuer/123#key-0",
  "type": "JsonWebKey",
  "controller": "https://jsonwebkey.example/issuer/123",
  "publicKeyJwk": {
    "kty": "OKP",
    "crv": "Ed25519",
    "x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ"
  }
}
          
{
  "id": "https://jsonwebkey.example/issuer/123#key-0",
  "type": "JsonWebKey",
  "controller": "https://jsonwebkey.example/issuer/123",
  "publicKeyJwk": {
    "kty": "EC",
    "crv": "BLS12381G2",
    "x": "Ajs8lstTgoTgXMF6QXdyh3m8k2ixxURGYLMaYylVK_x0F8HhE8zk0YWiGV3CHwpQEa2sH4PBZLaYCn8se-1clmCORDsKxbbw3Js_Alu4OmkV9gmbJsy1YF2rt7Vxzs6S",
    "y": "BVkkrVEib-P_FMPHNtqxJymP3pV-H8fCdvPkoWInpFfM9tViyqD8JAmwDf64zU2hBV_vvCQ632ScAooEExXuz1IeQH9D2o-uY_dAjZ37YHuRMEyzh8Tq-90JHQvicOqx"
  }
}
          
{
  "@context": "https://www.w3.org/ns/cid/v1",
  "id": "https://controller.example/123",
  "verificationMethod": [{
    "id": "https://jsonwebkey.example/issuer/123#key-1",
    "type": "JsonWebKey",
    "controller": "https://jsonwebkey.example/issuer/123",
    "publicKeyJwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
      "y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
    }
  }, {
    "id": "https://jsonwebkey.example/issuer/123#key-2",
    "type": "JsonWebKey",
    "controller": "https://jsonwebkey.example/issuer/123",
    "publicKeyJwk": {
      "kty": "EC",
      "crv": "P-521",
      "x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
      "y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
    }
  }, {
    "id": "https://jsonwebkey.example/issuer/123#key-3",
    "type": "JsonWebKey",
    "controller": "https://jsonwebkey.example/issuer/123",
    "publicKeyJwk": {
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
    }
  }],
  "authentication": [
    "https://controller.example/123#key-1"
  ],
  "assertionMethod": [
    "https://controller.example/123#key-2"
    "https://controller.example/123#key-3"
  ],
  "capabilityDelegation": [
    "https://controller.example/123#key-2"
  ],
  "capabilityInvocation": [
    "https://controller.example/123#key-2"
  ]
}
          

修订历史

本节包含了此规范随时间进行的实质性更改。

本规范的创建是为了将[[[?DID-CORE]]]规范泛化,以支持非去中心化标识符和系统,例如 HTTPS URL。因此,[[[?DID-CORE]]]规范中的大部分内容被复制到本文档中,并通过主要的编辑性更改进行了泛化。自[[[?DID-CORE]]]规范以来的更改如下:

致谢

本规范的作者感谢对 W3C 去中心化标识符 (DIDs) v1.0 规范做出贡献的人员,该规范是本工作的基础。

工作组对促成本规范创建的工作表示由衷的感谢,并向那些在技术和规范上深刻影响我们工作的个人致以诚挚的敬意。特别是,这包括 Phil Zimmerman、Jon Callas、Lutz Donnerhacke、Hal Finney、David Shaw 和 Rodney Thayer 在 1990 年代和 2000 年代对 Pretty Good Privacy (PGP) 的工作。

在 2010 年代中期,未来成为去中心化标识符的初步实现 Jeremie Miller 的 Telehash 项目和由 Dave Longley 和 Manu Sporny 领导的 W3C Web Payments 社区组的工作中被构建。一年后,XDI.org 注册工作组 开始探索用于替代其现有标识符注册表的去中心化技术。最早的 书面 文档 探索去中心化标识符概念的工作可以追溯到由 Christopher Allen 召集的前几次 Rebooting the Web of Trust 研讨会。这项工作促成了 Christopher Allen、Drummond Reed、Les Chasen、Manu Sporny 和 Anil John 之间的关键合作。Anil 看到了这项技术的潜力,并分配了最初的一组政府资金来探索这一领域。如果没有 Anil John 的支持和多年来��指导,去中心化标识符可能不会发展到今天的地步。在 Rebooting the Web of Trust 研讨会的进一步完善工作促成了 第一个实现者文档,由 Drummond Reed、Les Chasen、Christopher Allen 和 Ryan Grant 编辑。贡献者包括 Manu Sporny、Dave Longley、Jason Law、Daniel Hardman、Markus Sabadello、Christian Lundkvist 和 Jonathan Endersby。这项初步工作随后被合并到 W3C Credentials 社区组中,进一步孵化,然后转移到 W3C 去中心化标识符工作组进行全球标准化。这项工作随后被用作本规范的基础,这是一项更通用且去中心化程度较低的规范。

本规范的部分工作由美国国土安全部 (US DHS) 科学与技术局通过合同 HSHQDC-16-R00012-H-SB2016-1-002 和 HSHQDC-17-C-00019,以及 US DHS 硅谷创新计划通过合同 70RSAT20T00000003、70RSAT20T00000010/P00001、70RSAT20T00000029、70RSAT20T00000030、70RSAT20T00000033、70RSAT20T00000045、70RSAT21T00000016/P00001、70RSAT23T00000005、70RSAT23C00000030 和 70RSAT23R00000006 资助完成。本规范的内容不一定反映美国政府的立场或政策,不应推断出任何官方认可。

本规范的部分工作还由欧盟 StandICT.eu 计划通过子资助合同 CALL05/19 资助完成。本规范的内容不一定反映欧盟的立场或政策,不应推断出任何官方认可。

我们还要感谢 base-x 软件库的贡献者和 Bitcoin Core 开发者,他们编写了原始代码,并以 MIT 许可证共享,见第 [[[#base-encode]]] 和第 [[[#base-decode]]] 节。

本规范的工作还得到了 Rebooting the Web of Trust 社区的支持,该社区由 Christopher Allen、Shannon Appelcline、Kiara Robles、Brian Weller、Betty Dhamers、Kaliya Young、Kim Hamilton Duffy、Manu Sporny、Drummond Reed、Joe Andrieu、Heather Vescent、Samantha Chase、Andrew Hughes、Erica Connell、Shigeya Suzuki 和 Zaïda Rivai 组织。本规范的开发还得到了 W3C Credentials 社区组 的支持,该组由 Kim Hamilton Duffy、Joe Andrieu、Christopher Allen、Heather Vescent 和 Wayne Chang 主持。互联网身份研讨会的参与者也通过许多旨在辩论、改进和教育参与者关于本规范的工作会议支持了这项工作,这些会议由 Phil Windley、Kaliya Young、Doc Searls 和 Heidi Nobantu Saul 组织。

工作组感谢以下个人对本规范的贡献(按字母顺序排列,Github 账号以 `@` 开头,按姓氏排序):Denis Ah-Kang、Nacho Alamillo、Christopher Allen、Joe Andrieu、Antonio、Phil Archer、George Aristy、Baha、Juan Benet、BigBlueHat、Dan Bolser、Chris Boscolo、Pelle Braendgaard、Daniel Buchner、Daniel Burnett、Juan Caballero、@cabo、Tim Cappalli、Melvin Carvalho、David Chadwick、Wayne Chang、Sam Curren、Hai Dang、Tim Daubenschütz、Oskar van Deventer、Kim Hamilton Duffy、Arnaud Durand、Ken Ebert、Veikko Eeva、@ewagner70、Carson Farmer、Nikos Fotiou、Gabe、Gayan、@gimly-jack、@gjgd、Ryan Grant、Peter Grassberger、Adrian Gropper、Amy Guy、Daniel Hardman、Kyle Den Hartog、Philippe Le Hegaret、Ivan Herman、Michael Herman、Alen Horvat、Dave Huseby、Marcel Jackisch、Mike Jones、Andrew Jones、Tom Jones、jonnycrunch、Gregg Kellogg、Michael Klein、@kdenhartog-sybil1、Paul Knowles、@ktobich、David I. Lehn、Charles E. Lehner、Michael Lodder、@mooreT1881、Dave Longley、Tobias Looker、Wolf McNally、Robert Mitwicki、Mircea Nistor、Grant Noble、Mark Nottingham、@oare、Darrell O'Donnell、Vinod Panicker、Dirk Porsche、Praveen、Mike Prorock、@pukkamustard、Drummond Reed、Julian Reschke、Yancy Ribbens、Justin Richer、Rieks、@rknobloch、Mikeal Rogers、Evstifeev Roman、Troy Ronda、Leonard Rosenthol、Michael Ruminer、Markus Sabadello、Cihan Saglam、Samu、Rob Sanderson、Wendy Seltzer、Mehran Shakeri、Jaehoon (Ace) Shim、Samuel Smith、James M Snell、SondreB、Manu Sporny、@ssstolk、Orie Steele、Shigeya Suzuki、Sammotic Switchyarn、@tahpot、Oliver Terbu、Ted Thibodeau Jr.、Joel Thorstensson、Tralcan、Henry Tsai、Rod Vagg、Mike Varley、Kaliya "Identity Woman" Young、Eric Welton、Fuqiao Xue、@Yue、Dmitri Zagidulin、@zhanb 和 Brent Zundel。