Skip to content

About

Open-source reference server for DID document hosting, Handle resolution, and ANP HTTP signature verification

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

9 Commits

Folders and files

Repository files navigation

Open DID Server

Open DID Server 是一个开源 DID 文档服务。它托管本实例签发的 did:wba(E1)和 did:web 文档,按标准 DID 路径把客户端上传的原文分发出去,并提供 Handle 到 DID 的查询。调用方用自己的认证私钥对 HTTP 请求签名;服务只接受一种有限形态的 ANP HTTP Message Signature。

它解决的是「把一份 DID 文档发布到这个域名,并让后续请求证明持有文档里的认证键」。首次发布要同时满足两件事:操作员发给你的发布凭据 ANP-Publication-Token,以及候选文档 authentication 键的持钥签名。发布凭据不能代替 DID 签名。文档一旦发布,更新只认已经发布的认证键,不认更新正文里新加的键。

首版明确不做这些事:

  • 不是完整的 RFC 9421。只接受标签 sig1、参数顺序 created;expires;nonce;keyid,以及接口文档里固定的签名组件。
  • 不是账户系统,也不做 OAuth、可验证凭证、即时消息、端到端加密、多设备或 DID 轮换恢复。
  • 不认证其他域名上已经存在的 DID。WBA 的精确 Handle 校验和 Web 的 Provider 域名校验是两套规则。

