JSON Web Encryption (JWE) в библиотеке jose реализует
модель, в которой полезная нагрузка не только подписывается, но и
полностью шифруется. В отличие от JWS, где обеспечивается целостность и
подлинность, JWE ориентирован на конфиденциальность данных.
Внутри спецификации выделяются ключевые компоненты:
В jose эта модель скрыта за высокоуровневым API
EncryptJWT.
EncryptJWTEncryptJWT используется для создания JWE в формате
Compact Serialization. Процесс включает последовательное формирование
объекта JWT с последующим шифрованием полезной нагрузки.
Класс строится вокруг цепочки методов:
Перед созданием зашифрованного токена требуется корректный ключ. В зависимости от выбранного алгоритма используются симметричные или асимметричные ключи.
Используется алгоритм dir, при котором один и тот же
ключ применяется для шифрования и дешифрования.
import { generateSecret } from 'jose'
const secret = await generateSecret('A256GCM')
Такой ключ подходит для сценариев внутри одной системы или доверенного окружения.
Для разделённых систем применяется публично-ключевая криптография.
import { generateKeyPair } from 'jose'
const { publicKey, privateKey } = await generateKeyPair('RSA-OAEP-256')
Публичный ключ используется для шифрования, приватный — для расшифровки.
Базовая структура формирования JWE через EncryptJWT
строится следующим образом:
import { EncryptJWT } from 'jose'
Payload представляет собой произвольный объект данных:
const payload = {
sub: 'user_123',
role: 'admin',
permissions: ['read', 'write']
}
Создание зашифрованного токена выполняется цепочкой методов:
const jwt = await new EncryptJWT(payload)
.setProtectedHeader({
alg: 'dir',
enc: 'A256GCM'
})
.setIssuedAt()
.setExpirationTime('2h')
.encrypt(secret)
Заголовок определяет криптографические параметры:
alg — алгоритм управления ключомenc — алгоритм шифрования содержимогоПример:
{
alg: 'dir',
enc: 'A256GCM'
}
Методы setIssuedAt и setExpirationTime
добавляют временные ограничения:
iat — время выпуска токенаexp — срок жизниДополнительно могут использоваться:
.setSubject('user_123')
.setIssuer('auth-service')
.setAudience('api')
Вызов encrypt(secret) инициирует:
Результат представляет собой строку вида:
header.encryptedKey.iv.ciphertext.tag
dir + A128GCMdir + A256GCMХарактеризуются минимальной задержкой и отсутствием обмена ключами.
RSA-OAEP-256 + A256GCMECDH-ES + A256GCMОбеспечивают безопасную передачу ключа CEK.
const jwt = await new EncryptJWT(payload)
.setProtectedHeader({
alg: 'RSA-OAEP-256',
enc: 'A256GCM'
})
.setIssuedAt()
.setExpirationTime('1h')
.encrypt(publicKey)
Расшифровка выполняется приватным ключом:
import { jwtDecrypt } from 'jose'
const { payload: decrypted } = await jwtDecrypt(jwt, privateKey)
ECDH-ES применяется для эфемерного обмена ключами.
const jwt = await new EncryptJWT(payload)
.setProtectedHeader({
alg: 'ECDH-ES',
enc: 'A256GCM'
})
.encrypt(publicKey)
Особенность заключается в генерации временного ключа для каждого сообщения.
Формат результата строго фиксирован:
BASE64URL(header).
BASE64URL(encryptedKey).
BASE64URL(iv).
BASE64URL(ciphertext).
BASE64URL(tag)
Каждая часть имеет криптографическое назначение и не может быть произвольно изменена без нарушения целостности.
Частые причины сбоев:
Пример типичной ошибки:
JWEInvalid: Invalid key or algorithm mismatch
JWE увеличивает размер данных из-за:
Ключи должны храниться:
В реальных системах EncryptJWT часто используется
совместно с:
jwtVerify — для подписанных токеновjwtDecrypt — для извлечения payloadSignJWT — для гибридных схем (подпись + шифрование
отдельно)Типичный поток:
Такой подход обеспечивает:
Библиотека jose использует WebCrypto API, что
обеспечивает:
RSA1_5)Дешифрование строго зависит от:
Любое изменение одного байта приводит к невозможности расшифровки без дополнительных ошибок восстановления.