如何验证 VC 签名?
解读
在国内 CouchDB 岗位面试中,面试官抛出“如何验证 VC 签名”并不是想听你背诵 JWT 或 JWS 的通用流程,而是考察三点:
- 你是否意识到 CouchDB 自身不原生支持 W3C Verifiable Credential(VC)语义,必须在应用层或中间件完成签名校验;
- 你是否能把“验证签名”拆解为取公钥 → 验签名 → 验信任链 → 存证审计四步,并给出可落地的 CouchDB 集成方案;
- 你是否熟悉国密 SM2/SM3在国内合规场景下的替换思路,以及离线优先同步时如何防止“签名重放”。
一句话:面试官想看你在分布式、离线、合规三大现实约束下,把 VC 签名验证无缝嫁接到 CouchDB 数据流中的工程能力。
知识点
- VC 三层数据结构:JWT 形式下分为 Header(含 kid/alg)、Payload(含 iss/nbf/exp/credentialSubject)、Signature;JSON-LD 形式下需额外处理 @context 与 proof 字段。
- 公钥获取策略:国内主流走政务外网 CA 目录或企业级 DID 侧链,CouchDB 端通过 HTTP 微服务网关缓存公钥,避免直接访问外网。
- 签名算法映射:
- 国际场景:ES256 (P-256) + SHA256;
- 国密合规:SM2 签名 + SM3 摘要,需在 Node/Python 层调用**国密硬件密码模块(HSM)**完成验签。
- 离线优先挑战:移动端先本地验签,再写入 CouchDB;_rev 与签名值联合做幂等键,防止同步时因冲突产生重复凭证。
- CouchDB 存储模式:
- 原始 VC 文档存为独立 JSON 文档,_id=凭证唯一标识(UUIDv4 或 DID + 片段);
- 验签结果(布尔值、公钥指纹、时间戳、审计流水号)写入同一文档的
meta.verify子对象,利用 CouchDB 不可变版本历史实现审计轨迹。
- 性能与并发:使用 CouchDB 的 _bulk_docs 批量写入验签结果,配合 Erlang 原生并发降低 CPU 密集验签带来的延迟;热点公钥放 Redis 本地缓存,TTL 按 CRL 更新周期设置。
- 安全加固:
- 开启 CouchDB 的 require_valid_user 与 proxy_authentication_handler,确保验签服务调用链路上每跳都有身份;
- 对
meta.verify子对象再加一次 HMAC-SM3 对称签名,防止内部运维人员篡改验签结果。
答案
落地步骤(可直接写进面试白板):
-
设计文档模型
每条 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" } } } -
公钥获取与缓存
在 Node.js 验签微服务里,先解析proof.jws的 Header.kid,得到 DID#key-1;通过政务外网 CA 目录接口拉取 PEM,把公钥指纹做 Redis key,TTL=CRL 下次更新间隔;拉取失败直接拒绝,不降级到本地自签。 -
执行验签
使用 node-sm-crypto 库,按国密要求走 SM2.verify(SM3(Header + Payload), Signature, PublicKey);国际场景则走 jose.verify。验签通过后将meta.verify对象写入 CouchDB,利用 _bulk_docs 保证原子性。 -
信任链与吊销检查
验签通过后,再调用 CA 的 OCSP 接口或本地 CRL 文件,确认证书未吊销;若失败,把meta.verify.result改 false,并写入revokeReason。 -
离线同步冲突处理
移动端 PouchDB 先本地验签,生成meta.verify;同步到 CouchDB 时,使用 custom conflict 函数:若云端已存在且 verify.result=true,则保留云端版本;否则以本地为准,确保“已验证”状态不会被旧版本覆盖。 -
审计与监管
每夜把meta.verify子对象通过 CouchDB _changes feed 推送到政务区块链存证节点,满足《电子证照国家标准》GB/T 36905-2018 对“验证结果不可篡改”要求。
一句话总结:CouchDB 只负责不可变存储与同步,验签逻辑全部放在受信微服务里,国密合规、离线冲突、审计监管三者缺一不可。
拓展思考
- Q:如果未来 CouchDB 原生支持 Erlang 版本的国密验签 NIF,是否就可以把验签下推到数据库层?
A:技术上可行,但不符合等保三权分立原则;数据库层应只存数据,不持有私钥或做密码运算,防止 DBA 越权。 - Q:在跨省离线场景,CRL 无法实时更新,如何降低吊销滞后风险?
A:引入短期凭证(有效期≤6 小时)+ 临时公钥列表(TSL),TSL 本身带短周期签名,CouchDB 同步时把 TSL 当作普通文档复制,客户端定时刷新即可。 - Q:面对海量 VC(日均 1 亿次写入),CouchDB 的 btree 写放大可能成为瓶颈,如何优化?
A:分层存储:热数据保留在 CouchDB 集群,温数据按 _id 范围做 shard 归档到对象存储(如国产华为 OBS),只保留meta.verify摘要;查询时通过 Erlang 外部视图回温,既满足合规“全量保存 15 年”,又降低在线集群规模。