В CryptoJS любые криптографические операции опираются не на строки
JavaScript напрямую, а на внутренний формат представления данных —
WordArray. Понимание того, как UTF-8 преобразуется в этот
формат и обратно, является ключевым для корректной работы с любыми
текстовыми данными, содержащими Unicode.
Строки в JavaScript представлены в формате UTF-16, где:
Это приводит к типичным проблемам:
length) не соответствует числу
символовCryptoJS решает эту проблему через явное кодирование в UTF-8.
UTF-8 — переменно-длинная кодировка Unicode:
CryptoJS всегда преобразует строку в UTF-8 перед криптографическими операциями:
const CryptoJS = require("crypto-js");
const data = "Привет, мир!";
const utf8Data = CryptoJS.enc.Utf8.parse(data);
Здесь происходит ключевой процесс:
JavaScript string → UTF-8 bytes → WordArray
WordArray — это массив 32-битных слов, где:
Пример структуры:
{
words: [0x6d795061, 0x7373776f, ...],
sigBytes: 12
}
UTF-8 байты упаковываются в эти 32-битные блоки.
Основной механизм:
const text = "CryptoJS UTF-8";
const wordArray = CryptoJS.enc.Utf8.parse(text);
Процесс внутри:
Важно учитывать:
Для декодирования используется:
const original = CryptoJS.enc.Utf8.stringify(wordArray);
Механизм:
Неправильный подход:
CryptoJS.MD5("Привет");
Правильный:
CryptoJS.MD5(CryptoJS.enc.Utf8.parse("Привет"));
Если не использовать UTF-8 явно, возможны:
Все алгоритмы CryptoJS (AES, SHA, HMAC) работают с байтами:
Пример AES:
const message = "Секретное сообщение";
const encrypted = CryptoJS.AES.encrypt(
CryptoJS.enc.Utf8.parse(message),
"ключ"
);
Дешифрование:
const decrypted = CryptoJS.AES.decrypt(encrypted, "ключ");
const text = CryptoJS.enc.Utf8.stringify(decrypted);
JavaScript может кодировать символы вне BMP как две 16-битные единицы.
Пример:
const emoji = "?";
Фактически это:
CryptoJS корректно обрабатывает это через UTF-8, но только при
использовании Utf8.parse.
Ошибка возникает при попытке обработки как “сырых” UTF-16 данных.
Unicode допускает разные представления одного и того же символа:
Пример:
CryptoJS не выполняет нормализацию автоматически.
Рекомендуется внешняя нормализация:
const normalized = text.normalize("NFC");
const wordArray = CryptoJS.enc.Utf8.parse(normalized);
Часто UTF-8 используется как промежуточный слой перед Base64:
const wordArray = CryptoJS.enc.Utf8.parse("данные");
const base64 = CryptoJS.enc.Base64.stringify(wordArray);
Обратное преобразование:
const parsed = CryptoJS.enc.Base64.parse(base64);
const text = CryptoJS.enc.Utf8.stringify(parsed);
Важно понимать:
При взаимодействии с:
UTF-8 является стандартом де-факто.
Пример интеграции с Buffer:
const buffer = Buffer.from("текст", "utf8");
const wordArray = CryptoJS.lib.WordArray.create(buffer);
CryptoJS.SHA256("текст");
Риск: несовпадение хэшей
CryptoJS.enc.Utf8.stringify(decrypted);
Если данные не UTF-8 → мусор в выводе
Без явного контроля возникает потеря данных
function encodeUtf8(text) {
return CryptoJS.enc.Utf8.parse(text);
}
function decodeUtf8(wordArray) {
return CryptoJS.enc.Utf8.stringify(wordArray);
}
function sha256(text) {
return CryptoJS.SHA256(encodeUtf8(text)).toString();
}
Если данные не являются валидным UTF-8:
Это особенно критично при:
Внутренний pipeline CryptoJS:
JavaScript String (UTF-16)
↓
Utf8.parse()
↓
UTF-8 bytes
↓
WordArray (32-bit words)
↓
Криптографический алгоритм
↓
WordArray (результат)
↓
Utf8.stringify() / Base64 / Hex
↓
JavaScript String
Эта цепочка должна соблюдаться строго для любых текстовых данных, содержащих Unicode.