В библиотеке CryptoJS вся работа с криптографическими примитивами
опирается на единый внутренний формат представления данных —
WordArray. Это ключевая абстракция, через которую проходят
ключи, сообщения, хэши и шифротексты. Понимание устройства этого формата
критично для корректной работы с бинарными данными, особенно при
взаимодействии с API браузера, Node.js и внешними сервисами.
Основной тип бинарных данных в CryptoJS — это объект
CryptoJS.lib.WordArray. Он представляет собой массив
32-битных слов (words), дополненный информацией о фактической длине
данных в байтах.
Структура выглядит следующим образом:
words: массив 32-битных целых чиселsigBytes: количество значимых байтПример создания:
const wordArray = CryptoJS.lib.WordArray.create([0x12345678, 0x90abcdef], 8);
Здесь:
8 означает, что используется 8 байт (то есть не все
биты последнего слова значимы)WordArray хранит данные в big-endian представлении внутри 32-битных слов. Это важно учитывать при ручной работе с байтами, так как порядок байтов внутри слова может отличаться от привычного побайтного представления.
CryptoJS автоматически конвертирует строки в WordArray через
кодировки из CryptoJS.enc.
const data = CryptoJS.enc.Utf8.parse("hello");
Результат — WordArray, содержащий байтовое представление UTF-8 строки.
const data = CryptoJS.enc.Hex.parse("68656c6c6f");
Каждые два символа hex превращаются в байт.
const data = CryptoJS.enc.Base64.parse("aGVsbG8=");
Base64 декодируется в исходные байты.
Любой WordArray можно сериализовать обратно:
const hex = wordArray.toString(CryptoJS.enc.Hex);
const base64 = wordArray.toString(CryptoJS.enc.Base64);
const text = wordArray.toString(CryptoJS.enc.Utf8);
Важно учитывать, что некорректная интерпретация бинарных данных как UTF-8 может привести к ошибкам декодирования.
CryptoJS предоставляет низкоуровневый доступ к управлению бинарными
данными через words и sigBytes.
const wa = CryptoJS.lib.WordArray.create();
Для конкатенации используется метод concat:
const a = CryptoJS.enc.Utf8.parse("hello ");
const b = CryptoJS.enc.Utf8.parse("world");
const result = a.concat(b);
После операции result содержит объединённые байты.
Иногда требуется вручную изменить длину данных:
wordArray.sigBytes = 5;
После этого лишние байты игнорируются при сериализации.
Для корректировки внутреннего состояния используется метод
clamp():
wordArray.clamp();
Он обнуляет лишние биты в последнем 32-битном слове, синхронизируя
его с sigBytes.
В браузере бинарные данные часто представлены через
ArrayBuffer или Uint8Array. CryptoJS не
работает с ними напрямую, поэтому требуется конвертация.
function uint8ToWordArray(u8Array) {
const words = [];
for (let i = 0; i < u8Array.length; i++) {
words[i >>> 2] |= u8Array[i] << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, u8Array.length);
}
function wordArrayToUint8Array(wordArray) {
const { words, sigBytes } = wordArray;
const u8 = new Uint8Array(sigBytes);
for (let i = 0; i < sigBytes; i++) {
u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
}
return u8;
}
Все алгоритмы CryptoJS (SHA, MD5, RIPEMD160) принимают WordArray как вход.
const hash = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse("message"));
Результат также является WordArray.
При симметричном шифровании (AES, DES) данные должны быть представлены в бинарном виде.
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse("secret data"),
"password"
);
Внутри происходит:
CryptoJS автоматически применяет padding при шифровании. Это приводит к изменению длины бинарных данных.
Типичный padding:
Каждый добавленный байт содержит значение количества добавленных байтов.
Это критично при ручной обработке WordArray, так как:
sigBytes включает paddingwords может содержать “мусорные” биты без
clamp()WordArray является изменяемым объектом. Простое присваивание создаёт ссылку, а не копию.
Глубокое копирование:
const copy = wordArray.clone();
Это важно при многократных операциях хэширования или шифрования, чтобы не испортить исходные данные.
WordArray можно объединять:
const a = CryptoJS.enc.Hex.parse("aa");
const b = CryptoJS.enc.Hex.parse("bb");
const combined = a.clone().concat(b);
Особенность: concat изменяет исходный объект, поэтому
часто используют clone().
Если не обновить sigBytes, результат сериализации может
содержать лишние байты.
После ручного изменения words обязательно:
wordArray.clamp();
Иначе возможны артефакты в старших битах.
Типичная ошибка:
CryptoJS.enc.Utf8.parse(hexString)
Если данные в hex, нужно:
CryptoJS.enc.Hex.parse(hexString)
WordArray не оптимизирован для высокопроизводительных потоковых операций. Основные ограничения:
Для больших массивов данных (сотни мегабайт) предпочтительнее использовать Web Crypto API или Node Buffer.
В Node.js часто требуется конвертация между Buffer и WordArray.
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);
}
function wordArrayToBuffer(wordArray) {
const buffer = Buffer.alloc(wordArray.sigBytes);
for (let i = 0; i < wordArray.sigBytes; i++) {
buffer[i] = (wordArray.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
}
return buffer;
}
HMAC в CryptoJS также полностью основан на WordArray:
const hmac = CryptoJS.HmacSHA256(
CryptoJS.enc.Utf8.parse("data"),
CryptoJS.enc.Utf8.parse("key")
);
Здесь и ключ, и сообщение — бинарные структуры.
Хотя CryptoJS не поддерживает потоковую обработку, можно имитировать её через последовательную конкатенацию блоков:
let acc = CryptoJS.lib.WordArray.create();
acc = acc.concat(CryptoJS.enc.Utf8.parse("part1"));
acc = acc.concat(CryptoJS.enc.Utf8.parse("part2"));
Однако каждый concat создаёт новый объект, что делает
подход затратным.
Внутренняя цепочка обработки данных в CryptoJS выглядит следующим образом:
Понимание этой цепочки позволяет точно контролировать поведение криптографических операций и корректно интегрировать CryptoJS в системы, работающие с бинарными протоколами, файлами и сетевыми потоками.