错误信息
InvalidAccessError: Unable to use this key to verify
这是 Node 的措辞(sign 报“Unable to use this key to sign”,使用其他算法的密钥则报“Key algorithm mismatch”)。浏览器的表述各不相同——Chrome 为“key.usages does not permit this operation”,Firefox 为“A parameter or an operation is not supported by the underlying object”,Safari 为“CryptoKey doesn't support verification”——但 DOMException 名称 InvalidAccessError 在各处都相同。
WebCrypto crypto.subtle.sign / verify — browsers & Node
含义
你传入的密钥对象不允许执行这个操作:要么它的 usages 数组中没有这个操作,要么密钥类别不对(用私钥验证,或用公钥签名),要么密钥所属的算法与调用要求的算法不同。密钥材料本身可能完全有效——有问题的是它的权限或角色。
原因
导入时 usages 中没有包含该操作
常见WebCrypto 密钥遵循最小权限原则:importKey(..., ["verify"]) 生成的密钥只能用于验证。用 [] 或 ["sign"] 导入后再调用 verify(),就会在这里失败——用途在创建时就已固定,之后无法添加。
公钥和私钥用反了
常见subtle.sign 需要私钥;subtle.verify 需要公钥。把 keyPair.publicKey 传给 sign(或把 privateKey 传给 verify)属于角色错误,API 会直接拒绝。
密钥的算法与操作的算法不一致
偶见在 ECDSA 签名调用中使用 ECDH 密钥(或者反过来),即使曲线相同也会失败——WebCrypto 把密钥绑定到创建时所针对的算法,因此“一个 EC 密钥两用”的设计必须把密钥材料导入两次。
修复方法
- 1.
声明将要用到的用途
生成或导入时列出每一项计划执行的操作;如果一个密钥要承担多个角色,就用不同的用途把同一份密钥材料重新导入。
js const keyPair = await crypto.subtle.generateKey( { name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"], // privateKey gets "sign", publicKey gets "verify" ); - 2.
为每个调用传入密钥对中正确的那一半
调试时查看 key.type 和 key.usages——它们准确说明了该对象允许做什么。
js console.log(key.type, key.usages); // e.g. "public" ["verify"] const sig = await crypto.subtle.sign(alg, keyPair.privateKey, data); const ok = await crypto.subtle.verify(alg, keyPair.publicKey, sig, data);