Python Web 开发进阶实战:可验证网络 —— 在 Flask + Vue 中实现去中心化身份(DID)与零知识证明(ZKP)认证

第一章:为什么需要可验证网络?

1.1 传统身份系统的缺陷

问题 说明
  • 中心化风险 | 平台掌握用户身份,可滥用或被攻破(如 Facebook 数据泄露)
  • 重复 KYC | 每个新服务都要重新提交身份证、住址等
  • 信息过曝 | 验证年龄需提交完整出生日期

1.2 可验证网络三要素(W3C 标准)

  1. DID(去中心化标识符)

    • 格式:did:example:123456abcdef
    • 特性:用户创建、用户控制、无需注册机构
    • 存储:区块链 / IPFS / 去中心化存储
  2. 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": { /* 签名 */ }
    }

  3. 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

总结:构建用户主权的身份层

可验证网络不是加密乌托邦,而是下一代互联网信任基础设施。

相关推荐
Uncertainty!!2 小时前
pycharm本地Failed to open X display(exiting)
ide·python·pycharm
勇往直前plus2 小时前
解决:pycharm运行程序时出现Run ‘python tests for XXX.py‘的问题
ide·python·pycharm
运筹vivo@2 小时前
攻防世界:Web_php_include
前端·web安全·php
Kratzdisteln2 小时前
【1902】自适应学习系统 - 完整技术方案
java·python·学习
饿了么骑手贪大心2 小时前
简单易用的网络测试工具——Clumsy使用总结
网络·测试工具
天若有情6732 小时前
【Python】从0到1实现轻量级接口测试工具:基于Python+FastAPI+Pytest
python·测试工具·fastapi
Jia ming2 小时前
游戏卡顿?SMB传输惹的祸!
网络
头发还没掉光光2 小时前
Linux网络之TCP协议
linux·运维·开发语言·网络·网络协议·tcp/ip
尼古拉斯·纯情暖男·天真·阿玮2 小时前
实验七 防火墙与入侵防护实验
linux·服务器·网络