错误信息
TypeError: Failed to execute 'importKey' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView or JsonWebKey)'.
也可能输出为
- TypeError: SubtleCrypto.importKey: Argument 2 is not an object.
- TypeError: Type error
上面是 Chromium 的文本;Firefox 报“SubtleCrypto.importKey: Argument 2 is not an object.”,Safari/WebKit 只报“Type error”。对于同样的调用,Node.js 报告的是 `ERR_INVALID_ARG_TYPE`。
WebCrypto SubtleCrypto.importKey — PEM text passed as keyData (Chromium / Firefox / WebKit)
含义
`importKey` 的 `keyData` 参数收到的是字符串。WebCrypto 从不解析文本:对于 `"spki"`、`"pkcs8"` 和 `"raw"`,它要求二进制 DER(`ArrayBuffer` 或类型化数组);对于 `"jwk"`,它要求 JavaScript 对象。浏览器在参数转换阶段就拒绝了这次调用,还没有查看格式或密钥——所以 PEM 本身可能完全有效。
原因
直接传入了 PEM 字符串
常见来自服务器、配置文件或某个 JWT 库文档的 `-----BEGIN PUBLIC KEY-----` 文本。必须先对它做 base64 解码,得到 DER。
传入 Base64 文本而不是字节
常见即使去掉了首尾的封装行,base64 字符串仍然是字符串。用 `atob`(或在支持时用 `Uint8Array.fromBase64`)把它解码为字节。
以 JSON 文本形式传入的 JWK
偶见对于 `"jwk"` 格式,经过 `JSON.stringify` 处理的文本同样会失败;应传入解析后的对象。
修复方法
- 1.
去掉封装行并解码为字节
删除 BEGIN/END 行和空白字符,做 base64 解码,然后根据标签选择格式:`PUBLIC KEY` → `spki`,`PRIVATE KEY` → `pkcs8`。
js function pemToDer(pem) { const base64 = pem.replace(/-----(BEGIN|END) [A-Z ]+-----/g, "").replace(/\s+/g, ""); return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); } const publicKey = await crypto.subtle.importKey( "spki", pemToDer(publicKeyPem), // "PUBLIC KEY" -> spki, "PRIVATE KEY" -> pkcs8 { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"], ); - 2.
以对象形式传入 JWK
JWKS 端点返回的本来就是对象;JSON 文本只需解析一次,然后把对象连同相匹配的曲线一起传入。
js const jwk = JSON.parse(jwkText); // an object, not the JSON string const key = await crypto.subtle.importKey("jwk", jwk, { name: "ECDSA", namedCurve: jwk.crv }, false, ["verify"]);