跳到主要内容
ecdsa.com
jsonwebtoken / jws / jwa (Node.js)JWT 库

错误信息

TypeError: "ES256" signatures must be "64" bytes, saw "71"

也可能输出为

  • "ES256" signatures must be "64" bytes, saw "70"
  • "ES256" signatures must be "64" bytes, saw "72"
  • "ES384" signatures must be "96" bytes, saw "103"
  • "ES512" signatures must be "132" bytes, saw "139"

对于 ES256,“saw”后面的数字在 70 到 72 之间变化(DER 长度取决于 r 和 s 的高位);ES384 要求 96 字节,ES512 要求 132 字节。这个错误是普通的 `TypeError`,而不是 `JsonWebTokenError`,因此针对 jsonwebtoken 错误的 `instanceof` 检查捕获不到它。

jsonwebtoken / jws / jwa (Node.js) — TypeError from ecdsa-sig-formatter

含义

令牌的签名段解码后为 71 字节,但 ES256 JWS 签名必须恰好是 64 字节,即 RFC 7518 §3.4 定义的原始拼接 r‖s。以 `0x30` 开头、长度为 70~72 字节的值,是同一签名的 ASN.1 DER 编码——OpenSSL、Java、KMS 服务和 Node 的 `crypto.sign()` 默认生成的就是这种格式。签发该令牌的签名方跳过了从 DER 到原始格式的转换;jsonwebtoken(通过 ecdsa-sig-formatter)在任何验证之前就拒绝了它。

原因

修复方法

  1. 1.

    让 node:crypto 输出原始 r‖s

    `dsaEncoding: "ieee-p1363"` 会让 Node 输出 JWS 要求的定长格式。更好的做法是用 JOSE 库签名(jsonwebtoken 的 `jwt.sign`、jose 的 `SignJWT`),它们会替你完成这一步。

    js
    import { sign } from "node:crypto";
    
    const signature = sign("sha256", Buffer.from(signingInput), {
      key: privateKey,
      dsaEncoding: "ieee-p1363", // raw r||s: 64 bytes for P-256, as RFC 7518 §3.4 requires
    });
    const token = signingInput + "." + signature.toString("base64url");
  2. 2.

    组装令牌之前,先转换 KMS 返回的 DER

    `ecdsa-sig-formatter`(随 jsonwebtoken 经由 jws/jwa 一并安装)可以把 DER 转换为 JOSE 格式。DER ⇄ 原始格式转换器会显示两种形式下的 r 和 s,方便你确认转换结果。

    js
    import ecdsaSigFormatter from "ecdsa-sig-formatter";
    
    // AWS KMS and Google Cloud KMS return DER; convert once before assembling the JWS.
    const derSignature = Buffer.from(kmsResponse.Signature);
    const joseSignature = ecdsaSigFormatter.derToJose(derSignature, "ES256"); // base64url, 64 bytes decoded
    const token = signingInput + "." + joseSignature;

相关错误

浏览完整的签名错误库