В криптографии качество случайных данных определяет устойчивость ключей, соли и производных значений. В CryptoJS генерация случайных байтов строится вокруг криптографически стойкого генератора случайных чисел, предоставляющего базовый строительный блок для ключей симметричного шифрования, соли для KDF и одноразовых значений.
Основной механизм случайности в CryptoJS реализован через объект
CryptoJS.lib.WordArray. Для получения случайных данных
используется метод:
const randomWordArray = CryptoJS.lib.WordArray.random(16);
Число 16 обозначает количество байтов. Результатом
является объект WordArray, содержащий псевдослучайную
последовательность, пригодную для криптографических операций.
WordArray представляет данные в виде массива 32-битных слов, что делает его совместимым с внутренними алгоритмами хеширования и шифрования библиотеки.
Для преобразования в читаемые форматы используются стандартные кодировки:
randomWordArray.toString(CryptoJS.enc.Hex);
randomWordArray.toString(CryptoJS.enc.Base64);
Hex используется для логирования и хранения, Base64 — для компактной передачи.
Ключи симметричного шифрования могут формироваться напрямую из случайных байтов:
const key = CryptoJS.lib.WordArray.random(32);
Размер ключа зависит от алгоритма:
Пример использования ключа в AES:
const encrypted = CryptoJS.AES.encrypt("secret data", key);
const decrypted = CryptoJS.AES.decrypt(encrypted, key);
При таком подходе ключ должен быть сохранён отдельно, так как восстановление невозможно.
Соль (salt) применяется для предотвращения атак с использованием радужных таблиц и для уникализации результатов хеширования.
Генерация соли:
const salt = CryptoJS.lib.WordArray.random(128 / 8);
Здесь используется 128-битная соль (16 байт), что является распространённой практикой для PBKDF2.
Соль не является секретной величиной и обычно хранится вместе с зашифрованными данными.
PBKDF2 (Password-Based Key Derivation Function 2) применяется для получения криптографического ключа из пароля.
const password = "user-password";
const salt = CryptoJS.lib.WordArray.random(16);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 10000
});
Параметры:
keySize — размер выходного ключа в 32-битных
словахiterations — число итераций (усиление вычислительной
сложности)salt — случайное значение для уникализации
результатаЧем выше число итераций, тем устойчивее результат к перебору, но тем выше вычислительная стоимость.
Использование одинаковой соли для разных паролей приводит к предсказуемости результатов.
Корректный подход:
function deriveKey(password) {
const salt = CryptoJS.lib.WordArray.random(16);
return {
salt: salt.toString(CryptoJS.enc.Hex),
key: CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 12000
}).toString()
};
}
Каждый вызов функции создаёт уникальный результат даже при одинаковом пароле.
Соль не скрывается, поэтому её необходимо сохранять вместе с зашифрованными данными:
const encryptedPackage = {
ciphertext: encrypted.toString(),
salt: salt.toString(CryptoJS.enc.Hex)
};
При расшифровке соль используется повторно:
const salt = CryptoJS.enc.Hex.parse(encryptedPackage.salt);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 12000
});
CryptoJS использует внутренний генератор, основанный на доступных в
среде источниках энтропии (например, Math.random в
браузерных реализациях старого типа или более надёжные источники в
модифицированных сборках).
Следует учитывать, что в некоторых окружениях стандартный CryptoJS может не обеспечивать достаточную криптографическую стойкость без дополнительной настройки. В таких случаях применяются внешние генераторы случайных чисел через Web Crypto API:
const array = new Uint8Array(16);
crypto.getRandomValues(array);
const wordArray = CryptoJS.lib.WordArray.create(array);
Такой подход обеспечивает использование системного CSPRNG.
Комбинированный подход с солью и PBKDF2 применяется чаще всего:
function encrypt(message, password) {
const salt = CryptoJS.lib.WordArray.random(16);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 15000
});
const encrypted = CryptoJS.AES.encrypt(message, key);
return {
ciphertext: encrypted.toString(),
salt: salt.toString(CryptoJS.enc.Hex)
};
}
Декодирование:
function decrypt(payload, password) {
const salt = CryptoJS.enc.Hex.parse(payload.salt);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 15000
});
return CryptoJS.AES.decrypt(payload.ciphertext, key)
.toString(CryptoJS.enc.Utf8);
}
Часто встречающиеся проблемы:
WordArray может быть преобразован в различные форматы:
const wa = CryptoJS.lib.WordArray.random(16);
wa.toString(CryptoJS.enc.Hex);
wa.toString(CryptoJS.enc.Base64);
wa.toString();
Выбор формата зависит от сценария:
Случайные значения используются не только для ключей и соли:
Пример генерации IV:
const iv = CryptoJS.lib.WordArray.random(16);
IV должен быть уникальным для каждого шифрования при использовании режимов CBC или CFB.
Соль и IV выполняют разные функции:
Неправильное смешивание этих понятий приводит к уязвимостям в криптосистемах.
Криптографически корректная схема включает:
Эта модель обеспечивает устойчивость к повторному анализу и атакующим сценариям, связанным с предсказуемостью данных.