如何验证 VC 签名?

解读

在国内 CouchDB 岗位面试中,面试官抛出“如何验证 VC 签名”并不是想听你背诵 JWT 或 JWS 的通用流程,而是考察三点:

  1. 你是否意识到 CouchDB 自身不原生支持 W3C Verifiable Credential(VC)语义,必须在应用层或中间件完成签名校验
  2. 你是否能把“验证签名”拆解为取公钥 → 验签名 → 验信任链 → 存证审计四步,并给出可落地的 CouchDB 集成方案;
  3. 你是否熟悉国密 SM2/SM3在国内合规场景下的替换思路,以及离线优先同步时如何防止“签名重放”。

一句话:面试官想看你在分布式、离线、合规三大现实约束下,把 VC 签名验证无缝嫁接到 CouchDB 数据流中的工程能力。

知识点

  1. VC 三层数据结构:JWT 形式下分为 Header(含 kid/alg)、Payload(含 iss/nbf/exp/credentialSubject)、Signature;JSON-LD 形式下需额外处理 @context 与 proof 字段。
  2. 公钥获取策略:国内主流走政务外网 CA 目录企业级 DID 侧链,CouchDB 端通过 HTTP 微服务网关缓存公钥,避免直接访问外网。
  3. 签名算法映射
    • 国际场景:ES256 (P-256) + SHA256;
    • 国密合规:SM2 签名 + SM3 摘要,需在 Node/Python 层调用**国密硬件密码模块(HSM)**完成验签。
  4. 离线优先挑战:移动端先本地验签,再写入 CouchDB;_rev 与签名值联合做幂等键,防止同步时因冲突产生重复凭证。
  5. CouchDB 存储模式
    • 原始 VC 文档存为独立 JSON 文档,_id=凭证唯一标识(UUIDv4 或 DID + 片段);
    • 验签结果(布尔值、公钥指纹、时间戳、审计流水号)写入同一文档的 meta.verify 子对象,利用 CouchDB 不可变版本历史实现审计轨迹
  6. 性能与并发:使用 CouchDB 的 _bulk_docs 批量写入验签结果,配合 Erlang 原生并发降低 CPU 密集验签带来的延迟;热点公钥放 Redis 本地缓存,TTL 按 CRL 更新周期设置。
  7. 安全加固
    • 开启 CouchDB 的 require_valid_userproxy_authentication_handler,确保验签服务调用链路上每跳都有身份;
    • meta.verify 子对象再加一次 HMAC-SM3 对称签名,防止内部运维人员篡改验签结果。

答案

落地步骤(可直接写进面试白板):

  1. 设计文档模型
    每条 VC 对应一个 CouchDB 文档:

    {
      "_id": "vc:123e4567-e89b-12d3-a456-426614174000",
      "credentialSubject": { "id": "did:example:u123", "degree": "bachelor" },
      "proof": {
        "type": "EcdsaSecp256r1Signature2019",
        "created": "2024-06-01T08:00:00Z",
        "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NiJ9..QRm4…"
      },
      "meta": {
        "verify": {
          "result": true,
          "pkFingerprint": "SM2:ab32cf…",
          "verifiedAt": "2024-06-01T08:01:00Z",
          "auditSN": "couch-240601-000001"
        }
      }
    }
    
  2. 公钥获取与缓存
    在 Node.js 验签微服务里,先解析 proof.jws 的 Header.kid,得到 DID#key-1;通过政务外网 CA 目录接口拉取 PEM,把公钥指纹做 Redis key,TTL=CRL 下次更新间隔;拉取失败直接拒绝,不降级到本地自签

  3. 执行验签
    使用 node-sm-crypto 库,按国密要求走 SM2.verify(SM3(Header + Payload), Signature, PublicKey);国际场景则走 jose.verify。验签通过后将 meta.verify 对象写入 CouchDB,利用 _bulk_docs 保证原子性

  4. 信任链与吊销检查
    验签通过后,再调用 CA 的 OCSP 接口或本地 CRL 文件,确认证书未吊销;若失败,把 meta.verify.result 改 false,并写入 revokeReason

  5. 离线同步冲突处理
    移动端 PouchDB 先本地验签,生成 meta.verify;同步到 CouchDB 时,使用 custom conflict 函数:若云端已存在且 verify.result=true,则保留云端版本;否则以本地为准,确保“已验证”状态不会被旧版本覆盖

  6. 审计与监管
    每夜把 meta.verify 子对象通过 CouchDB _changes feed 推送到政务区块链存证节点,满足《电子证照国家标准》GB/T 36905-2018 对“验证结果不可篡改”要求。

一句话总结:CouchDB 只负责不可变存储与同步,验签逻辑全部放在受信微服务里,国密合规、离线冲突、审计监管三者缺一不可

拓展思考

  1. Q:如果未来 CouchDB 原生支持 Erlang 版本的国密验签 NIF,是否就可以把验签下推到数据库层?
    A:技术上可行,但不符合等保三权分立原则;数据库层应只存数据,不持有私钥或做密码运算,防止 DBA 越权。
  2. Q:在跨省离线场景,CRL 无法实时更新,如何降低吊销滞后风险?
    A:引入短期凭证(有效期≤6 小时)+ 临时公钥列表(TSL),TSL 本身带短周期签名,CouchDB 同步时把 TSL 当作普通文档复制,客户端定时刷新即可。
  3. Q:面对海量 VC(日均 1 亿次写入),CouchDB 的 btree 写放大可能成为瓶颈,如何优化?
    A分层存储:热数据保留在 CouchDB 集群,温数据按 _id 范围做 shard 归档到对象存储(如国产华为 OBS),只保留 meta.verify 摘要;查询时通过 Erlang 外部视图回温,既满足合规“全量保存 15 年”,又降低在线集群规模。