可验证凭证要解决的问题
如今的凭证验证都需要验证者回调签发者:
- 学位证书 —— 雇主打电话给大学:“Alice 真的毕业了吗?”
- 在职证明 —— 房东打电话给公司:“Bob 真的在那里工作吗?”
- 智能体授权 —— 合作伙伴调用你的系统:“Agent-42 是否被允许执行此操作?”
存在的问题:
- 隐私泄露 —— 签发者会得知是谁在验证、何时验证
- 依赖性 —— 若签发者的系统宕机,验证者就无法完成验证
- 延迟 —— 实时验证需要发起 API 调用
- 吊销检查 —— 每次都必须向签发者核实状态
可验证凭证(VC) 解决了这个问题:一种经过密码学签名的声明,任何人都可以验证,而无需联系签发者。
什么是可验证凭证?
VC 是由一方(签发者)针对另一方(持有者)签发的、经过数字签名的声明。
示例:大学向 Alice 签发学位证书
{
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "UniversityDegreeCredential"],
"issuer": "did:webvh:stanford.edu",
"issuanceDate": "2024-06-15T00:00:00Z",
"credentialSubject": {
"id": "did:webvh:alice.com",
"degree": {
"type": "BachelorDegree",
"name": "Bachelor of Science in Computer Science"
}
},
"proof": {
"type": "Ed25519Signature2020",
"created": "2024-06-15T00:00:00Z",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:webvh:stanford.edu#key-1",
"proofValue": "z3FXQi...signature..."
}
}
关键属性:
- 签发者 —— 谁做出了这个声明(斯坦福大学)
- 持有者 —— 声明是关于谁的(Alice)
- 声明内容 —— 所主张的内容(获得的学位)
- 证明 —— 来自签发者的密码学签名
- 可验证 —— 任何人都能在不联系斯坦福大学的情况下验证签名
可验证凭证的工作原理
1. 签发
签发者对凭证进行签名:
// 签发者 (斯坦福大学) 为 Alice 创建凭证
const credential = {
issuer: 'did:webvh:stanford.edu',
credentialSubject: {
id: 'did:webvh:alice.com',
degree: { type: 'BachelorDegree', name: 'Computer Science' }
},
issuanceDate: new Date().toISOString()
}
// 用签发者的私钥签名
const signedVC = await sign(credential, issuerPrivateKey)
// Alice 存储它 (存入钱包、数据库等)
2. 出示
持有者共享该凭证:
// Alice 向雇主出示该 VC
const presentation = {
type: 'VerifiablePresentation',
holder: 'did:webvh:alice.com',
verifiableCredential: [signedVC],
proof: { /* Alice 的签名,证明她控制该 DID */ }
}
3. 验证
验证者检查凭证:
// 雇主验证:
// 1. 签发者的签名是否有效
const issuerDID = presentation.verifiableCredential[0].issuer
const issuerKey = await resolve(issuerDID) // 获取斯坦福大学的公钥
const isValidSignature = await verify(signedVC, issuerKey)
// 2. 持有者是否控制该 DID (Alice 对出示内容进行了签名)
const holderKey = await resolve(presentation.holder)
const isValidPresentation = await verify(presentation.proof, holderKey)
// 3. 凭证是否已被吊销 (可选,检查状态列表)
const isRevoked = await checkRevocation(signedVC)
// ✅ 所有检查通过 — 凭证有效
无需回调斯坦福大学!
可验证凭证与传统凭证的对比
| 维度 | 传统方式 (例如 API 调用) | 可验证凭证 |
|---|---|---|
| 验证方式 | 调用签发者的 API | 在本地验证签名 |
| 隐私性 | 签发者能看到每一次验证 | 签发后签发者不再获知任何信息 |
| 可用性 | 签发者宕机则验证失败 | 可离线工作 |
| 延迟 | 需要网络往返 | 即时 (本地验证) |
| 选择性披露 | 全有或全无 | 只共享所需的部分 |
| 吊销 | 每次都要向签发者核实 | 检查状态列表 (可缓存) |
真实应用场景
1. 智能体授权 Mandate
问题: A 银行的智能体需要证明自己被授权进行交易。B 银行必须实时验证。
解决方案: A 银行向该智能体签发一份 VC(Mandate)。B 银行验证签名,无需回调 A 银行。
{
"type": ["VerifiableCredential", "TradingMandate"],
"issuer": "did:webvh:bankA.com",
"credentialSubject": {
"id": "did:webvh:bankA.com:agent-42",
"mandate": {
"authorizedBy": "did:webvh:bankA.com:trader-alice",
"permissions": ["execute-trades"],
"expiry": "2026-12-31"
}
},
"proof": { "proofValue": "z3FXQi..." }
}
B 银行的验证过程:
// 1. 检查 A 银行的签名 (这真的是 A 银行签发的吗?)
const isValid = await verifyCredential(mandate)
// 2. 检查是否过期 (是否仍然有效?)
const isExpired = new Date() > new Date(mandate.credentialSubject.mandate.expiry)
// 3. 检查吊销状态 (A 银行是否吊销了它?)
const isRevoked = await checkRevocation(mandate)
// ✅ 验证通过——无需调用 A 银行的 API
2. PHI 访问授权
问题: AI 智能体需要证明某位个案管理员已授权其访问 PHI——HIPAA 要求提供证明。
解决方案: 签发一份将智能体与个案管理员关联起来的 VC。审计日志捕获该 VC——证明是密码学层面的,而不仅仅是一条日志记录。
{
"type": ["VerifiableCredential", "PHIAccessAuthorization"],
"issuer": "did:webvh:hospital.com",
"credentialSubject": {
"id": "did:webvh:hospital.com:agent-99",
"authorization": {
"authorizedBy": "did:webvh:hospital.com:caseworker-bob",
"patient": "patient-12345",
"purpose": "treatment-review",
"expiry": "2026-07-22T00:00:00Z"
}
},
"proof": { "proofValue": "..." }
}
审计追踪:
// 日志条目
{
timestamp: '2026-07-21T19:30:00Z',
action: 'access-PHI',
agent: 'did:webvh:hospital.com:agent-99',
authorization: vcAsJson, // 完整的 VC
verifiedBy: 'gateway-node-5'
}
// 数月后,审计员依然可以验证该 VC 的签名——仍然有效
3. 选择性披露(年龄验证)
问题: 证明你年满 21 岁,而不透露你的出生日期。
解决方案: 使用 BBS+ 签名 的 VC 可以在不透露完整声明的情况下证明某项属性。
// 已签发的 VC (完整声明)
const vc = {
credentialSubject: {
name: 'Alice',
birthdate: '1995-03-15',
address: '123 Main St'
}
}
// 选择性披露——证明年龄 > 21 而不透露出生日期
const derivedVC = await deriveCredential(vc, {
reveal: ['over21: true'], // 只披露这一项
hide: ['name', 'birthdate', 'address'] // 隐藏其余全部信息
})
// 验证者看到:
{
credentialSubject: {
over21: true // 已通过密码学方式证明,不会透露其他任何数据
}
}
吊销机制:Status List 2021
问题: 一份已经流通在外的 VC,该如何吊销?
解决方案: Status List 2021 —— 一个由签发者托管的位串(bitstring)。每份 VC 都指向其中的一个比特位:
{
"credentialStatus": {
"id": "https://issuer.com/status/42#94567",
"type": "StatusList2021Entry",
"statusPurpose": "revocation",
"statusListIndex": "94567",
"statusListCredential": "https://issuer.com/status/42"
}
}
验证:
// 1. 获取状态列表 (可缓存)
const statusList = await fetch('https://issuer.com/status/42')
// 2. 检查第 94567 位
const isRevoked = statusList.bitstring[94567] === 1
// ✅ 若为 0 → 有效,若为 1 → 已吊销
隐私保护: 验证者能得知”这份凭证是否已被吊销?“,但签发者无法得知是谁查询的。
Affinidi 技术栈中的可验证凭证
Elements Services 负责签发 VC:
const vc = await elements.issueCredential({
holder: 'did:webvh:agent-42.com',
type: 'TradingMandate',
claims: {
authorizedBy: 'trader-alice',
permissions: ['execute-trades'],
expiry: '2026-12-31'
}
})
Agent Gateway 在允许操作前验证 VC:
// 智能体携带 VC 提出请求
const request = {
action: 'execute-trade',
credential: vcAsJson
}
// Gateway 进行验证
const isValid = await agentGateway.verifyCredential(request.credential)
if (!isValid) throw new Error('未获授权')
// 若有效则继续执行
信任注册表 存储吊销状态:
// 检查 VC 是否已被吊销
const isRevoked = await trustRegistry.checkRevocation({
credentialId: 'urn:uuid:abc-123',
statusListUrl: 'https://issuer.com/status/42'
})
快速上手
面向开发者
签发一份可验证凭证:
npm install @affinidi/affinidi-tdk
import { VC } from '@affinidi/affinidi-tdk'
const credential = await VC.issue({
issuer: 'did:webvh:yourcompany.com',
holder: 'did:webvh:agent-42.com',
type: 'EmploymentCredential',
claims: {
position: 'Trading Agent',
authorizedBy: 'manager@yourcompany.com',
startDate: '2026-01-01'
}
})
console.log(credential) // 已签名的 VC,可供共享
验证一份凭证:
const isValid = await VC.verify(credential)
console.log(isValid) // true/false
面向架构师
何时应使用 VC:
- ✅ 需要在离线情况下依然有效的授权证明
- ✅ 无需共享基础设施的跨组织信任
- ✅ 隐私保护型验证
- ✅ 带密码学证明的审计追踪
何时不应使用 VC:
- ❌ 高频更新的场景(VC 是一次性签发的,需要更新时应重新签发)
- ❌ 实时流式数据
- ❌ 使用 API 调用就足够的内部系统
延伸阅读
W3C 规范:
Affinidi 文档:
相关深度解析:
相关解决方案: