康心伴Logo
康心伴WellAlly
Data & Privacy

使用 Next.js 构建零知识证明应用

5 分钟阅读

使用 Next.js 构建零知识证明应用

概述

零知识证明(Zero-Knowledge Proof, ZKP)是一种革命性的密码学技术,允许一方(证明者)向另一方(验证者)证明某个陈述的真实性,而无需透露除该陈述真实性以外的任何信息。

在医疗健康应用中,零知识证明架构可以实现:

  • 完全隐私保护:服务器永远无法访问用户的原始健康数据
  • 可验证性:用户可以证明符合条件而不泄露敏感信息
  • 去中心化信任:无需信任第三方即可验证数据真实性

技术架构

1. 零知识证明类型选择

zk-SNARKs(零知识简洁非交互式知识论证)

code
// lib/crypto/zk-snarks.ts
import { groth16 } from 'snarkjs';
import { buildPoseidon } from 'circomlibjs';

export class ZKSnarksProcessor {
  private poseidon: any;

  async init() {
    this.poseidon = await buildPoseidon();
  }

  // 生成证明
  async generateProof(input: any, wasmPath: string, zkeyPath: string) {
    const { proof, publicSignals } = await groth16.fullProve(
      input,
      wasmPath,
      zkeyPath
    );
    return { proof, publicSignals };
  }

  // 验证证明
  async verifyProof(
    proof: any,
    publicSignals: string[],
    vkeyPath: string
  ): Promise<boolean> {
    const vKey = await fetch(vkeyPath).then(r => r.json());
    return await groth16.verify(vKey, publicSignals, proof);
  }

  // Poseidon 哈希(用于电路中的哈希操作)
  hash(inputs: bigint[]): bigint {
    return this.poseidon(inputs);
  }
}
Code collapsed

zk-STARKs(零知识可扩展透明知识论证)

code
// lib/crypto/zk-starks.ts
import { StarkNet } from 'starknet';

export class ZKStarksProcessor {
  // STARKs 适用于大规模数据证明
  // 无需可信设置,但证明体积较大
  async generateStarkProof(witness: any[]): Promise<string> {
    // 实现略 - 使用 starknet 库或类似工具
    return '';
  }

  async verifyStarkProof(proof: string): Promise<boolean> {
    // 实现略
    return true;
  }
}
Code collapsed

2. 客户端加密实现

端到端加密工具类

code
// lib/crypto/client-encryption.ts
import { encrypt, decrypt } from '@metamask/eth-sig-util';
import { randomBytes, scryptSync } from 'crypto';

export class ClientEncryption {
  // 生成加密密钥对
  static async generateKeyPair(): Promise<CryptoKeyPair> {
    return await crypto.subtle.generateKey(
      {
        name: 'ECDH',
        namedCurve: 'P-256',
      },
      true,
      ['deriveKey']
    );
  }

  // 从密码派生密钥
  static deriveKey(password: string, salt: Buffer): Buffer {
    return scryptSync(password, salt, 32);
  }

  // 加密敏感数据
  static async encryptData(
    data: string,
    publicKey: string
  ): Promise<{ encrypted: string; iv: string }> {
    const iv = randomBytes(16);
    const key = await this.importKey(publicKey);

    const encrypted = await crypto.subtle.encrypt(
      {
        name: 'AES-GCM',
        iv: iv,
      },
      key,
      new TextEncoder().encode(data)
    );

    return {
      encrypted: Buffer.from(encrypted).toString('base64'),
      iv: iv.toString('base64'),
    };
  }

  // 解密数据
  static async decryptData(
    encryptedData: string,
    iv: string,
    privateKey: CryptoKey
  ): Promise<string> {
    const decrypted = await crypto.subtle.decrypt(
      {
        name: 'AES-GCM',
        iv: Buffer.from(iv, 'base64'),
      },
      privateKey,
      Buffer.from(encryptedData, 'base64')
    );

    return new TextDecoder().decode(decrypted);
  }

  static async importKey(keyData: string): Promise<CryptoKey> {
    return await crypto.subtle.importKey(
      'raw',
      Buffer.from(keyData, 'base64'),
      { name: 'AES-GCM' },
      false,
      ['encrypt', 'decrypt']
    );
  }
}
Code collapsed

3. Next.js API 路由实现

零知识验证端点

code
// app/api/verify/zk-proof/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ZKSnarksProcessor } from '@/lib/crypto/zk-snarks';

const zkProcessor = new ZKSnarksProcessor();
await zkProcessor.init();

export async function POST(req: NextRequest) {
  try {
    const { proof, publicSignals } = await req.json();

    // 验证零知识证明
    const vkeyPath = '/keys/verification_key.json';
    const isValid = await zkProcessor.verifyProof(
      proof,
      publicSignals,
      vkeyPath
    );

    if (!isValid) {
      return NextResponse.json(
        { error: '无效的零知识证明' },
        { status: 400 }
      );
    }

    // 服务器只存储证明和公开信号,不存储原始数据
    return NextResponse.json({
      verified: true,
      timestamp: Date.now(),
    });
  } catch (error) {
    return NextResponse.json(
      { error: '证明验证失败' },
      { status: 500 }
    );
  }
}
Code collapsed

