错误信息
secretOrPublicKey must be an asymmetric key when using ES256
含义
你让 jsonwebtoken 验证(或者签名——与之对应的另一条消息写的是 secretOrPrivateKey)一个 ES256 令牌,但你提供的密钥材料是对称密钥——一个既不是 PEM 也不是 KeyObject 的普通字符串或 Buffer。ES256 是基于 P-256 的 ECDSA:它需要 EC 密钥对,而不是共享密钥。
原因
配置中残留着 HMAC 时代的密钥
常见服务原本使用带共享密钥的 HS256,后来有人把 alg 改成了 ES256,而 JWT_SECRET 里仍然是“supersecret”。随机字符串不是 EC 密钥,jsonwebtoken 也不会假装它是。
接入了错误的配置变量
常见PEM 确实存在,但代码读取的变量里装的是别的东西——密钥 ID 或 JWKS URL(https://issuer/.well-known/jwks.json 是字符串,不是密钥)。变量为空时报出的是另一个错误:“secret or public key must be provided”。
PEM 在经过配置传递时被损坏了
常见如果 PEM 到达时带有字面的 \n 序列、被引号包裹,或者被 YAML 文件加了缩进,就无法再被解析为密钥,于是 jsonwebtoken 9 会把这个字符串当作共享密钥,并报出这个错误。传入之前,先恢复换行符并去掉引号。
在需要 PEM/KeyObject 的地方传入了 JWK
偶见JWK 是 JSON 对象;把它字符串化后交给 jwt.verify(),得到的字符串既不是 PEM 也不是 KeyObject。必须先转换(crypto.createPublicKey({ key: jwk, format: "jwk" }))。
修复方法
- 1.
加载真正的 EC 公钥
用 PEM(SPKI)或 KeyObject 验证 ES256。如果你手里是 JWK(例如来自 JWKS 端点),先用 node:crypto 转换。
js import { createPublicKey } from "node:crypto"; const key = createPublicKey({ key: jwk, format: "jwk" }); // or a PEM string jwt.verify(token, key, { algorithms: ["ES256"] }); - 2.
用辅助函数获取 JWKS 中的密钥
如果你的颁发者在 JWKS URL 上发布密钥,不要把 URL 当作密钥传入——应针对每个令牌按 kid 获取对应的密钥。
js import { createRemoteJWKSet, jwtVerify } from "jose"; const jwks = createRemoteJWKSet(new URL("https://issuer.example/.well-known/jwks.json")); const { payload } = await jwtVerify(token, jwks, { algorithms: ["ES256"] }); - 3.
或者:你其实想用的是 HS256
如果颁发者和验证方都是你自己的服务,并且有意使用共享密钥,就在两端都设置 algorithm: "HS256",而不是 ES256。这个错误要防止的正是混用这两类算法——包括经典的密钥混淆攻击场景。