错误信息
DataError: Data provided to an operation does not meet requirements
应该匹配的是 DOMException 名称 DataError;消息文本因运行时而异。Firefox 和 Safari 显示上面那句通用的话;Chrome 常常让消息为空,或者指出原因(“The imported EC key specifies a different curve than requested”);Node 打印“Invalid keyData”或“Named curve mismatch”之类的具体原因。
WebCrypto crypto.subtle.importKey — browsers & Node
含义
importKey(或 unwrapKey)拒绝了这些密钥字节:它们无法按你声明的格式解析,或者其内容与算法参数相矛盾(曲线不对、JWK 字段不匹配)。这相当于 WebCrypto 在说“这些字节不是你所说的那个密钥”——在创建任何密钥对象之前就会抛出。
原因
格式名称与字节内容不符
常见用“pkcs8”格式(私钥容器)导入公钥,或者用“spki”导入私钥,是最常见的弄反情况。这些名称并不直观:spki = 公钥,pkcs8 = 私钥,raw = 裸 EC 点,jwk = JSON 对象。
PEM 文本没有转换为二进制
常见importKey 接受的是 DER 的 ArrayBuffer,而不是 PEM 文本。传入按 UTF-8 编码的 PEM 字符串——或者在没有先去掉 BEGIN/END 行和换行符的情况下对它做 base64 解码——得到的字节都无法通过结构解析。
密钥与算法的曲线不匹配
偶见用 namedCurve: "P-384" 导入 P-256 SPKI(Node 会报告“Named curve mismatch”),或者 JWK 的 crv 与请求的算法不一致。字节没有问题,错的是声明。Firefox 在导入时不检查 SPKI 的这种情况,所以同样的代码在 Firefox 中能通过,在 Chrome、Safari 和 Node 中却会失败。
raw 格式的点无效
偶见对于“raw”格式,这 65 个字节必须是 0x04 ‖ X ‖ Y,并且 (X, Y) 确实位于曲线上;Chrome、Firefox 和 Node 会在导入时拒绝损坏的点,而在我们的测试中,Safari 的引擎没有任何报错就导入了它。压缩点(33 字节,前缀为 0x02/0x03)则是反方向的可移植性陷阱:Chrome、Firefox 和 Node 接受它们,Safari 却会拒绝并报出这个错误。
修复方法
- 1.
正确完成 PEM → ArrayBuffer 转换
去掉封装行,对正文做 base64 解码,然后根据 PEM 标签选择格式:PUBLIC KEY → spki,PRIVATE KEY → pkcs8。
js function pemToArrayBuffer(pem) { const b64 = pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, ""); return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)).buffer; } const key = await crypto.subtle.importKey( "spki", pemToArrayBuffer(publicKeyPem), { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"], ); - 2.
如果手头是 JSON 密钥材料,优先使用 JWK
JWKS 端点提供的本来就是可直接用于 WebCrypto 的对象——无需二进制转换,crv 也随密钥一起提供。
js const key = await crypto.subtle.importKey( "jwk", jwk, { name: "ECDSA", namedCurve: jwk.crv }, false, ["verify"], ); - 3.
确认这些字节究竟是什么
把密钥粘贴到下方的证书解码器或签名验证工具中——两者都会在本地解析 SPKI/PKCS#8/JWK,并报告实际的曲线和密钥类型,通常一眼就能看出哪里不匹配。