依赖的 SDK 是 PyPI 上的 anp==1.0.5(https://pypi.org/simple,wheel anp-1.0.5-py3-none-any.whl)。项目不使用本机源码路径。

接口、错误码和签名覆盖见 docs/api.md。反向代理、备份和高水位限制见 docs/deployment.md。设计依据是 docs/plan.md。

安装并在本机启动

需要 Python 3.11+ 和 uv。命令在仓库根目录执行。下面的 DID 使用域名 example.test,HTTP 只监听 127.0.0.1。监听端口不写入 DID。

uv sync
export OPEN_DID_DATA_DIR=./data
export OPEN_DID_HOST=127.0.0.1
export OPEN_DID_PORT=8765
export PUBLIC_DID_DOMAIN=example.test
export PUBLIC_DID_PORT=443
export HANDLE_PROVIDER_DOMAIN=example.test
export REQUEST_BASE_URL=http://127.0.0.1:8765
export LOCAL_DEMO_MODE=1
uv run open-did-server init
uv run open-did-server create-grant \
  --owner demo-owner \
  --wba-path identities/wba/alice \
  --web-path identities/web/bob \
  --handle alice.example.test \
  --handle bob.example.test
uv run open-did-server serve

create-grant 只打印一次 token=。这个令牌、私钥、生成的身份和 ./data/ 里的数据库都不要提交到 Git。./data/ 已被忽略。不要把 LOCAL_DEMO_MODE=1 的测试凭据用于正式域名。

服务起来之后,可以用下面两个独立进程示例。它们都通过真实 HTTP 访问 --base-url,不是进程内调用。

示例一:走完一次发布

examples/client/run.py 是本地演示客户端。它按这个顺序做三件事:

  1. 创建 DID。 在本机生成 WBA E1 和 Web 两份文档及认证私钥。私钥只写入 --out。这一步还没有访问服务,输出里 published 为 false。
  2. 发布 DID。 用发布凭据和这份文档自己的认证私钥签名,把文档上传到服务。服务返回 201,published 变为 true,随后按标准路径读回的 id 必须等于刚创建的 DID。
  3. 用 DID 发起请求,由服务端做身份认证。 客户端用同一把认证私钥签名 whoami 和 echo。服务端取出已发布文档里的公钥验签,确认键在 authentication 里,并通过时间窗口和重放检查。通过后响应里的 did 就是这个身份,auth_scheme 为 http_signatures。发布凭据不参与这两次请求。

后面还会绑定 Handle、更新 WBA 文档。更新时 HTTP 签名不能代替文档自己的 proof。

本地解析使用 SDK 的 base_url_override,输出里的 resolution 是 development-demo。这一步只说明「回环上的文档能被指到本地服务」,不是正式 HTTPS 验收。最后一行 production_https 在本地运行时是 not-run。

另开一个终端:

uv run python examples/client/run.py \
  --base-url http://127.0.0.1:8765 \
  --token '刚才打印的 token' \
  --out ./data/demo

每次运行都会新建密钥,所以 WBA 的指纹每次不同。下面是一次真实运行里创建、发布和身份认证的结果(令牌和私钥没有出现在输出中):

{"step": "wba_create", "did": "did:wba:example.test:identities:wba:alice:e1_yVcTuxpNpgAY3ufK98aR3lac0SrlwP56lTBIEy5E_2E", "authentication": "did:wba:example.test:identities:wba:alice:e1_yVcTuxpNpgAY3ufK98aR3lac0SrlwP56lTBIEy5E_2E#key-1", "published": false}
{"step": "web_create", "did": "did:web:example.test:identities:web:bob", "authentication": "did:web:example.test:identities:web:bob#key-1", "published": false}
{"step": "wba_publish", "did": "did:wba:example.test:identities:wba:alice:e1_yVcTuxpNpgAY3ufK98aR3lac0SrlwP56lTBIEy5E_2E", "published": true}
{"step": "web_publish", "did": "did:web:example.test:identities:web:bob", "published": true}
{"step": "wba_whoami", "did": "did:wba:example.test:identities:wba:alice:e1_yVcTuxpNpgAY3ufK98aR3lac0SrlwP56lTBIEy5E_2E", "auth_scheme": "http_signatures", "mode": "local-demo"}
{"step": "wba_echo", "did": "did:wba:example.test:identities:wba:alice:e1_yVcTuxpNpgAY3ufK98aR3lac0SrlwP56lTBIEy5E_2E", "auth_scheme": "http_signatures", "mode": "local-demo", "body": {"hello": "wba"}}
{"step": "web_whoami", "did": "did:web:example.test:identities:web:bob", "auth_scheme": "http_signatures", "mode": "local-demo"}
{"step": "web_echo", "did": "did:web:example.test:identities:web:bob", "auth_scheme": "http_signatures", "mode": "local-demo", "body": {"hello": "web"}}

wba_create 和 wba_publish 的 did 相同,说明发布的就是刚创建的那一份。wba_whoami 的 did 再与它相同,并且 auth_scheme 为 http_signatures,说明服务端把这次请求认证成了该 DID。wba_echo 在同一次认证之后把签名正文原样返回。mode 为 local-demo 表示这次服务开了本地演示开关。

示例二:命名系统测试

examples/system/run.py 把创建、发布和身份认证拆成命名用例,并多做一次必须失败的重放。发布和绑定复用示例一的请求,没有第二套实现。HTTPS 时证书校验保持解释器默认值,解析文档时不使用 base_url_override。

先按上一节启动服务并创建覆盖 identities/wba/alice、identities/web/bob、alice.example.test、bob.example.test 的 grant。然后:

uv run python examples/system/run.py \
  --base-url http://127.0.0.1:8765 \
  --token '刚才打印的 token' \
  --domain example.test \
  --out ./data/system-demo

每个用例打印一行 JSON。result 为 pass 表示该成功用例通过;rejected 表示服务按预期拒绝了重放。进程退出码为 0 才表示整组通过。

用例 在检查什么
wba-create / web-create 本地已经生成 DID 和 authentication 方法,尚未发布
wba-publish / web-publish 发布返回 201,响应里的 DID 等于刚创建的那一份
wba-read / web-read 标准 did.json 返回 200,且 id 等于上传的 DID
wba-handle / web-handle GET /.well-known/handle/{local} 返回 200、status=active,且 DID 一致
wba-whoami / web-whoami 用该 DID 签名的 whoami 被服务端认证,返回同一个 did 和 auth_scheme=http_signatures
wba-echo / web-echo 用该 DID 签名的带正文请求被认证,服务端原样返回正文
whoami-replay 同一条 whoami 签名再发一次,返回 401 invalid_nonce

下面是一次回环真实运行的完整输出。WBA 指纹每次新建密钥都会变;Web 的 id、各用例的 result 和 HTTP 状态是这次运行的结果:

{"case": "wba-create", "result": "pass", "id": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "authentication": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4#key-1", "published": false}
{"case": "wba-publish", "result": "pass", "http": 201, "id": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "published": true}
{"case": "wba-read", "result": "pass", "http": 200, "id": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "matched": true}
{"case": "wba-handle", "result": "pass", "http": 200, "did": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "status": "active", "matched": true}
{"case": "wba-whoami", "result": "pass", "http": 200, "did": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "auth_scheme": "http_signatures", "authenticated": true}
{"case": "wba-echo", "result": "pass", "http": 200, "did": "did:wba:example.test:identities:wba:alice:e1_uGWK4THZWstV4AUJOgWLOZfMUpWCgd00bKwqp_LdlA4", "auth_scheme": "http_signatures", "body": {"hello": "wba"}}
{"case": "web-create", "result": "pass", "id": "did:web:example.test:identities:web:bob", "authentication": "did:web:example.test:identities:web:bob#key-1", "published": false}
{"case": "web-publish", "result": "pass", "http": 201, "id": "did:web:example.test:identities:web:bob", "published": true}
{"case": "web-read", "result": "pass", "http": 200, "id": "did:web:example.test:identities:web:bob", "matched": true}
{"case": "web-handle", "result": "pass", "http": 200, "did": "did:web:example.test:identities:web:bob", "status": "active", "matched": true}
{"case": "web-whoami", "result": "pass", "http": 200, "did": "did:web:example.test:identities:web:bob", "auth_scheme": "http_signatures", "authenticated": true}
{"case": "web-echo", "result": "pass", "http": 200, "did": "did:web:example.test:identities:web:bob", "auth_scheme": "http_signatures", "body": {"hello": "web"}}
{"case": "whoami-replay", "result": "rejected", "http": 401, "error": "invalid_nonce"}

在自己的 HTTPS 域名上跑系统测试

本地回环只证明服务进程和示例客户端说的是同一种 HTTP。正式形态是:TLS 在反向代理上终止,Open DID Server 仍只听回环,客户端签名的 URL 是 https://你的域名/...。

服务进程使用:

export LOCAL_DEMO_MODE=0
export PUBLIC_DID_DOMAIN=你的域名
export PUBLIC_DID_PORT=443
export HANDLE_PROVIDER_DOMAIN=你的域名
export REQUEST_BASE_URL=https://你的域名
export TRUSTED_PROXY_CIDRS=127.0.0.1/32

deploy/ 里有 Caddy、nginx 和 systemd 示例。受信代理只转发一份 X-Forwarded-Proto: https 和 X-Forwarded-Host。生产模式拒绝 DID_RESOLUTION_BASE_URL_OVERRIDE。

grant 的路径和 Handle 必须落在这个域名上。然后用同一套系统测试,证书校验保持默认开启:

uv run python examples/system/run.py \
  --base-url https://你的域名 \
  --token '刚才打印的 token' \
  --domain 你的域名 \
  --out ./data/system-demo

不要为了让这一步通过而关闭证书校验,也不要用 base_url_override 代替它。示例进程没有提供关闭校验的开关。

开发测试

uv run pytest

覆盖文档校验、签名格式、真实 HTTP 发布与 Handle、独立构造的签名基串、并发更新和重放、独立客户端进程、上面的系统测试示例,以及有效窗口内重启后的重放拒绝。

License

Apache License 2.0。

About

Open-source reference server for DID document hosting, Handle resolution, and ANP HTTP signature verification

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages