Алгоритм работы с ключами внутри библиотеки

В библиотеке CryptoJS ключи не рассматриваются как примитивные строки. Основной внутренний формат — объект типа WordArray. Это структура, представляющая собой массив 32-битных слов и дополнительное поле длины (в байтах). Такой подход обеспечивает:

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

Пример структуры:

{
    words: [0x12345678, 0x90abcdef],
    sigBytes: 8
}

Поле sigBytes определяет фактическое количество значимых байтов, что важно при работе с неполными словами.


Преобразование входных данных в ключ

Любой входной ключ (строка, hex, base64) перед использованием проходит нормализацию:

var key = CryptoJS.enc.Utf8.parse("my-secret-key");

Поддерживаются различные кодировки:

  • CryptoJS.enc.Utf8
  • CryptoJS.enc.Hex
  • CryptoJS.enc.Base64
  • CryptoJS.enc.Latin1

Каждая кодировка преобразует данные в WordArray, который далее используется алгоритмами.


Автоматическая генерация ключа из пароля

Если вместо ключа передаётся строка-пароль, библиотека автоматически запускает механизм деривации ключа (Key Derivation Function, KDF). В основе лежит алгоритм OpenSSL EVP_BytesToKey.

var encrypted = CryptoJS.AES.encrypt("data", "password");

Внутренние шаги:

  1. Генерация случайной соли (8 байт)
  2. Повторное хеширование (обычно MD5)
  3. Формирование ключа и IV

Формула процесса:

D_i = MD5(D_(i-1) + password + salt)

Где:

  • D_i — промежуточные блоки
  • итоговый ключ = конкатенация D_i

Разделение ключа и IV

Для симметричных алгоритмов (например, AES) требуется:

  • ключ шифрования (Key)
  • вектор инициализации (IV)

При использовании KDF оба значения извлекаются из общего результата:

var keySize = 256 / 32; // 8 слов
var ivSize = 128 / 32;  // 4 слова

Итоговый массив разбивается:

var key = derived.words.slice(0, keySize);
var iv = derived.words.slice(keySize, keySize + ivSize);

Ручное управление ключами

При передаче готового ключа KDF не используется:

var key = CryptoJS.enc.Hex.parse("00112233445566778899aabbccddeeff");
var iv = CryptoJS.enc.Hex.parse("0102030405060708");

CryptoJS.AES.encrypt("data", key, { iv: iv });

Особенности:

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

Проверка длины ключа

Алгоритмы требуют строго определённые размеры:

Алгоритм Размер ключа
AES-128 16 байт
AES-192 24 байта
AES-256 32 байта

CryptoJS не всегда явно валидирует длину — возможны неявные ошибки или автоматическое усечение.


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

Для безопасной генерации используется встроенный генератор:

var key = CryptoJS.lib.WordArray.random(32);

Важно:

  • используется псевдослучайный генератор
  • в браузере опирается на crypto.getRandomValues (если доступен)
  • в Node.js — на системный источник энтропии

Хранение ключей в памяти

WordArray не защищён от:

  • дампа памяти
  • утечек через логирование
  • копирования при операциях

Каждая операция (concat, slice) создаёт новые объекты, что увеличивает поверхность атаки.


Конкатенация и расширение ключей

Ключи могут комбинироваться:

var combined = key1.clone().concat(key2);

Используется при:

  • построении составных ключей
  • реализации собственных KDF
  • генерации MAC

Использование ключей в хешировании

В алгоритмах HMAC ключ также преобразуется в WordArray:

CryptoJS.HmacSHA256(message, key);

Если ключ длиннее блока:

  • он сначала хешируется
  • затем используется как стандартный ключ

Кеширование ключей

Внутри некоторых алгоритмов (например, HMAC) происходит кеширование:

  • внутренние состояния (ipad, opad) сохраняются
  • ускоряется повторное использование ключа

Это снижает вычислительные затраты при множественных операциях.


Управление ключами в потоковых операциях

При работе с потоками:

var cipher = CryptoJS.algo.AES.createEncryptor(key, { iv: iv });

Ключ сохраняется в состоянии объекта:

  • не пересоздаётся на каждом шаге
  • используется для последовательной обработки блоков

Проблемы безопасности при работе с ключами

Ключевые риски:

  • использование строковых паролей без KDF
  • повторное использование IV
  • хранение ключей в открытом виде
  • недостаточная энтропия

Рекомендации по безопасной работе

  • всегда использовать WordArray.random для генерации
  • применять PBKDF2 вместо встроенного EVP_BytesToKey
  • хранить ключи вне клиентского кода
  • использовать разные ключи для разных задач

Пример PBKDF2:

var key = CryptoJS.PBKDF2("password", salt, {
    keySize: 256/32,
    iterations: 100000
});

Внутренние оптимизации

CryptoJS оптимизирует работу с ключами через:

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

Это позволяет эффективно обрабатывать даже большие объёмы данных без значительных затрат памяти.


Роль ключей в архитектуре библиотеки

Ключ является центральным элементом:

  • управляет шифрованием/дешифрованием
  • участвует в генерации MAC
  • влияет на криптостойкость

Все алгоритмы в CryptoJS построены вокруг корректного преобразования и использования ключей, что делает понимание их внутреннего устройства критически важным для безопасной разработки.