Сериализация и десериализация зашифрованных объектов

В SJCL зашифрованные данные обычно представляют собой структурированный JSON-объект, в котором объединены параметры криптографического контекста и непосредственно ciphertext. Такой формат делает возможной полную самодостаточность результата шифрования: без внешнего хранения IV, соли или параметров ключа.

Основная идея сериализации в SJCL заключается в том, что результат шифрования — это строка JSON, содержащая все необходимые данные для последующей расшифровки. Это позволяет безопасно передавать зашифрованные сообщения между системами, хранить их в базе данных или пересылать через небезопасные каналы связи без потери контекста.

При вызове:

const ciphertext = sjcl.encrypt(password, "секретное сообщение");

результатом будет строка, которая после парсинга JSON превращается в объект следующей структуры:

{
  "iv": "base64...",
  "v": 1,
  "iter": 10000,
  "ks": 128,
  "ts": 64,
  "mode": "ccm",
  "adata": "",
  "cipher": "aes",
  "salt": "base64...",
  "ct": "base64..."
}

Каждое поле играет строго определённую роль в процессе восстановления исходного текста.

iv (Initialization Vector)

Инициализационный вектор используется для обеспечения уникальности шифрования одного и того же текста при одинаковом ключе. Даже если входные данные не меняются, изменение IV приводит к полностью различному ciphertext.

IV в SJCL кодируется в Base64 и хранится прямо в объекте.

salt

Соль используется при выводе ключа из пароля (KDF — Key Derivation Function). Она предотвращает атаки с использованием радужных таблиц и делает невозможным предварительный расчёт ключей для популярных паролей.

SJCL генерирует случайную соль при каждом шифровании, что гарантирует уникальность производного ключа даже при одинаковом пароле.

iter

Количество итераций функции PBKDF2, используемой для получения ключа из пароля. Чем выше значение, тем медленнее вычисление ключа и тем выше устойчивость к brute-force атакам.

sjcl.encrypt(password, data, { iter: 20000 });

Увеличение iter напрямую влияет на стоимость атаки перебором.

ks (key size)

Размер ключа в битах. Обычно используется 128, 192 или 256 бит. В большинстве реализаций SJCL по умолчанию применяется 128-битный ключ.

ts (tag size)

Размер аутентификационного тега. Используется в режимах аутентифицированного шифрования (например, CCM). Чем больше значение, тем сложнее подделать ciphertext.

mode

Режим шифрования. SJCL поддерживает несколько режимов, наиболее часто используется:

  • "ccm" — Counter with CBC-MAC (аутентифицированное шифрование)
  • "ocb2" — более старый режим, но быстрее
  • "gcm" — в некоторых сборках

Режим определяет способ объединения конфиденциальности и целостности данных.

cipher

Алгоритм шифрования. В стандартной конфигурации SJCL используется:

  • "aes"

AES является базовым блочным шифром, на котором строится вся криптосистема библиотеки.

adata (associated data)

Дополнительные данные, которые не шифруются, но участвуют в проверке целостности. Используются для защиты структуры сообщения или метаданных.

Если adata изменяется после шифрования, расшифровка завершится ошибкой проверки целостности.

ct (ciphertext)

Основное зашифрованное содержимое. Представлено в Base64 и является результатом применения симметричного шифрования к исходному тексту.


Сериализация: как SJCL формирует JSON

Функция sjcl.encrypt выполняет несколько этапов:

  1. Генерация случайной соли
  2. Вычисление ключа через PBKDF2
  3. Генерация IV
  4. Шифрование данных AES
  5. Формирование аутентификационного тега
  6. Сборка объекта
  7. Сериализация в JSON строку

Именно последний шаг делает результат пригодным для хранения:

const encrypted = sjcl.encrypt("password", "data");
console.log(typeof encrypted); // "string"

Несмотря на то что внутри используется объектная структура, наружу библиотека возвращает строку.


Десериализация зашифрованных данных

Обратный процесс начинается с преобразования строки в объект:

const obj = sjcl.json.parse(encrypted);

или напрямую:

const decrypted = sjcl.decrypt("password", encrypted);

Внутри SJCL выполняет автоматический разбор JSON и извлекает все необходимые параметры.

Если используется ручной подход, десериализация выглядит так:

const data = JSON.parse(encrypted);

После этого структура становится доступной для низкоуровневой работы.


Полный цикл сериализации и десериализации

const encrypted = sjcl.encrypt("password", "секрет");

const decrypted = sjcl.decrypt("password", encrypted);

На уровне реализации происходит:

  • JSON → объект криптопараметров
  • Base64 → bitArray
  • PBKDF2 → ключ
  • AES → расшифровка
  • CCM проверка целостности

Внутренний формат и bitArray

SJCL активно использует внутренний тип bitArray, который отличается от привычных строк или Uint8Array.

При сериализации происходит преобразование:

  • бинарные данные → bitArray
  • bitArray → Base64 строка

Пример:

const bits = sjcl.codec.utf8String.toBits("hello");
const base64 = sjcl.codec.base64.fromBits(bits);

Именно такой механизм лежит в основе полей ct, iv, salt.


Совместимость сериализованных данных

Формат SJCL JSON считается стабильным, однако имеет зависимость от версии библиотеки.

Поле v (version) указывает версию формата:

"v": 1

При изменении алгоритмов или структуры сериализации возможна необходимость обратной совместимости через этот параметр.


Ручная модификация сериализованных объектов

Изменение любых полей внутри JSON приводит к изменению результата расшифровки. Особенно критичны:

  • iv
  • salt
  • ct
  • tag (внутренне в CCM)

Даже минимальное изменение Base64 строки делает объект невалидным.


Хранение сериализованных данных

Чаще всего SJCL-объекты сохраняются:

  • в localStorage
  • в базе данных как TEXT
  • в JSON API ответах

Пример:

localStorage.setItem("secureData", sjcl.encrypt(password, data));

И восстановление:

const decrypted = sjcl.decrypt(password, localStorage.getItem("secureData"));

Ошибки десериализации

Типичные причины:

  • неправильный пароль (ключ не совпадает)
  • повреждённый JSON
  • изменение iv или salt
  • несовпадение версии алгоритма
  • нарушение целостности CCM

SJCL в таких случаях выбрасывает исключение, не возвращая частично расшифрованные данные, что критично для безопасности.


Формат как самодостаточный криптоконтейнер

Сериализованный объект SJCL фактически является минималистичным криптоконтейнером, включающим:

  • параметры ключа
  • параметры шифрования
  • данные проверки целостности
  • зашифрованный payload

Такой подход избавляет от необходимости хранить метаданные отдельно и делает каждое сообщение автономным.