Сериализация CipherParams в строку и обратно

В криптографической библиотеке CryptoJS результат шифрования представлен не просто строкой, а структурой CipherParams. Это объект, который инкапсулирует ключевые данные: зашифрованный текст, соль, вектор инициализации (IV), алгоритм и другие параметры, необходимые для корректного восстановления исходного состояния.

Работа с CipherParams становится особенно важной тогда, когда зашифрованные данные нужно передавать между системами, сохранять в базе данных или сериализовать в текстовый формат (например, JSON или строку URL-safe). Именно на этом этапе возникает необходимость преобразования CipherParams в строку и обратно без потери информации.


Объект CipherParams в CryptoJS обычно содержит следующие поля:

  • ciphertext — основной зашифрованный блок (WordArray)
  • key — ключ шифрования (опционально)
  • iv — вектор инициализации
  • salt — соль (используется при деривации ключа)
  • algorithm — информация об алгоритме
  • formatter — объект форматирования вывода

На практике чаще всего используется только ciphertext, но для корректной расшифровки в сложных сценариях важны и остальные поля.


Преобразование CipherParams в строку

CryptoJS предоставляет встроенный механизм сериализации через форматтеры. Самый простой способ — использование метода toString().

Базовая сериализация

const encrypted = CryptoJS.AES.encrypt("секретные данные", "ключ");
const serialized = encrypted.toString();

По умолчанию используется формат OpenSSL-подобной строки, закодированной в Base64. Результат выглядит как непрозрачная строка:

U2FsdGVkX1+...

Эта строка уже содержит:

  • соль (если используется)
  • IV
  • ciphertext

Но важно понимать: структура скрыта внутри формата.


Форматы сериализации

CryptoJS использует концепцию Formatter — объект, который определяет, как CipherParams превращается в строку и обратно.

OpenSSL формат (по умолчанию)

CryptoJS.AES.encrypt("text", "passphrase").toString();

Он включает:

  • префикс Salted__
  • соль
  • зашифрованный текст

Пример внутренней структуры:

Salted__ + salt + ciphertext

Hex формат

Можно явно задать формат:

const encrypted = CryptoJS.AES.encrypt("text", "key");
const hex = encrypted.ciphertext.toString(CryptoJS.enc.Hex);

Однако это уже не полный CipherParams, а только ciphertext.

Чтобы сериализовать всё целиком:

const hexString = encrypted.toString(CryptoJS.format.Hex);

Base64 формат

const base64 = encrypted.toString(CryptoJS.format.Base64);

Base64 удобен для передачи через API, JSON и URL.


Десериализация строки обратно в CipherParams

Чтобы восстановить исходный объект, нужно использовать соответствующий форматтер.


Восстановление OpenSSL строки

const encrypted = CryptoJS.AES.encrypt("text", "key");
const serialized = encrypted.toString();

const parsed = CryptoJS.AES.decrypt(serialized, "key");
const original = parsed.toString(CryptoJS.enc.Utf8);

Здесь происходит:

  1. разбор строки
  2. извлечение salt и IV
  3. восстановление ключа
  4. расшифровка

Роль format.parse

Каждый форматтер в CryptoJS содержит метод parse, который превращает строку обратно в CipherParams.

Пример (условно):

const cipherParams = CryptoJS.format.OpenSSL.parse(serialized);

Результат:

{
  ciphertext: WordArray,
  salt: WordArray
}

После этого можно вручную расшифровать:

const key = CryptoJS.PBKDF2("password", cipherParams.salt, {
  keySize: 256/32,
});

const decrypted = CryptoJS.AES.decrypt(cipherParams, key);

Пользовательская сериализация

В реальных системах часто требуется собственный формат, например JSON.

Пример JSON-сериализации

const encrypted = CryptoJS.AES.encrypt("data", "key");

const json = JSON.stringify({
  ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
  iv: encrypted.iv && encrypted.iv.toString(),
  salt: encrypted.salt && encrypted.salt.toString()
});

Восстановление из JSON

const obj = JSON.parse(json);

const cipherParams = CryptoJS.lib.CipherParams.create({
  ciphertext: CryptoJS.enc.Base64.parse(obj.ciphertext),
  iv: CryptoJS.enc.Hex.parse(obj.iv),
  salt: CryptoJS.enc.Hex.parse(obj.salt)
});

const decrypted = CryptoJS.AES.decrypt(cipherParams, "key")
  .toString(CryptoJS.enc.Utf8);

Важный момент: разница между ciphertext и CipherParams

Частая ошибка — сериализовать только ciphertext:

encrypted.ciphertext.toString()

Это приводит к потере:

  • IV
  • salt
  • метаданных алгоритма

В результате расшифровка становится невозможной без внешнего контекста.


Безопасность сериализации

Сериализация CipherParams напрямую влияет на безопасность системы:

  • Base64 не шифрует данные, только кодирует
  • JSON-сериализация требует защиты отдельных полей
  • OpenSSL формат удобен, но менее гибок
  • кастомные форматы должны учитывать совместимость и целостность

Практическая схема хранения

На практике часто используют один из двух подходов:

1. Полный OpenSSL формат

const stored = encrypted.toString();

Плюсы:

  • минимальный код
  • полная совместимость
  • встроенная поддержка CryptoJS

Минусы:

  • слабая читаемость
  • сложность интеграции с другими системами

2. Явный JSON формат

{
  ct: "...",
  iv: "...",
  s: "..."
}

Плюсы:

  • контроль над структурой
  • удобство API
  • совместимость с другими языками

Минусы:

  • требуется ручной парсинг
  • выше вероятность ошибок

Типичная схема полного цикла

const encrypted = CryptoJS.AES.encrypt("hello", "password");

// сериализация
const serialized = encrypted.toString();

// передача / хранение

// восстановление
const decrypted = CryptoJS.AES.decrypt(serialized, "password");
const result = decrypted.toString(CryptoJS.enc.Utf8);

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

Частые проблемы:

  • несоответствие формата (Hex vs Base64)
  • потеря salt
  • неправильный ключ
  • ручное изменение строки
  • попытка расшифровать ciphertext без CipherParams

Когда требуется ручная работа с CipherParams

Ручная сериализация нужна в случаях:

  • межъязыковая совместимость (Java, Python, Go)
  • хранение в нестандартных БД схемах
  • криптографические протоколы поверх CryptoJS
  • кастомные API форматы

Итоговая логика работы

CipherParams — это не просто контейнер, а полноценное описание криптографического состояния. Сериализация превращает его в переносимый формат, а десериализация восстанавливает контекст, необходимый для корректной работы алгоритма шифрования и дешифрования.