Совместимость с модулем crypto стандартной библиотеки

В экосистеме JavaScript существует два широко используемых подхода к криптографическим операциям: библиотека CryptoJS, ориентированная на универсальность и работу в браузере, и встроенный модуль Node.js crypto, основанный на нативных реализациях OpenSSL. Несмотря на схожие цели, их внутренняя архитектура, формат данных и набор возможностей существенно различаются, что напрямую влияет на совместимость.

Различие в архитектуре и источниках криптографических функций

CryptoJS представляет собой чисто JavaScript-реализацию криптографических алгоритмов. Все операции выполняются на уровне JS-кода без обращения к нативным системным библиотекам. Это обеспечивает кроссплатформенность, но накладывает ограничения на производительность и набор поддерживаемых алгоритмов.

Модуль crypto в Node.js, напротив, является обёрткой над OpenSSL. Это означает:

  • использование нативного кода на C/C++
  • доступ к системным криптографическим возможностям
  • более высокая производительность
  • поддержка современных алгоритмов и режимов шифрования

Такое различие приводит к тому, что одинаковые операции могут давать разные результаты при некорректной настройке параметров.

Несовместимость форматов данных

Одной из ключевых проблем при взаимодействии CryptoJS и Node.js crypto является различие в представлении данных.

CryptoJS использует WordArray

Внутренний формат CryptoJS — WordArray, представляющий данные как массив 32-битных слов. Например:

  • результат хеширования
  • ключи шифрования
  • бинарные данные

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

  • CryptoJS.enc.Hex
  • CryptoJS.enc.Base64
  • CryptoJS.enc.Utf8

Node.js использует Buffer

В Node.js стандартом является Buffer, который ближе к нативному бинарному представлению данных:

  • фиксированные байтовые массивы
  • интеграция с потоками (streams)
  • прямое взаимодействие с файловой системой и сетью

Конвертация между Buffer и WordArray

Для совместимости требуется явное преобразование.

Buffer → WordArray

const CryptoJS = require("crypto-js");

function bufferToWordArray(buffer) {
  const words = [];
  for (let i = 0; i < buffer.length; i++) {
    words[i >>> 2] |= buffer[i] << (24 - (i % 4) * 8);
  }
  return CryptoJS.lib.WordArray.create(words, buffer.length);
}

WordArray → Buffer

function wordArrayToBuffer(wordArray) {
  const { words, sigBytes } = wordArray;
  const buffer = Buffer.alloc(sigBytes);

  for (let i = 0; i < sigBytes; i++) {
    buffer[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
  }

  return buffer;
}

Несовпадение форматов является основной причиной расхождений при попытке совместного использования алгоритмов.

Хеширование: различия реализации

CryptoJS

const hash = CryptoJS.SHA256("data").toString();

Результат по умолчанию возвращается в hex-строке.

Node.js crypto

const crypto = require("crypto");

const hash = crypto.createHash("sha256")
  .update("data")
  .digest("hex");

Несмотря на одинаковый алгоритм, различия могут возникнуть из-за:

  • кодировки входных данных
  • способа сериализации результата
  • обработки строк UTF-8

Шифрование AES и режимы работы

CryptoJS и Node.js crypto поддерживают AES, однако различаются параметры по умолчанию.

CryptoJS (AES)

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

По умолчанию используется:

  • режим CBC
  • PKCS7 padding
  • производное ключа через OpenSSL EVP

Node.js crypto (AES)

const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);

Здесь требуется явное указание:

  • ключа фиксированной длины
  • IV (initialization vector)
  • режима работы

Проблема совместимости ключей

CryptoJS автоматически выполняет derivation ключа при передаче строки-пароля. Node.js этого не делает.

В CryptoJS:

  • используется OpenSSL-compatible EVP derivation

В Node.js:

  • требуется crypto.pbkdf2 или scrypt

Это приводит к несовместимости даже при одинаковом пароле:

  • CryptoJS: скрытая генерация ключа
  • Node.js: явная генерация ключа

PBKDF2 и различия реализации

CryptoJS:

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

Node.js:

const key = crypto.pbkdf2Sync("password", "salt", 100000, 32, "sha256");

Основные расхождения:

  • количество итераций
  • хеш-функция
  • формат результата

HMAC и совпадение результатов

HMAC в обоих окружениях совместим при строгом совпадении параметров.

CryptoJS:

CryptoJS.HmacSHA256("message", "key").toString();

Node.js:

crypto.createHmac("sha256", "key")
  .update("message")
  .digest("hex");

Несовпадение обычно связано с:

  • кодировкой строки (UTF-8 vs binary)
  • различием в интерпретации ключа

Работа с кодировками

CryptoJS оперирует абстрактными энкодерами:

  • Utf8
  • Latin1
  • Hex
  • Base64

Node.js использует Buffer с поддержкой стандартных кодировок:

  • utf8
  • base64
  • hex

Несоответствие часто возникает при:

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

Потоки данных и ограничения CryptoJS

Node.js crypto поддерживает потоковую обработку:

stream.pipe(cipher)

CryptoJS работает только в памяти, без потоков. Это создаёт ограничения:

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

Производительность и системные ограничения

Node.js crypto использует аппаратные оптимизации:

  • ускорение AES-NI
  • оптимизированные реализации SHA
  • поддержка системных криптопровайдеров

CryptoJS:

  • чистый JavaScript
  • зависимость от интерпретатора V8
  • значительно ниже производительность при больших объёмах данных

Совместимость алгоритмов: реальные ограничения

Не все алгоритмы CryptoJS напрямую соответствуют Node.js:

  • RC4 — устаревшая поддержка
  • некоторые режимы AES могут различаться по padding
  • EVP-derive поведение отличается по умолчанию

Даже при одинаковом названии алгоритма результат может отличаться из-за:

  • различий в padding
  • различий в derivation key
  • интерпретации строковых входных данных

Практическая модель взаимодействия

Корректная интеграция требует соблюдения единого набора правил:

  • единая кодировка (обычно UTF-8)
  • явное управление ключами и IV
  • отказ от автоматической derivation CryptoJS
  • использование Buffer как промежуточного слоя
  • фиксированные параметры алгоритмов

При нарушении этих условий совместимость теряется, даже если алгоритм формально совпадает.

Итоговая модель различий

  • CryptoJS ориентирован на универсальность и браузер
  • Node.js crypto ориентирован на производительность и системную интеграцию
  • форматы данных несовместимы без преобразования
  • ключевая несовместимость связана с derivation и padding
  • бинарные операции требуют строгого контроля кодировок