Формат General JSON Serialization для JWS предназначен для случаев, когда одно и то же сообщение подписывается несколькими сторонами или несколькими ключами. В отличие от компактной сериализации (Compact JWS), где присутствует только одна подпись, здесь структура позволяет хранить массив подписей.
Структура General JWS:
{
"payload": "base64url-encoded payload",
"signatures": [
{
"protected": "base64url-encoded protected header",
"header": { "unprotected": "header" },
"signature": "base64url-encoded signature"
}
]
}
Ключевые элементы:
generalVerifyФункция generalVerify из библиотеки
jose выполняет проверку всех подписей, содержащихся в
General JWS. Она возвращает результат первой успешно проверенной подписи
или выбрасывает исключение, если ни одна подпись не прошла
валидацию.
Импорт:
import { generalVerify } from 'jose'
await generalVerify(jws, key, options)
Параметры:
import { generalVerify } from 'jose'
const jws = {
payload: 'SGVsbG8gd29ybGQ',
signatures: [
{
protected: 'eyJhbGciOiJIUzI1NiJ9',
signature: '...'
}
]
}
const secret = new TextEncoder().encode('secret-key')
const { payload, protectedHeader } = await generalVerify(jws, secret)
console.log(new TextDecoder().decode(payload))
console.log(protectedHeader)
Результат:
Uint8Array с декодированным
содержимымGeneral JWS может содержать несколько подписей:
{
"payload": "...",
"signatures": [
{ "protected": "...", "signature": "..." },
{ "protected": "...", "signature": "..." }
]
}
generalVerify:
Если ни одна подпись не валидна:
try {
await generalVerify(jws, key)
} catch (err) {
console.error('Все подписи недействительны')
}
Часто подписи могут быть созданы разными ключами. В этом случае используется функция-резолвер:
const keyResolver = async (protectedHeader, jws) => {
if (protectedHeader.kid === 'key1') {
return key1
}
if (protectedHeader.kid === 'key2') {
return key2
}
throw new Error('Unknown key')
}
await generalVerify(jws, keyResolver)
Аргументы функции:
Ограничение допустимых алгоритмов:
await generalVerify(jws, key, {
algorithms: ['HS256']
})
Если алгоритм подписи не входит в список — выбрасывается ошибка.
Заголовки crit требуют явного указания:
await generalVerify(jws, key, {
crit: ['exp']
})
Если критический заголовок присутствует, но не обработан — верификация завершится ошибкой.
Payload возвращается в виде Uint8Array.
Преобразование:
const text = new TextDecoder().decode(payload)
Если payload содержит JSON:
const data = JSON.parse(text)
Возможна работа с бинарными данными без преобразования:
const { payload } = await generalVerify(jws, key)
// payload остаётся Uint8Array
При отсутствии kid или явной привязки:
const keys = [key1, key2, key3]
const resolver = async () => keys
await generalVerify(jws, resolver)
Библиотека переберёт ключи для каждой подписи.
Типичные ошибки:
Пример:
try {
await generalVerify(jws, key)
} catch (err) {
if (err.code === 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {
// обработка ошибки подписи
}
}
Подключение удалённого набора ключей:
import { createRemoteJWKSet } from 'jose'
const JWKS = createRemoteJWKSet(new URL('https://example.com/.well-known/jwks.json'))
await generalVerify(jws, JWKS)
Особенности:
kidcompactVerify| Характеристика | generalVerify | compactVerify |
|---|---|---|
| Поддержка подписей | Множественные | Одна |
| Формат | JSON | Строка |
| Гибкость | Высокая | Ограниченная |
| Использование | Сложные сценарии | Простые сценарии |
При большом числе подписей:
kid для быстрого выбора ключаkid и источник ключейheader)protected)await generalVerify(jws, key, {
algorithms: ['RS256'],
crit: ['b64'],
complete: true
})
{
payload: Uint8Array,
protectedHeader: object
}
При использовании complete: true:
{
payload,
protectedHeader,
signature
}
Если требуется проверить каждую подпись:
for (const sig of jws.signatures) {
try {
await generalVerify(
{ payload: jws.payload, signatures: [sig] },
key
)
console.log('Подпись валидна')
} catch {}
}
kidprotected заголовки