Сообщение об ошибке
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"
Число после «saw» для ES256 колеблется от 70 до 72 (длина DER зависит от старших битов r и s); ES384 ожидает 96 байт, а ES512 — 132. Это обычный `TypeError`, а не `JsonWebTokenError`, поэтому проверки `instanceof` на ошибки jsonwebtoken его не перехватывают.
jsonwebtoken / jws / jwa (Node.js) — TypeError from ecdsa-sig-formatter
Что это означает
Сегмент подписи токена декодируется в 71 байт, а подпись JWS для ES256 должна занимать ровно 64 байта: это конкатенация r‖s в формате raw, определённая в RFC 7518, § 3.4. Значение длиной 70–72 байта с первым байтом `0x30` — это та же подпись в кодировке ASN.1 DER: в этом формате подпись по умолчанию выдают OpenSSL, Java, сервисы KMS и `crypto.sign()` в Node. Токен подписан кодом, который пропустил преобразование из DER в raw; jsonwebtoken (через ecdsa-sig-formatter) отклоняет его ещё до какой-либо проверки.
Почему это происходит
Самописный код подписи выдал DER
частоКод, который сам собирает JWT и подписывает его через `crypto.sign("sha256", data, key)` или `createSign().sign(key)`, получает подпись в DER, если не передать `dsaEncoding: "ieee-p1363"`. Токен везде выглядит нормально, пока проверяющая сторона, которая следует стандарту, не проверит длину подписи.
Токен подписан в KMS или HSM
часто`Sign` в AWS KMS и `asymmetricSign` в Google Cloud KMS возвращают подписи ECDSA в DER, поэтому для подписи JWT поверх них нужен явный шаг преобразования. (Azure Key Vault и `CKM_ECDSA` в PKCS#11 сразу возвращают raw r‖s — прежде чем преобразовывать, проверьте, что указано в документации вашего провайдера.)
Кодировка по умолчанию в другом языке
иногда`SHA256withECDSA` в Java, `sign()` в Python-библиотеке cryptography, `ecdsa.SignASN1` в Go и OpenSSL CLI выдают DER. Библиотеки, созданные для JOSE, преобразуют подпись автоматически, а универсальные криптографические вызовы — нет.
Сегмент подписи повреждён
редкоЕсли указанная в сообщении длина не попадает в диапазон 70–72 (например, «saw 2»), третий сегмент обрезан или подменён, а не закодирован иначе.
Как исправить
- 1.
Запросите у node:crypto формат raw r‖s
С `dsaEncoding: "ieee-p1363"` Node выдаёт подпись в формате фиксированной длины, которого требует JWS. Ещё лучше подписывать библиотекой JOSE (`jwt.sign` из jsonwebtoken, `SignJWT` из jose): она сделает это за вас.
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.
Преобразуйте DER из KMS перед сборкой токена
`ecdsa-sig-formatter` (устанавливается вместе с jsonwebtoken через jws/jwa) преобразует DER в формат JOSE. Конвертер DER ⇄ raw показывает 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;