JWE (JSON Web Encryption) описывает формат защищённого сообщения, в котором данные шифруются и, при необходимости, дополнительно аутентифицируются. В экосистеме JavaScript для работы с JWE широко используется библиотека Jose, реализующая спецификации JOSE (JSON Object Signing and Encryption).
В отличие от компактной сериализации, JSON-сериализация JWE предназначена для сценариев, где требуется более гибкая структура: работа с несколькими получателями, раздельное представление ключевых материалов и расширяемость метаданных.
JSON-сериализация JWE существует в двух вариантах:
Оба формата описывают один и тот же криптографический результат, но различаются структурой представления.
Любая JWE-структура включает следующие элементы:
Дополнительно могут использоваться:
Flattened-формат применяется, когда у JWE ровно один получатель. Это упрощённое представление, в котором данные о получателе находятся на верхнем уровне объекта.
{
"protected": "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0",
"encrypted_key": "gW7a9...example...",
"iv": "48V1_ALb6US04U3b",
"ciphertext": "5eym8...example...",
"tag": "XFBoMYUZodetZdvTiFvSkQ"
}
Пример шифрования через Jose:
import { JWE, generateKeyPair } from 'jose'
const { publicKey } = await generateKeyPair('RSA-OAEP-256')
const encoder = new TextEncoder()
const jwe = await new JWE.Encrypt(
encoder.encode('секретные данные')
)
.setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' })
.encrypt(publicKey)
console.log(jwe)
Результат будет в формате Flattened JSON Serialization.
General JSON Serialization используется при необходимости шифрования для нескольких получателей. Это расширенный формат, в котором каждый получатель описывается отдельно.
{
"protected": "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0",
"recipients": [
{
"encrypted_key": "abc123...recipient1..."
},
{
"encrypted_key": "def456...recipient2..."
}
],
"iv": "48V1_ALb6US04U3b",
"ciphertext": "5eym8...example...",
"tag": "XFBoMYUZodetZdvTiFvSkQ"
}
encrypted_keyFlattened:
encrypted_key на верхнем уровнеGeneral:
recipientsFlattened:
General:
В General JSON Serialization каждый получатель получает свой вариант CEK, зашифрованный его публичным ключом.
Процесс:
recipientsimport { JWE, generateKeyPair } from 'jose'
const encoder = new TextEncoder()
const { publicKey: publicKey1 } = await generateKeyPair('RSA-OAEP-256')
const { publicKey: publicKey2 } = await generateKeyPair('RSA-OAEP-256')
const jwe = await new JWE.Encrypt(
encoder.encode('общие секретные данные')
)
.setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM' })
.addRecipient(publicKey1)
.addRecipient(publicKey2)
.encrypt()
console.log(JSON.stringify(jwe, null, 2))
Результат автоматически будет представлен в General JSON Serialization.
Содержит параметры, влияющие на криптографическую обработку:
Этот заголовок всегда включается в вычисление аутентификационного тега.
Используется для метаданных, которые не участвуют в криптографической защите:
AAD позволяет включать дополнительные данные в процесс аутентификации без их шифрования.
Пример:
jwe.setAdditionalAuthenticatedData(
encoder.encode('metadata')
)
Эти данные не попадают в ciphertext, но защищаются тегом.
Библиотека Jose автоматически:
Flattened формат не поддерживает массив recipients. Попытка добавить второго получателя приводит к ошибке или невалидному JWE.
Неверная комбинация:
Любое изменение protected после шифрования делает токен недействительным.