盲签名端点

code
// app/api/crypto/blind-signature/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { blindSign } from '@/lib/crypto/blind-signature';

export async function POST(req: NextRequest) {
  try {
    const { blindedMessage } = await req.json();

    // 服务器对盲化消息进行签名
    const signature = await blindSign(blindedMessage);

    // 服务器无法看到原始消息内容
    return NextResponse.json({ signature });
  } catch (error) {
    return NextResponse.json(
      { error: '盲签名失败' },
      { status: 500 }
    );
  }
}
Code collapsed

4. 前端组件实现

加密数据存储 Hook

code
// hooks/useEncryptedStorage.ts
import { useState, useEffect } from 'react';
import { ClientEncryption } from '@/lib/crypto/client-encryption';

export function useEncryptedStorage<T>(key: string, password: string) {
  const [data, setData] = useState<T | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    async function loadData() {
      try {
        const stored = localStorage.getItem(key);
        if (!stored) {
          setIsLoading(false);
          return;
        }

        const { encrypted, iv } = JSON.parse(stored);

        // 从密码派生密钥
        const salt = Buffer.from(encrypted, 'base64').slice(0, 16);
        const derivedKey = ClientEncryption.deriveKey(password, salt);

        // 解密数据
        const decrypted = await ClientEncryption.decryptData(
          encrypted,
          iv,
          await ClientEncryption.importKey(derivedKey.toString('base64'))
        );

        setData(JSON.parse(decrypted));
      } catch (err) {
        setError(err as Error);
      } finally {
        setIsLoading(false);
      }
    }

    loadData();
  }, [key, password]);

  const saveData = async (newData: T) => {
    const publicKey = await getPublicKey();
    const { encrypted, iv } = await ClientEncryption.encryptData(
      JSON.stringify(newData),
      publicKey
    );

    localStorage.setItem(key, JSON.stringify({ encrypted, iv }));
    setData(newData);
  };

  return { data, isLoading, error, saveData };
}
Code collapsed

零知识证明提交组件

code
// components/zk/ZKProofSubmit.tsx
'use client';

import { useState } from 'react';
import { ZKSnarksProcessor } from '@/lib/crypto/zk-snarks';

export function ZKProofSubmit() {
  const [isGenerating, setIsGenerating] = useState(false);
  const [proofResult, setProofResult] = useState(null);

  const generateAndSubmitProof = async (privateInput: any) => {
    setIsGenerating(true);

    try {
      const zkProcessor = new ZKSnarksProcessor();
      await zkProcessor.init();

      // 在客户端生成证明
      const { proof, publicSignals } = await zkProcessor.generateProof(
        privateInput,
        '/circules/health.wasm',
        '/circules/health.zkey'
      );

      // 提交证明到服务器
      const response = await fetch('/api/verify/zk-proof', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ proof, publicSignals }),
      });

      const result = await response.json();
      setProofResult(result);
    } catch (error) {
      console.error('证明生成失败:', error);
    } finally {
      setIsGenerating(false);
    }
  };

  return (
    <div className="p-6 border rounded-lg">
      <h3>零知识证明验证</h3>
      <button
        onClick={() => generateAndSubmitProof({ /* 私有输入 */ })}
        disabled={isGenerating}
      >
        {isGenerating ? '生成证明中...' : '生成并提交证明'}
      </button>
      {proofResult && (
        <div className="mt-4 p-4 bg-green-50 rounded">
          证明验证成功!
        </div>
      )}
    </div>
  );
}
Code collapsed

5. Circom 电路设计

健康数据验证电路

code
// circuits/health-eligibility.circom
pragma circom 2.0.0;

include "../node_modules/circomlib/circuits/poseidon.circom";

// 证明用户年龄符合要求而不透露实际年龄
template AgeProof() {
    signal input birthYear;      // 私有输入
    signal input currentYear;    // 公开输入
    signal input minAge;         // 公开输入(最低年龄要求)
    signal output isEligible;    // 公开输出

    // 计算年龄
    signal age;
    age <== currentYear - birthYear;

    // 比较年龄
    signal diff;
    diff <== age - minAge;

    // 如果 diff >= 0,isEligible = 1
    isEligible <== DiffieHellman.isZero(diff);
}

// 证明健康数据符合标准而不透露具体数值
template HealthRangeProof() {
    signal input value;          // 私有输入
    signal input min;            // 公开输入
    signal input max;            // 公开输入
    signal output inRange;       // 公开输出

    // 检查 value >= min
    signal aboveMin;
    aboveMin <== DiffieHellman.isZero(min - value);

    // 检查 value <= max
    signal belowMax;
    belowMax <== DiffieHellman.isZero(value - max);

    // 两个条件都满足
    inRange <== AND(aboveMin, belowMax);
}

component main {public [currentYear, minAge]} = AgeProof();
Code collapsed

6. 同态加密集成

code
// lib/crypto/homomorphic.ts
import * as tfhe from 'node-tfhe';

