第一章:为什么需要可验证网络?
1.1 传统身份系统的缺陷
| 问题 | 说明 |
|---|
- 中心化风险 | 平台掌握用户身份,可滥用或被攻破(如 Facebook 数据泄露)
- 重复 KYC | 每个新服务都要重新提交身份证、住址等
- 信息过曝 | 验证年龄需提交完整出生日期
1.2 可验证网络三要素(W3C 标准)
-
DID(去中心化标识符)
- 格式:
did:example:123456abcdef - 特性:用户创建、用户控制、无需注册机构
- 存储:区块链 / IPFS / 去中心化存储
- 格式:
-
VC(可验证凭证)
- 由 Issuer(发行方) 签发(如政府、银行)
- 包含 Claims(声明) + Issuer 签名
- 示例:
{
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "AgeCredential"],
"issuer": "did:example:gov123",
"issuanceDate": "2025-01-01T00:00:00Z",
"credentialSubject": {
"id": "did:example:user456",
"birthDate": "2000-05-20"
},
"proof": { /* 签名 */ }
} -
VP(可验证陈述)
- 用户从多个 VC 中 选择性披露 信息
- 可附加 零知识证明 实现最小化披露
信任模型转变 :
从 "相信平台" → "验证密码学证明"
第二章:架构设计 ------ DID + ZKP 全栈集成
2.1 整体流程(无密码登录)
[Vue 前端]
│
├── 1. 生成挑战(nonce) ←── [Flask 后端]
│
├── 2. 用户钱包签名 nonce + DID
│ (或生成 ZK 证明)
│
└── 3. 提交 VP → [Flask 验证] → 登录成功
2.2 技术栈选型
| 功能 | 技术 | 说明 |
|---|
- DID 管理 |
ethr-did(以太坊兼容) | 支持 EVM 链- VC 签发/验证 |
veramo(JavaScript SDK) | W3C 兼容- ZK 电路 | Circom + SnarkJS | 浏览器友好
- 钱包连接 | WalletConnect v2 | 支持多钱包
- 后端验证 | Python
py_did+ 自定义 ZK 验证器 |
第三章:无密码登录 ------ DID 签名认证
3.1 后端:生成挑战
# routes/auth.py
from flask import session
import secrets
@app.route('/auth/challenge')
def get_challenge():
challenge = secrets.token_urlsafe(32)
session['challenge'] = challenge
return jsonify({"challenge": challenge})
3.2 前端:钱包签名
<script setup>
import { EthereumProvider } from '@walletconnect/ethereum-provider'
const connectWallet = async () => {
const provider = await EthereumProvider.init({
projectId: 'YOUR_WALLETCONNECT_ID',
chains: [1],
methods: ['eth_requestAccounts', 'personal_sign']
})
await provider.enable()
window.ethereum = provider
}
const loginWithDID = async () => {
// 1. 获取挑战
const res = await fetch('/auth/challenge')
const { challenge } = await res.json()
// 2. 获取用户地址(作为 DID)
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
const did = `did:pkh:eip155:1:${accounts[0].toLowerCase()}`
// 3. 签名挑战
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [challenge, accounts[0]]
})
// 4. 提交 VP
await fetch('/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ did, challenge, signature })
})
}
</script>
3.3 后端:验证签名
# utils/did_auth.py
from ecdsa import VerifyingKey, SECP256k1
import sha3
from eth_utils import to_checksum_address
def verify_did_signature(did: str, challenge: str, signature: str) -> bool:
# 1. 从 DID 提取地址
if not did.startswith('did:pkh:eip155:1:'):
return False
address = to_checksum_address('0x' + did.split(':')[-1])
# 2. 构造签名消息(EIP-191)
message = f"\x19Ethereum Signed Message:\n{len(challenge)}{challenge}"
message_hash = sha3.keccak_256(message.encode()).digest()
# 3. 恢复公钥
sig = bytes.fromhex(signature[2:]) # 去掉 0x
vk = VerifyingKey.from_signature(
sig[:-1], # 去掉恢复 ID
message_hash,
curve=SECP256k1,
hashfunc=sha3.keccak_256
)
recovered_addr = to_checksum_address(vk.to_string('compressed')[1:])
return recovered_addr == address
优势:
- 无密码、无 OAuth 依赖
- 用户用已有钱包即可登录
第四章:零知识证明实战 ------ 年龄验证
4.1 问题定义
用户想证明自己 ≥18 岁,但不想透露具体生日。
4.2 ZK 电路设计(Circom)
// circuits/age_check.circom
pragma circom 2.0.0;
template AgeCheck() {
signal input birthYear; // 用户私有输入
signal input currentYear; // 公共输入
signal output isAdult; // 公共输出
signal diff;
diff <== currentYear - birthYear;
isAdult <== diff >= 18 ? 1 : 0;
}
component main = AgeCheck();
4.3 编译与生成证明(前端)
# 编译电路
circom age_check.circom --r1cs --wasm --sym
# 启动 SnarkJS 服务(用于可信设置)
snarkjs groth16 setup age_check.r1cs pot17_final.ptau age_check_0000.zkey
4.4 浏览器中生成证明
// zk/age_prover.ts
import { groth16 } from 'snarkjs'
import ageCheckWasm from './age_check.wasm?url'
import zkey from './age_check_0000.zkey?url'
export async function generateAgeProof(birthYear: number, currentYear: number = 2025) {
const { proof, publicSignals } = await groth16.fullProve(
{ birthYear, currentYear },
ageCheckWasm,
zkey
)
return {
proof,
publicSignals // [isAdult (0/1), currentYear]
}
}
4.5 后端验证证明
# utils/zk_verifier.py
import json
from py_ecc.bn128 import bn128_curve as curve
from snarkjs import groth16_verify # 假设有 Python 绑定
def verify_age_proof(proof_json: dict, public_signals: list) -> bool:
with open('verification_key.json') as f:
vkey = json.load(f)
return groth16_verify(vkey, public_signals, proof_json)
隐私保障:
- 后端只知道
isAdult=1,不知道birthYear=2000- 证明不可伪造(基于椭圆曲线密码学)
第五章:可验证凭证(VC)系统
5.1 发行 VC(银行 KYC 场景)
# services/vc_issuer.py
from veramo import create_verifiable_credential
def issue_kyc_vc(user_did: str, name: str, birth_date: str):
credential = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "KYCCredential"],
"issuer": BANK_DID,
"issuanceDate": datetime.utcnow().isoformat() + "Z",
"credentialSubject": {
"id": user_did,
"name": name,
"birthDate": birth_date
}
}
return create_verifiable_credential(credential, issuer_private_key)
5.2 用户存储 VC
- 数字钱包(如 Polygon ID Wallet)安全存储 VC
- VC 不上链:仅 DID 文档和状态(如撤销)上链
5.3 验证 VC(第三方平台)
# routes/verify_kyc.py
from veramo import verify_credential
@app.route('/verify/kyc', methods=['POST'])
def verify_kyc():
vp = request.json # Verifiable Presentation
# 1. 验证 VC 签名
if not verify_credential(vp['verifiableCredential']):
return jsonify({"error": "Invalid VC"}), 400
# 2. 检查 DID 是否在许可列表
if vp['holder']['id'] not in allowed_dids:
return jsonify({"error": "Unauthorized"}), 403
return jsonify({"status": "verified"})
优势:
- 用户一次 KYC,多处复用
- 平台无需存储敏感个人信息
第六章:前端集成 ------ Vue + WalletConnect
6.1 封装 DID 登录组件
<template>
<button @click="login" :disabled="loading">
{{ loading ? 'Connecting...' : 'Login with Wallet' }}
</button>
</template>
<script setup>
import { useDIDAuth } from '@/composables/didAuth'
const { login, loading } = useDIDAuth()
</script>
6.2 Composable 逻辑
// composables/didAuth.ts
import { ref } from 'vue'
export function useDIDAuth() {
const loading = ref(false)
const login = async () => {
loading.value = true
try {
await connectWallet()
const { challenge } = await fetchChallenge()
const { did, signature } = await signChallenge(challenge)
await submitVP({ did, challenge, signature })
// 登录成功,跳转
router.push('/dashboard')
} finally {
loading.value = false
}
}
return { login, loading }
}
用户体验:与传统"微信登录"按钮无异,但更安全、更隐私。
第七章:隐私与安全最佳实践
7.1 防重放攻击
- 挑战(nonce)一次性 + 短期有效(如 5 分钟)
- VP 中包含 有效期 和 唯一 ID
7.2 凭证撤销
- 使用 Status List 2021(W3C 标准)
- 撤销列表存储于 IPFS,DID 文档指向最新列表
7.3 最小化披露原则
- 默认使用 ZK 证明 而非原始 VC
- 前端明确告知用户"将披露哪些信息"
第八章:性能与兼容性
8.1 ZK 证明生成时间
| 设备 | 电路复杂度 | 生成时间 |
|---|
现代笔记本 | AgeCheck(简单) | ~800ms
iPhone 14 | 同上 | ~1.2s
低端安卓 | 同上 | ~2.5s
优化:预加载 WASM 模块
使用 Web Worker 避免阻塞 UI
8.2 钱包兼容性
| 钱包 | DID 支持 | VC 支持 |
|---|
- MetaMask | ✅ (via Snap) | ❌
- Polygon ID Wallet | ✅ | ✅
- Microsoft Entra Verified ID | ✅ | ✅
策略:优先支持 Polygon ID,降级到 DID 签名。
第九章:合规与伦理
9.1 GDPR 合规
- 用户可删除 VC:钱包本地删除,平台仅保留验证记录(无 PII)
- 数据最小化:ZK 证明天然符合
9.2 防止歧视
- 避免生物特征 VC:如种族、基因等敏感属性
- 开源验证逻辑:防止隐藏偏见算法
第十章:未来方向
10.1 BBS+ 签名
- 支持 选择性披露 而无需 ZK(如只出示 VC 中的"年龄"字段)
- 工具:
@mattrglobal/bbs-signatures
10.2 跨链 DID
- 使用 ENS + .eth 域名 作为人类可读 DID
- 示例:
did:pkh:eip155:1:0x...→alice.eth
总结:构建用户主权的身份层
可验证网络不是加密乌托邦,而是下一代互联网信任基础设施。