使用 crypto-pouch 插件时,密码轮换策略如何同步到 CouchDB 端?
解读
面试官真正想考察的是:
- 你是否理解 crypto-pouch 只在浏览器/客户端侧做加密,而 CouchDB 本身只存密文;
- 当业务要求 定期轮换密钥 时,如何 在不破坏离线优先、多主复制的前提下,让旧密文平滑过渡到新密钥,并保证 所有节点(含离线后回来的移动端)都能解密历史数据;
- 你是否能给出 可落地、可回滚、符合国内等保/密评要求 的工程方案,而不是简单一句“重新加密”。
知识点
- crypto-pouch 加密层级:在 PouchDB 落盘前对 JSON 文档做 AES-GCM 全字段加密,CouchDB 看到的已是密文,密钥只存在于客户端。
- CouchDB 复制协议:基于 _rev 向量时钟,不会自动触发客户端重新加密,也不会感知加密层变化。
- 密钥轮换三大痛点:
- 旧数据仍用旧密钥加密,不能强制全量重写(移动设备可能离线);
- 新数据必须用新密钥,且要向后兼容;
- 国内合规要求 密钥分级、密钥托管、审计日志,不能硬编码在代码仓库。
- 常见错误:
- 把新密钥直接写死在新版本 App 里,导致老版本离线用户回来后无法解密;
- 在 CouchDB 侧建“密钥表”,破坏零信任原则;
- 用 _changes 监听后全量重写,造成 雪崩式冲突。
答案
给出一套在国内金融、政务场景已落地的 “双密钥信封 + 分阶段标记” 方案,分五步:
-
密钥分级与托管
主密钥(KEK)放在 国密合规的 HSM 或阿里云 KMS,工作密钥(DEK)由 KEK 加密后随文档携带,CouchDB 只存密文 DEK,不存明文。 -
文档内增加加密元数据
在每条 doc 里新增_crypto字段:{ "_id": "order/123", "cipher": "...", "_crypto": { "ver": 2, // 密钥版本 "dek_id": "dek_2025Q2", // DEK 标识 "wrapped_dek": "..." // 被 KEK 加密的 DEK } }该字段 被 crypto-pouch 排除在加密之外,方便客户端快速识别版本。
-
轮换触发流程
由 密钥管理系统(KMS)定时推送 MQTT/HTTP 通知 到移动端;App 收到后:
a. 生成新 DEK,用 KEK 加密后写入新dek_id;
b. 本地新建一个 “只写新密钥” 的 crypto-pouch 实例;
c. 老数据 按需解密时再读旧 DEK,不写回;新数据一律用新 DEK。 -
** CouchDB 复制冲突处理**
由于_crypto.ver不同,同一 doc 不会出现两份不同密文冲突;若用户在不同设备分别更新,CouchDB 会生成冲突节点,客户端在 post-replication 钩子里用本地最新 DEK 重新加密后手动解决冲突,并打上merged: true标记,避免无限循环。 -
旧密钥退役与审计
当所有活跃设备都上报last_crypto_ver >= 2且 90 天无 ver=1 写操作 后,KMS 将旧 KEK 标记为 “禁用”(非删除),并生成 密钥轮换审计报告,供等保测评使用。
若需紧急回滚,只需把旧 KEK 重新启用,客户端在解密失败时自动回退到旧 DEK,业务零中断。
一句话总结:密码轮换完全在客户端完成,CouchDB 仅作为密文与加密元数据的存储载体,通过“密钥版本 + 信封加密”实现平滑过渡,既满足离线优先,又符合国内合规审计。
拓展思考
-
如果业务要求 “字段级加密”(如只加密手机号),而 crypto-pouch 默认全文档加密,你会如何改造?
提示:可引入 sub-cipher 插件,把敏感字段抽成子 doc,用相同信封策略,但需额外处理 子 doc 的 _rev 与父 doc 的一致性。 -
当政府客户要求 “国密 SM4/SM9” 替代 AES-GCM 时,crypto-pouch 的 WebAssembly 性能是否会成为瓶颈?
提示:在 React Native 端可 预置 SM4 硬件加速模块,并通过 JSCore 与原生层零拷贝 解决 200 MB 大附件的流式加密。 -
若未来引入 “量子计算威胁”,需要 后量子算法(如 Kyber) 保护 KEK,如何在不换 CouchDB 的前提下完成 双层密钥升级?
提示:把 Kyber 公钥直接放进_crypto.pq_kem字段,客户端在轮询时做 混合密钥交换,老设备不解读该字段即可向前兼容。