用途
vetKeys(Verifiable Encrypted Threshold Keys,可验证加密门限密钥)通过 vetKD 协议为互联网计算机(IC)带来链上隐私:安全、按需的密钥派生,使公链能够持有并处理秘密数据。其密钥具备三个特性:可验证(用户可检查正确性、确认未被篡改)、加密(派生密钥在用户提供的传输密钥下加密,任何节点或容器都看不到原始密钥)、门限(由子网节点法定人数协作派生,无单一主体持有主密钥)。
典型用途包括:去中心化密钥管理(DKMS)、链上加密存储、私密消息、基于身份的加密(IBE)、时间锁加密、门限 BLS 以及可验证随机数。
主要能力与核心概念
- vetKey:由 (canister_id, context, input) 确定性派生的密钥材料,相同输入始终产生相同密钥。容器和子网节点都不会看到原始密钥,因为它在客户端本地解密前一直以客户端传输密钥加密。
- 传输密钥(Transport key):客户端生成的临时密钥对。公钥发送给容器,供 IC 加密派生密钥以便投递;只有持有对应私钥的客户端才能解密结果。
- Context:域分隔符 blob,用于按用例隔离派生子密钥,防止同一容器内密钥冲突,可理解为命名空间。
- Input:应用定义的数据,用于标识要派生哪个密钥(如用户 principal、文件 ID、聊天室 ID)。它以明文发送给管理容器,只能用作标识符,绝不能放入秘密数据。
- IBE(基于身份的加密):向某个身份(如 principal)使用派生公钥加密的方案。任何人都可以用容器的派生公钥向某 principal 加密,只有该 principal 能获取匹配的 vetKey 并解密,无需事先密钥交换或接收方在线。
系统 API(Candid)
vetKD API 让容器请求由门限协议派生的 vetKeys。派生是确定性的,相同输入始终产生相同密钥;不同输入产生不同密钥,容器可派生无限数量的唯一密钥。
vetkd_public_key:返回用于验证由 vetkd_derive_key 派生密钥的公钥。空 context 得到容器级主公钥;非空 context 得到该 context 的派生子密钥。在 IBE 中,该公钥让任何人都能向某身份加密。
vetkd_public_key : (record {
canister_id : opt canister_id;
context : blob;
key_id : record { curve : vetkd_curve; name : text };
}) -> (record { public_key : blob })
- canister_id:可选。省略(null)时返回调用容器的公钥;提供时返回该容器的密钥。
- context:域分隔符,含义与 vetkd_derive_key 中相同。
- key_id.curve:bls12_381_g2(唯一支持的曲线)。
- key_id.name:主密钥名称,test_key_1(本地 + 主网测试)或 key_1(生产)。
vetkd_derive_key:为给定 (context, input) 派生密钥材料,并在接收方传输公钥下加密返回。只有持有传输密钥者能解密。解密后的材料再按用例使用(例如通过 toDerivedKeyMaterial() 派生对称密钥,或用于 IBE 解密)。
vetkd_derive_key : (record {
input : blob;
context : blob;
transport_public_key : blob;
key_id : record { curve : vetkd_curve; name : text };
}) -> (record { encrypted_key : blob })
- input:用作密钥标识符的任意数据,不同输入产生不同派生密钥;无需随机,以明文发送给管理容器。
- context:域分隔符,必须与获取公钥时使用的 context 一致。
- transport_public_key:接收方公钥,派生密钥在其下加密以安全投递。
- 返回 encrypted_key:用传输密钥解密得到原始 vetKey,再按需使用(例如派生对称密钥;不要将原始字节直接用作 AES 密钥)。
容器 ID 与主密钥
- 管理容器 aaaaa-aa:暴露 vetkd_public_key 和 vetkd_derive_key 系统 API。它不是真实容器,而是系统级 API 端点。对 aaaaa-aa 的调用由系统路由到持有 key_id 指定主密钥的、启用 vetKD 的子网,由该子网节点执行门限密钥派生。你的容器可从任意子网调用。
- 链密钥测试容器 vrqyr-saaaa-aaaan-qzn4q-cai:仅用于测试的假 vetKD 实现(硬编码密钥、无门限),可在不支付生产 API 费用的情况下演练密钥派生。使用密钥名 insecure_test_key_1。不安全,仅限测试,切勿用于生产或敏感数据。也可从仓库自行部署实例。
主密钥名称与费用:
- test_key_1:本地 + 主网,用于开发与测试,主网约 10_000_000_000 cycles。
- key_1:主网,用于生产,约 26_153_846_153 cycles,子网 pzp6e(在 uzr34 上有备份)。
费用取决于主密钥所在子网(及其大小),而非调用容器所在子网。若容器可能被黑洞化或被其他容器使用,应发送多于当前成本的 cycles,以免未来子网规模增长导致调用失败;未使用的 cycles 会被退还。
使用方式
前置依赖:Rust 使用 ic-vetkeys = "0.6"(crates.io);Motoko 使用下方所示的原始管理容器方式;前端使用 @dfinity/vetkeys v0.4.0。
Rust(推荐使用 ic-vetkeys 高层库):Cargo.toml 依赖包括 candid 0.10、ic-cdk 0.19、serde 1、serde_bytes 0.11、ic-vetkeys 0.6、ic-stable-structures 0.7。使用 KeyManager(对 AccessControl 类型泛型,默认 AccessRights,使用稳定内存持久化访问控制状态),在 init 中配置 key_id 与域分隔符,并暴露 get_encrypted_vetkey 与 get_vetkey_verification_key 两个 update 方法。注意在 await 之前捕获 caller。
Rust(直接调用管理容器,较低层):定义 VetKdKeyId、VetKdCurve、请求/响应结构,通过 ic_cdk::api::call 调用 aaaaa-aa 的 vetkd_public_key(不需要 cycles),通过 call_with_payment128 调用 vetkd_derive_key(key_1 约 26B cycles,test_key_1 约 10B cycles)。
Motoko:mops.toml 依赖 core 2.0.0,通过 actor "aaaaa-aa" 声明 vetkd_public_key 与 vetkd_derive_key 的类型并直接调用。
适用场景
需要链上隐私与去中心化密钥管理的 IC 应用,例如加密存储、私密通信、IBE 消息、时间锁加密、门限签名与可验证随机数。
限制与常见错误
- vetKeys 是 IC 较新的特性,ic-vetkeys crate 与 @dfinity/vetkeys npm 包虽已发布,但 API 仍可能变化。请固定依赖版本,升级后查阅 DFINITY 论坛的迁移指南。
- 不要跨会话复用传输密钥,每个会话必须生成新的传输密钥对。
- 不要将 vetkd_derive_key 的原始输出直接用作加密密钥,它是加密 blob,必须用传输密钥解密得到 vetKey,再按用例处理(如通过 toDerivedKeyMaterial() 派生对称密钥)。
- 不要将 vetKD 与传统公钥密码混淆:没有每用户的静态密钥对,密钥按需从子网门限主密钥派生。
- 不要把秘密数据放入 input 字段,它以明文发送给管理容器。
- 不要忘记 vetkd_derive_key 是异步跨容器调用,消耗 cycles 且需要 await;建议在 await 前捕获 caller。
- context 使用要一致,前后端不一致会导致派生密钥不匹配、解密静默失败。
- 不要忘记为 vetkd_derive_key 附加足够 cycles(vetkd_public_key 不需要)。
- 若自行实现 IBE 而不做授权检查,容器必须确保 vetkd_derive_key 只向被授权调用者返回派生密钥,否则任何调用者都能请求任意派生密钥并解密他人消息。建议优先使用 ic-vetkeys / @dfinity/vetkeys 库而非自定义实现。