Hex-строки: получение и разбор

Работа с hex-строками в CryptoJS строится вокруг базовой сущности библиотеки — WordArray. Любые бинарные данные внутри CryptoJS представлены массивом 32-битных слов (words) и количеством значащих байт (sigBytes). Hex-формат выступает одним из стандартных способов сериализации этого внутреннего представления в человекочитаемую строку и обратно.


Внутренний формат CryptoJS не оперирует байтовыми массивами напрямую. Основная структура:

  • words — массив 32-битных целых чисел
  • sigBytes — количество значимых байт

Hex-строка является просто текстовым отображением этих байтов, где каждый байт кодируется двумя шестнадцатеричными символами.

Пример соответствия:

  • байт 0x0f → строка "0f"
  • байт 0xa3 → строка "a3"

Таким образом, длина hex-строки всегда кратна двум.


Преобразование WordArray → hex

Для сериализации используется модуль CryptoJS.enc.Hex и метод stringify.

import CryptoJS from "crypto-js";

const wordArray = CryptoJS.enc.Utf8.parse("data");
const hexString = CryptoJS.enc.Hex.stringify(wordArray);

console.log(hexString);

В результате каждый байт исходной строки преобразуется в два hex-символа.

Особенности сериализации

  • данные группируются по 32 бита (4 байта)
  • порядок байтов внутри слова зависит от big-endian представления CryptoJS
  • незначащие байты отбрасываются на основе sigBytes

Пример:

const wa = CryptoJS.enc.Hex.parse("01020304");
CryptoJS.enc.Hex.stringify(wa); // "01020304"

Преобразование hex → WordArray

Обратная операция выполняется через parse.

const wordArray = CryptoJS.enc.Hex.parse("deadbeef");

На выходе формируется структура:

  • words = [0xdeadbeef]
  • sigBytes = 4

Важное ограничение формата

Hex-строка должна содержать чётное количество символов. Каждый байт кодируется двумя символами.

Нечётная строка:

CryptoJS.enc.Hex.parse("abc"); // некорректный случай

Корректная форма требует:

CryptoJS.enc.Hex.parse("0abc");

Потеря ведущих нулей и значимость sigBytes

Ключевая особенность CryptoJS заключается в том, что значимость данных определяется не содержимым массива words, а параметром sigBytes.

Рассмотрим ситуацию:

const wa = CryptoJS.enc.Hex.parse("0001");
CryptoJS.enc.Hex.stringify(wa);

Результат:

"0001"

Однако при некорректных преобразованиях через числа возможна потеря ведущих нулей:

const wa = CryptoJS.lib.WordArray.create([1], 1);
CryptoJS.enc.Hex.stringify(wa);

Результат:

"01"

Если исходная длина была 2 байта (0001), то sigBytes должен это отражать. Иначе происходит усечение данных.


Побайтовая структура hex в WordArray

Каждое 32-битное слово хранит 4 байта:

0x11223344 → байты: 11 22 33 44

Hex-строка формируется последовательной записью байтов:

[0x11][0x22][0x33][0x44] → "11223344"

При этом порядок байтов внутри слова соответствует big-endian логике представления CryptoJS, что часто становится источником ошибок при взаимодействии с Node.js Buffer или ArrayBuffer.


Работа с хешами и hex-представлением

Большинство криптографических функций CryptoJS возвращают WordArray, который по умолчанию отображается в hex при вызове .toString().

const hash = CryptoJS.SHA256("message");
console.log(hash.toString());

Выход:

64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232b3f8b6d6c3a8c3

Явное указание кодировщика:

hash.toString(CryptoJS.enc.Hex);

Фактически эквивалентно, так как hex является стандартным представлением.


Парсинг hex-строк произвольной длины

При работе с бинарными протоколами часто встречаются строки переменной длины:

const input = "a1b2c3d4e5f6";
const wa = CryptoJS.enc.Hex.parse(input);

Каждая пара символов интерпретируется как байт.

Если длина строки не кратна 2, необходимо нормализовать вход:

function normalizeHex(hex) {
  return hex.length % 2 === 0 ? hex : "0" + hex;
}

Конвертация hex ↔︎ Buffer (Node.js совместимость)

В экосистеме Node.js hex часто используется вместе с Buffer.

Эквивалентные операции:

const buffer = Buffer.from("deadbeef", "hex");

CryptoJS эквивалент:

const wa = CryptoJS.enc.Hex.parse("deadbeef");

Обратное преобразование:

buffer.toString("hex");
CryptoJS.enc.Hex.stringify(wa);

Различие заключается в том, что Buffer работает с байтовыми массивами напрямую, тогда как CryptoJS использует промежуточный WordArray.


Нормализация регистра hex-строк

Hex-строки могут быть как в нижнем, так и в верхнем регистре:

deadbeef
DEADBEEF

CryptoJS не различает регистр при парсинге:

CryptoJS.enc.Hex.parse("DEADBEEF");
CryptoJS.enc.Hex.parse("deadbeef");

Однако при сериализации результат всегда формируется в нижнем регистре.


Встраивание hex в криптографические операции

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

Пример AES:

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

const encrypted = CryptoJS.AES.encrypt("data", key, {
  iv: iv
});

const ciphertextHex = encrypted.ciphertext.toString(CryptoJS.enc.Hex);

Здесь:

  • ключ и IV представлены hex-строками
  • ciphertext извлекается как WordArray и сериализуется в hex

Ошибки при работе с hex-данными

Наиболее частые проблемы связаны с несоответствием длины и интерпретации байтов:

  1. Утрата ведущих нулей

    • возникает при преобразовании через числа вместо WordArray
  2. Нечётная длина строки

    • приводит к неверному парсингу последнего символа
  3. Несоответствие endianness

    • особенно при обмене с внешними системами
  4. Игнорирование sigBytes

    • приводит к появлению лишних байтов в результате сериализации

Взаимодействие hex и других кодировок CryptoJS

Hex часто выступает промежуточным форматом между бинарными и текстовыми представлениями.

const utf8 = CryptoJS.enc.Utf8.parse("text");
const hex = CryptoJS.enc.Hex.stringify(utf8);
const back = CryptoJS.enc.Hex.parse(hex);

При корректной работе цепочка преобразований сохраняет исходные данные без потерь.


Практическая модель обработки hex

Типичный поток данных в криптографических задачах:

  1. входная строка или бинарный буфер
  2. преобразование в WordArray
  3. криптографическая операция
  4. сериализация в hex для передачи или хранения
const input = "hello";
const wa = CryptoJS.enc.Utf8.parse(input);
const hash = CryptoJS.SHA256(wa);
const hex = hash.toString(CryptoJS.enc.Hex);

Итоговые свойства hex в CryptoJS

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