JWK thumbprint представляет собой детерминированный идентификатор JSON Web Key, вычисляемый по строго определённым правилам RFC 7638. В контексте криптографических систем он используется для сравнения ключей, кэширования, привязки ключей к сущностям и проверки соответствия ключевых материалов без необходимости сравнивать их полностью.
В библиотеке jose для JavaScript работа с thumbprint реализована
через функцию calculateJwkThumbprint, которая позволяет
получить компактное представление JWK в виде хеша, нормализованного по
стандарту.
Алгоритм вычисления основан на нескольких жёстких правилах:
Ключевой момент — идентичность результата для одного и того же ключа независимо от порядка полей в исходном объекте.
Разные типы JWK используют разные обязательные поля:
Используются параметры:
ektynИспользуются параметры:
crvktyxyИспользуются параметры:
crvktyxЛюбые дополнительные поля игнорируются при расчёте thumbprint.
В jose функция calculateJwkThumbprint принимает JWK и
возвращает строку thumbprint.
import { calculateJwkThumbprint } from 'jose'
const jwk = {
kty: 'RSA',
n: '0vx7agoebGcQSuuPiLJXZptN9...',
e: 'AQAB',
alg: 'RS256',
kid: 'key-1'
}
const thumbprint = await calculateJwkThumbprint(jwk)
console.log(thumbprint)
Результат представляет собой строку base64url.
Процесс можно разложить на несколько этапов.
Из JWK удаляются все служебные поля:
algusekey_opskidОстаются только поля, определённые спецификацией для конкретного типа ключа.
Создаётся объект с фиксированным порядком ключей:
{"e":"...","kty":"RSA","n":"..."}
Важно, что порядок всегда одинаковый, независимо от входного объекта.
JSON преобразуется в строку без пробелов:
{"e":"AQAB","kty":"RSA","n":"..."}
Любое отличие в сериализации приводит к изменению результата, поэтому процесс строго стандартизирован.
Полученная строка преобразуется в байты и хешируется алгоритмом SHA-256.
Внутренне используется криптографический API Node.js или WebCrypto в зависимости от среды выполнения.
Финальный шаг — преобразование бинарного хеша в base64url:
+ заменяется на -/ заменяется на _= в концеИтог — компактная строка фиксированной длины.
RSA ключ:
const jwk = {
kty: 'RSA',
n: 'sXchpQ...',
e: 'AQAB',
use: 'sig',
kid: 'demo-key'
}
После обработки остаётся:
{"e":"AQAB","kty":"RSA","n":"sXchpQ..."}
Далее:
Thumbprint используется для сравнения ключей без прямого сопоставления их структуры.
import { calculateJwkThumbprint } from 'jose'
const thumb1 = await calculateJwkThumbprint(jwkA)
const thumb2 = await calculateJwkThumbprint(jwkB)
if (thumb1 === thumb2) {
// ключи эквивалентны
}
Такой подход особенно полезен при работе с наборами JWK из разных источников.
В системах JWT thumbprint часто применяется как:
kid (Key ID)Пример генерации kid на основе thumbprint:
import { calculateJwkThumbprint } from 'jose'
const kid = await calculateJwkThumbprint(jwk)
const key = { ...jwk, kid }
Чувствительность к параметрам n и e делает
thumbprint стабильным идентификатором публичной части ключа.
Кривые (P-256, P-384, P-521) влияют на вычисление через поле
crv, что делает thumbprint уникальным для каждой кривой и
точки.
В Ed25519 ключах используется только координата x, что
упрощает структуру и делает вычисление более компактным.
Типичные проблемы при вычислении:
n, e,
x, y)ktyd,
p, q), которые игнорируютсяВ случае некорректного ключа функция выбрасывает исключение.
Thumbprint не является подписью и не предназначен для защиты данных. Его свойства:
Он выполняет роль идентификатора, а не механизма безопасности.
В реальных системах thumbprint часто используется:
Особенно важно его использование в распределённых системах, где ключи приходят из внешних JWKS endpoints.
Поле kid может быть произвольным и не
стандартизированным, тогда как thumbprint:
Поэтому thumbprint часто используется как основа для генерации
kid, а не наоборот.