Симметричные алгоритмы шифрования в режиме CBC, CFB или CTR требуют использования вектора инициализации. В CryptoJS этот параметр задаётся явно и при этом остаётся критически важным для корректного и безопасного расшифрования. Основная проблема практической реализации заключается не в генерации IV, а в его корректной передаче вместе с шифротекстом.
Вектор инициализации обеспечивает случайность первого блока шифрования. При одинаковом открытом тексте и одинаковом ключе без IV результат шифрования будет полностью идентичным, что делает систему уязвимой к анализу повторов.
y = E_{K}(P IV)
В режиме CBC каждый блок шифротекста зависит от предыдущего, а первый — от IV. Поэтому повторное использование IV с тем же ключом приводит к утечке структуры данных.
Ключевые свойства IV:
В CryptoJS IV не добавляется автоматически к результату шифрования
при использовании низкоуровневого API CryptoJS.AES.encrypt
с явной передачей параметров. Результат операции содержит только
шифротекст, тогда как IV остаётся отдельным объектом.
const CryptoJS = require("crypto-js");
const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const iv = CryptoJS.lib.WordArray.random(16);
const encrypted = CryptoJS.AES.encrypt("секретное сообщение", key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log(encrypted.toString());
console.log(iv.toString());
В данном случае шифротекст и IV существуют как два независимых значения, что создаёт задачу их совместной упаковки.
Шифротекст без IV невозможно корректно расшифровать. Однако IV не является секретом, поэтому его допустимо передавать вместе с данными. Основная задача заключается в выборе структуры упаковки.
Существуют несколько распространённых подходов:
IV добавляется в начало или конец итоговой строки после кодирования.
const ivBase64 = CryptoJS.enc.Base64.stringify(iv);
const ciphertextBase64 = encrypted.toString();
const payload = ivBase64 + ":" + ciphertextBase64;
При расшифровке строка разделяется на две части, после чего IV восстанавливается:
const parts = payload.split(":");
const iv = CryptoJS.enc.Base64.parse(parts[0]);
const ciphertext = parts[1];
const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
Недостатком является необходимость строгого контроля формата и разделителей.
Более структурированный вариант — использование JSON-объекта.
const payload = JSON.stringify({
iv: CryptoJS.enc.Base64.stringify(iv),
data: encrypted.toString()
});
Расшифрование:
const obj = JSON.parse(payload);
const iv = CryptoJS.enc.Base64.parse(obj.iv);
const ciphertext = obj.data;
const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
Данный подход обеспечивает расширяемость структуры (например, добавление версии протокола или алгоритма).
В некоторых реализациях используется объединение в бинарном виде: IV + ciphertext → единый WordArray → Base64.
const combined = iv.clone().concat(encrypted.ciphertext);
const result = CryptoJS.enc.Base64.stringify(combined);
При расшифровке:
const raw = CryptoJS.enc.Base64.parse(result);
const iv = CryptoJS.lib.WordArray.create(raw.words.slice(0, 4), 16);
const ciphertext = CryptoJS.lib.WordArray.create(raw.words.slice(4));
Такой способ минимизирует накладные расходы, но требует точного контроля длины IV.
Повторное использование IV с одним и тем же ключом приводит к криптографической уязвимости. В режиме CBC это позволяет вычислять XOR между двумя открытыми текстами:
C_1 C_2 = (P_1 IV) (P_2 IV)
IV сокращается, и структура сообщений становится частично восстановимой.
Поэтому стандартная практика:
CryptoJS.lib.WordArray.random(16)CryptoJS оперирует объектом WordArray, поэтому
преобразование в строковые форматы обязательно.
Base64:
CryptoJS.enc.Base64.stringify(iv)
Hex:
CryptoJS.enc.Hex.stringify(iv)
Base64 используется чаще из-за меньшего размера и совместимости с JSON и HTTP.
В отличие от CBC, режим GCM включает аутентификацию данных и использует nonce вместо классического IV. Однако в CryptoJS поддержка GCM ограничена, поэтому часто используется CBC с внешней реализацией контроля целостности.
В системах, где GCM доступен, структура обычно включает:
Все три компонента передаются вместе, аналогично IV в CBC.
Типичные проблемы при реализации:
Особенно критичной является ошибка, при которой IV генерируется, но не сохраняется вместе с результатом шифрования, что делает расшифрование невозможным.
В реальных системах чаще всего применяется комбинированный формат:
{
"v": 1,
"iv": "Base64",
"ct": "Base64"
}
Такой подход позволяет:
CryptoJS остаётся на уровне криптографического примитива, поэтому вся логика упаковки данных реализуется на уровне приложения, а не библиотеки.