Генерация случайных ключей и соли

В криптографии качество случайных данных определяет устойчивость ключей, соли и производных значений. В 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-128 → 16 байт
  • AES-192 → 24 байта
  • AES-256 → 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

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.

Практика генерации ключей для AES через пароль

Комбинированный подход с солью и 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);
}

Ошибки при работе со случайными данными

Часто встречающиеся проблемы:

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

Форматы представления случайных данных

WordArray может быть преобразован в различные форматы:

const wa = CryptoJS.lib.WordArray.random(16);

wa.toString(CryptoJS.enc.Hex);
wa.toString(CryptoJS.enc.Base64);
wa.toString();

Выбор формата зависит от сценария:

  • Hex — совместимость и отладка
  • Base64 — передача через сети
  • WordArray — внутренние операции CryptoJS

Генерация случайных значений вне ключей

Случайные значения используются не только для ключей и соли:

  • nonce в режимах шифрования
  • initialization vector (IV)
  • уникальные идентификаторы сессий
  • одноразовые токены

Пример генерации IV:

const iv = CryptoJS.lib.WordArray.random(16);

IV должен быть уникальным для каждого шифрования при использовании режимов CBC или CFB.

Связь соли и IV

Соль и IV выполняют разные функции:

  • соль влияет на производные ключи
  • IV влияет на начальный блок шифрования

Неправильное смешивание этих понятий приводит к уязвимостям в криптосистемах.

Итоговая модель безопасной генерации

Криптографически корректная схема включает:

  • генерацию случайной соли
  • применение PBKDF2 для вывода ключа
  • генерацию IV для шифрования
  • использование CSPRNG для всех случайных значений
  • хранение соли и IV вместе с шифротекстом

Эта модель обеспечивает устойчивость к повторному анализу и атакующим сценариям, связанным с предсказуемостью данных.