export class HomomorphicEncryption {
  private client: any;

  constructor() {
    this.client = new tfhe.TFHEClient();
  }

  // 加密数值
  encryptInt(value: number): string {
    return this.client.encryptInt(value);
  }

  // 在加密状态下进行计算
  addEncrypted(a: string, b: string): string {
    return this.client.add(a, b);
  }

  // 解密结果
  decryptInt(encrypted: string): number {
    return this.client.decryptInt(encrypted);
  }

  // 聚合加密数据
  aggregateEncryptedSum(values: string[]): string {
    return values.reduce((acc, val) => this.addEncrypted(acc, val), this.encryptInt(0));
  }
}
Code collapsed

合规性要求

HIPAA 合规

code
// lib/compliance/hipaa.ts
export const HIPAARequirements = {
  // 最小必要原则
  minimumNecessary: {
    description: '仅收集和使用必要的最小量健康信息',
    implementation: '零知识证明天然符合此原则',
  },

  // 安全保障
  safeguards: {
    administrative: [
      '访问控制策略',
      '员工培训记录',
      '应急响应计划',
    ],
    physical: [
      '服务器物理安全',
      '设备管理政策',
    ],
    technical: [
      '端到端加密',
      '访问日志审计',
      '完整性验证',
    ],
  },

  // 病人权利
  patientRights: [
    '访问自己的健康信息',
    '要求更正错误信息',
    '获取数据使用记录',
    '要求删除数据(GDPR 风格)',
  ],
};
Code collapsed

GDPR 合规

code
// lib/compliance/gdpr.ts
export const GDPRRequirements = {
  // 数据最小化
  dataMinimization: {
    description: '零知识架构:服务器不存储个人数据',
    compliant: true,
  },

  // 被遗忘权
  rightToErasure: {
    description: '可删除本地加密密钥实现数据擦除',
    implementation: 'app/api/user/delete/route.ts',
  },

  // 数据可携权
  dataPortability: {
    description: '用户可导出自己的加密数据',
    implementation: 'app/api/user/export/route.ts',
  },

  // 透明性
  transparency: {
    description: '明确告知数据如何被证明和验证',
    privacyPolicy: '/privacy#zero-knowledge',
  },
};
Code collapsed

安全检查清单

开发阶段

  • 使用可信的密码学库(snarkjs、circom、tfhe)
  • 电路代码经过形式化验证
  • 可信设置在安全环境下进行
  • 密钥生成使用安全的随机数生成器
  • 加密密钥与签名密钥分离
  • 实现密钥轮换机制

部署阶段

  • 所有 API 使用 HTTPS
  • 启用 HTTP 严格传输安全(HSTS)
  • 实现证书固定(Certificate Pinning)
  • 验证文件使用内容寻址存储(IPFS)
  • 密钥不存储在应用代码中
  • 使用环境变量或密钥管理服务

运行阶段

  • 定期审计密码学库版本
  • 监控异常证明模式
  • 记录所有验证操作(不含敏感数据)
  • 实现速率限制防止暴力攻击
  • 定期进行渗透测试
  • 建立安全事件响应流程

用户通信

  • 隐私政策明确说明零知识架构
  • 用户界面清晰显示加密状态
  • 提供数据使用透明度报告
  • 教育用户如何保护密钥
  • 明确告知哪些数据是加密的

性能优化

1. 证明生成优化

code
// 使用 Web Worker 避免阻塞主线程
// workers/zk-proof-generator.ts
import { expose } from 'threads/worker';
import { ZKSnarksProcessor } from '@/lib/crypto/zk-snarks';

expose(async function generateProof(input: any) {
  const processor = new ZKSnarksProcessor();
  await processor.init();
  return await processor.generateProof(
    input,
    '/circuits/health.wasm',
    '/circuits/health.zkey'
  );
});
Code collapsed

2. 证明缓存

code
// lib/cache/proof-cache.ts
import { LRUCache } from 'lru-cache';

export const proofCache = new LRUCache<string, any>({
  max: 500,
  ttl: 1000 * 60 * 60, // 1小时
});

export function getCachedProof(inputHash: string): any | undefined {
  return proofCache.get(inputHash);
}

export function setCachedProof(inputHash: string, proof: any): void {
  proofCache.set(inputHash, proof);
}
Code collapsed

3. 批量验证

code
// lib/crypto/batch-verify.ts
export async function batchVerifyProofs(
  proofs: Array<{ proof: any; publicSignals: string[] }>
): Promise<boolean[]> {
  return Promise.all(
    proofs.map(({ proof, publicSignals }) =>
      zkProcessor.verifyProof(proof, publicSignals, vkeyPath)
    )
  );
}
Code collapsed

参考资料


免责声明:本文提供的代码示例仅供学习参考。在生产环境中使用零知识证明系统前,请务必进行安全审计,并咨询密码学和法律专家。

#

文章标签

nextjs
zero-knowledge
cryptography
security
privacy

觉得这篇文章有帮助?

立即体验康心伴,开始您的健康管理之旅