Компактное представление JWE (JSON Web Encryption) — это строка, состоящая из пяти частей, разделённых точками:
Пример структуры:
xxxxx.yyyyy.zzzzz.aaaaa.bbbbb
Каждая часть закодирована в Base64URL. В контексте расшифровки важно
понимать, что библиотека jose берет на себя разбор
структуры, проверку алгоритмов и выполнение криптографических
операций.
Для выполнения расшифровки требуется ключ, соответствующий алгоритму шифрования. В зависимости от того, как был сформирован JWE, используется один из типов ключей:
A256GCM)RSA-OAEP,
RSA-OAEP-256)CryptoKeyБиблиотека jose поддерживает работу с Web Crypto API и
Node.js crypto модулем.
В библиотеке jose для расшифровки компактного JWE
используется функция:
import { compactDecrypt } from 'jose'
Сигнатура:
compactDecrypt(jwe, key, options?)
Возвращает объект:
plaintext — расшифрованные данные (Uint8Array)protectedHeader — заголовок JWEimport { compactDecrypt } from 'jose'
const jwe = 'eyJhbGciOi...'
const privateKey = {
kty: 'RSA',
n: '...',
e: '...',
d: '...',
p: '...',
q: '...',
dp: '...',
dq: '...',
qi: '...'
}
const { plaintext, protectedHeader } = await compactDecrypt(jwe, privateKey)
const decoded = new TextDecoder().decode(plaintext)
console.log(protectedHeader)
console.log(decoded)
Чаще всего JWE создаётся с использованием RSA-OAEP, где шифруется симметрический ключ, а затем данным ключом шифруется payload.
При расшифровке:
Этот процесс полностью инкапсулирован внутри
compactDecrypt.
В современных окружениях предпочтительно использовать
CryptoKey.
import { compactDecrypt } from 'jose'
import { importPKCS8 } from 'jose/key/import'
const pkcs8 = `
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
`
const privateKey = await importPKCS8(pkcs8, 'RSA-OAEP-256')
const jwe = 'eyJhbGciOi...'
const { plaintext } = await compactDecrypt(jwe, privateKey)
console.log(new TextDecoder().decode(plaintext))
После расшифровки доступен заголовок JWE:
const { protectedHeader } = await compactDecrypt(jwe, privateKey)
Типичный содержимое:
{
"alg": "RSA-OAEP-256",
"enc": "A256GCM",
"typ": "JWE"
}
Значения:
alg — алгоритм шифрования ключаenc — алгоритм шифрования контентаtyp — тип токенаЭти данные критичны для проверки корректности входного токена.
При необходимости можно ограничить допустимые алгоритмы через параметры:
await compactDecrypt(jwe, privateKey, {
keyManagementAlgorithms: ['RSA-OAEP-256'],
contentEncryptionAlgorithms: ['A256GCM']
})
Это защищает от атак, связанных с подменой алгоритма.
Расшифровка может завершиться ошибкой по нескольким причинам:
Пример обработки:
try {
const { plaintext } = await compactDecrypt(jwe, privateKey)
console.log(new TextDecoder().decode(plaintext))
} catch (err) {
console.error('Ошибка расшифровки JWE:', err)
}
plaintext возвращается в виде Uint8Array.
Для преобразования в строку используется TextDecoder:
const decoded = new TextDecoder().decode(plaintext)
Если данные представляют JSON:
const json = JSON.parse(decoded)
На практике встречаются следующие комбинации:
alg: RSA-OAEP-256enc: A256GCMalg: ECDH-ESenc: A256GCMalg: direnc: A128GCM, A256GCMВ случае dir никакого шифрования ключа нет —
используется общий секрет.
import { compactDecrypt } from 'jose'
const secret = new TextEncoder().encode('super-secret-key-32bytes-long!!')
const { plaintext } = await compactDecrypt(jwe, secret)
console.log(new TextDecoder().decode(plaintext))
protectedHeader обязательна перед
использованием данныхЛогика внутри вызова можно представить так:
Все шаги выполняются внутри compactDecrypt, но понимание
этой цепочки важно для отладки и архитектуры систем безопасности
Расшифрованные данные не буферизуются как строки сразу — это бинарный поток. Это позволяет безопасно работать с большими сообщениями без лишнего копирования памяти.
alg и типа ключаTextDecoderВ серверной архитектуре compactDecrypt часто
используется как промежуточный слой:
Это обеспечивает переносимость секретных данных без раскрытия содержимого в транспортном уровне