Тестирование совместимости с серверной стороной

Клиентские библиотеки шифрования, такие как Crypto-js, часто используются в связке с серверной логикой, реализованной на других языках: Node.js, Python, Java, PHP. Несмотря на использование одинаковых алгоритмов (AES, SHA-256 и др.), различия в форматах данных, способах кодирования и параметрах шифрования могут приводить к несовместимости.

Ключевые источники проблем:

  • различия в представлении ключей и векторов инициализации (IV)
  • несоответствие режимов работы алгоритма (CBC, CFB, GCM и т.д.)
  • различия в паддинге (PKCS7, ZeroPadding и др.)
  • кодировки (UTF-8, Hex, Base64)
  • сериализация результатов (строки, байтовые массивы)

Проверка идентичности алгоритмов

Первый этап тестирования — подтверждение того, что клиент и сервер используют один и тот же алгоритм с одинаковыми параметрами.

Пример AES (CBC + PKCS7):

Crypto-js по умолчанию:

  • режим: CBC
  • паддинг: PKCS7

На сервере необходимо явно указать те же параметры.

Node.js (crypto):

crypto.createCipheriv('aes-256-cbc', key, iv);

Python (PyCryptodome):

AES.new(key, AES.MODE_CBC, iv)

Любое расхождение делает результат несовместимым.


Согласование форматов данных

Crypto-js активно использует собственные структуры (WordArray). При передаче данных на сервер требуется преобразование.

Основные форматы:

  • Hex
  • Base64
  • UTF-8

Пример преобразования:

const encrypted = CryptoJS.AES.encrypt("text", key, { iv: iv });
const base64 = encrypted.toString(); // Base64

На сервере необходимо:

  1. декодировать Base64
  2. извлечь IV (если он включён)
  3. расшифровать данные

Работа с ключами

Crypto-js допускает два способа задания ключа:

  1. строка (пароль)
  2. бинарный ключ (WordArray)

Особенность: если передаётся строка, Crypto-js автоматически применяет алгоритм OpenSSL EVP_BytesToKey для генерации ключа и IV.

Это критически важно.

Рекомендация для совместимости:

  • использовать явно заданные ключ и IV
  • избегать передачи “пароля” без контроля derivation

Пример корректного подхода:

const key = CryptoJS.enc.Hex.parse("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
const iv = CryptoJS.enc.Hex.parse("00112233445566778899aabbccddeeff");

CryptoJS.AES.encrypt("text", key, { iv: iv });

Проверка паддинга

Crypto-js по умолчанию использует PKCS7. На сервере необходимо выбрать тот же метод.

Ошибки при несовпадении:

  • некорректная длина блока
  • ошибка дешифрования
  • “мусор” в конце строки

Формат выходных данных Crypto-js

Результат шифрования представляет собой объект:

{
  ciphertext: WordArray,
  salt: WordArray (опционально),
  iv: WordArray (если задан)
}

Метод .toString() возвращает строку в формате OpenSSL:

U2FsdGVkX1...

Это:

  • Base64
  • включает salt
  • использует специальный префикс Salted__

Сервер должен уметь:

  1. распознать префикс
  2. извлечь salt
  3. повторить derivation ключа

Или — отключить этот механизм и передавать всё явно.


Стратегии тестирования

1. Тест “клиент → сервер”

  • зашифровать данные в Crypto-js
  • отправить на сервер
  • расшифровать
  • сравнить с оригиналом

2. Тест “сервер → клиент”

  • зашифровать на сервере
  • передать в браузер
  • расшифровать через Crypto-js

3. Проверка бинарной идентичности

Важно не только совпадение текста, но и байтов.

Сравнение:

  • Hex
  • Base64

Набор контрольных тестов

Для надёжной проверки создаются фиксированные тест-кейсы:

Пример:

  • текст: "Hello World"
  • ключ: 001122...
  • iv: aabbcc...
  • ожидаемый ciphertext: Base64 строка

Эти значения фиксируются и используются во всех средах.


Частые ошибки

1. Разная кодировка строки

Crypto-js:

CryptoJS.enc.Utf8.parse("text")

Если сервер использует ASCII или Latin1 — данные не совпадут.


2. Использование пароля вместо ключа

CryptoJS.AES.encrypt("text", "password")

На сервере это не будет работать без повторения derivation алгоритма.


3. Потеря IV

Если IV не передан:

  • расшифрование невозможно
  • результат будет некорректным

4. Неправильный режим

AES-ECB и AES-CBC дают разные результаты даже с одинаковыми ключами.


5. Ошибки Base64

Некоторые серверные библиотеки:

  • добавляют переносы строк
  • используют URL-safe Base64

Это ломает совместимость.


Практика: совместимость с Node.js

Crypto-js (клиент):

const encrypted = CryptoJS.AES.encrypt("text", key, { iv: iv }).ciphertext.toString(CryptoJS.enc.Base64);

Node.js (сервер):

const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuffer, ivBuffer);
let decrypted = decipher.update(base64Data, 'base64', 'utf8');
decrypted += decipher.final('utf8');

Практика: совместимость с Python

Crypto-js → Python:

cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(base64.b64decode(data)), AES.block_size)

Отладка несовместимости

Метод пошаговой проверки:

  1. проверить длину ключа (128 / 192 / 256 бит)

  2. убедиться в совпадении IV

  3. сравнить режим шифрования

  4. проверить паддинг

  5. вывести промежуточные значения:

    • ключ (hex)
    • iv (hex)
    • ciphertext (hex)

Рекомендованный протокол обмена

Для полной совместимости:

  • ключ передаётся в Hex
  • IV передаётся отдельно
  • ciphertext передаётся в Base64
  • паддинг: PKCS7
  • режим: CBC

Формат JSON:

{
  "iv": "hex",
  "data": "base64"
}

Автоматизация тестирования

Создаются unit-тесты:

  • одинаковые входные данные
  • проверка в обе стороны

Инструменты:

  • Jest (JS)
  • PyTest (Python)
  • JUnit (Java)

Контрольная проверка на разных платформах

Минимальный набор:

  • браузер (Crypto-js)
  • Node.js
  • Python

Если данные совпадают во всех трёх средах — реализация считается совместимой.


Влияние версий библиотек

Разные версии Crypto-js могут:

  • менять формат вывода
  • по-разному обрабатывать строки

Фиксация версии обязательна:

npm install crypto-js@4.1.1

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

  • не использовать реальные ключи
  • не логировать секреты в production
  • использовать тестовые значения

Диагностические приёмы

  • сравнение через Hex вместо строк
  • вывод длины данных
  • побайтовое сравнение

Итоговая структура проверки

  1. Жёстко зафиксированные параметры
  2. Единый формат передачи
  3. Двусторонние тесты
  4. Побайтовая валидация
  5. Автоматизация тестов

Такая схема обеспечивает устойчивую и предсказуемую совместимость между Crypto-js и серверной частью независимо от языка реализации.