错误信息
Invalid JWK EC key
Node.js crypto — TypeError, code ERR_CRYPTO_INVALID_JWK
含义
使用 format: "jwk" 的 createPublicKey/createPrivateKey 拒绝了这个 JWK 对象:缺少必需的成员、坐标无法解码,或者点 (x, y) 实际上并不在指定的命名曲线上。Node 在导入时会验证这个点,所以损坏或被截断的坐标会在这里被发现,而不会生成一个永远验证不通过的密钥。无论 JWK 来自 JWKS 端点、配置文件还是其他服务,都会执行同样的检查——这个错误指向的是对象本身,而不是传输过程。
原因
坐标不是 base64url
常见JWK 的 x/y/d 必须是不带填充的 base64url(RFC 7518)。十六进制字符串和十进制整数解码后都得不到 P-256 应有的 32 字节。Node 对其中一种变体比较宽容:使用 + / 字符和 = 填充的标准 base64 可以顺利导入,而浏览器会以 DataError 拒绝同一个 JWK——在服务器上可用的密钥,在客户端仍可能被拒绝。
坐标被截断或损坏
常见坐标在传输中丢了一个字符或被改了一个字符,表示的就是另一个数,点也就不在曲线上了。经典的前导零 bug——编码器因为首字节为零而只写出 31 字节——是 Node 唯一能容忍的截断:数值没有变,所以密钥可以导入。浏览器会拒绝同一个 JWK(提示“should be 32”字节),所以这个问题无论如何都值得修复。
crv 与密钥材料不符
偶见标着 crv: "P-256"、却带着 secp256k1 或 P-384 坐标的 JWK(手动编辑或从模板复制粘贴后出现),不可能描述所声明曲线上的点。
修复方法
- 1.
导入前先校验 JWK 的结构
检查成员和解码后的长度。对于 P-256,x 和 y 解码后都必须恰好是 32 字节。
js const x = Buffer.from(jwk.x, "base64url"); const y = Buffer.from(jwk.y, "base64url"); console.log(jwk.kty, jwk.crv, x.length, y.length); // EC P-256 32 32 - 2.
在过短的坐标左侧补零
如果生成方去掉了前导零,在构造 JWK 之前先恢复固定长度。
js const pad32 = (b) => Buffer.concat([Buffer.alloc(32 - b.length), b]); jwk.x = pad32(Buffer.from(jwk.x, "base64url")).toString("base64url"); jwk.y = pad32(Buffer.from(jwk.y, "base64url")).toString("base64url"); const key = crypto.createPublicKey({ key: jwk, format: "jwk" }); - 3.
从可信来源重新生成
如果坐标校验不通过,而 JWK 又不是你自己构造的,就从颁发者的 JWKS 端点重新获取,而不是去修补字节——需要动手术才能用的 JWK,本来就不该信任。