Безопасная генерация случайных чисел

Криптографическая стойкость любых операций в JavaScript, связанных с ключами, токенами, подписями и идентификаторами, напрямую зависит от качества генератора случайных чисел. В контексте Jsrsasign этот аспект реализован через криптографически стойкие генераторы, интегрированные в пространство KJUR.crypto.

Использование Math.random() в криптографических задачах приводит к предсказуемости значений. Алгоритм генерации псевдослучайных чисел в JavaScript не предназначен для защиты данных и может быть восстановлен при наличии достаточного количества наблюдений.

Типичные проблемы слабой генерации:

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

Любая криптографическая операция, основанная на таком источнике, теряет смысл как средство защиты.

Криптографически стойкий генератор в Jsrsasign

Jsrsasign использует специализированный модуль SecureRandom, реализующий CSPRNG (Cryptographically Secure Pseudo Random Number Generator). В основе лежат системные источники энтропии:

  • window.crypto.getRandomValues в браузере
  • crypto.randomBytes или аналогичные API в Node.js (при интеграции через полифилы)
  • внутренние буферы смешивания состояния при отсутствии системного API

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

Архитектура SecureRandom

В Jsrsasign генерация случайных данных реализуется через объект KJUR.crypto.SecureRandom.

Ключевые элементы:

  • буфер накопления энтропии
  • функция извлечения случайных байтов
  • интерфейс заполнения массивов Uint8Array
  • преобразование байтов в hex/base64/BigInteger

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

Базовое получение случайных байтов

var rng = new KJUR.crypto.SecureRandom();

var ba = new Array(32);
rng.nextBytes(ba);

Массив ba заполняется криптографически стойкими значениями. Каждый элемент представляет байт в диапазоне 0–255.

Генерация hex-строк фиксированной длины

Часто требуется получение случайных идентификаторов или nonce:

var hex = KJUR.crypto.Util.getRandomHexOfNbytes(16);

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

Такой формат используется для:

  • nonce в протоколах подписи
  • session identifiers
  • salt для хэширования
  • одноразовых токенов

Генерация случайных чисел для криптографических операций

В криптографических протоколах важна не только строковая форма, но и числовая интерпретация.

var bi = new KJUR.crypto.BigInteger();
bi.fromBuffer(new KJUR.crypto.SecureRandom().nextBytes(32));

Такая конструкция используется при работе с RSA, DSA и ECC, где требуются случайные значения большой длины.

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

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

Пример генерации RSA ключа:

var kp = KEYUTIL.generateKeypair("RSA", 2048);

Внутри процесса генерации происходит многократный вызов SecureRandom для:

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

Слабый RNG в этом процессе делает факторизацию ключа практически тривиальной задачей.

Источники энтропии и поведение в браузере

В браузерной среде основным источником является:

window.crypto.getRandomValues

Он обеспечивает:

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

Jsrsasign оборачивает этот API, стандартизируя его использование во всех операциях.

При отсутствии crypto библиотека может переходить на fallback-режим, что снижает криптографическую стойкость и требует явного контроля окружения.

Генерация случайных чисел в Node.js

В Node.js предпочтительным источником является:

require("crypto").randomBytes

Интеграция с Jsrsasign может происходить через адаптер, обеспечивающий совместимость интерфейса SecureRandom с системным RNG Node.js.

Типовые ошибки использования RNG

Повторное использование сида

Некоторые реализации PRNG позволяют задавать seed вручную. В криптографии это критическая ошибка, так как делает последовательность воспроизводимой.

Использование Math.random для токенов

var token = Math.random().toString(36);

Такая конструкция не обеспечивает ни уникальности, ни безопасности.

Урезание энтропии

Обрезка случайных значений (например, использование только младших бит) приводит к уменьшению пространства поиска при атаке перебором.

Генерация случайных идентификаторов

Типичный безопасный подход:

function generateId() {
  return KJUR.crypto.Util.getRandomHexOfNbytes(12);
}

Такой идентификатор содержит 96 бит энтропии, что делает перебор практически невозможным при корректной реализации RNG.

Роль SecureRandom в криптографических протоколах

В протоколах, реализуемых через Jsrsasign (PKCS#1, PKCS#5, ECDSA), случайность используется на всех уровнях:

  • подписи сообщений
  • генерация nonce в ECDSA (критически важно)
  • salt в PBKDF2
  • padding в RSA-OAEP

Особенно чувствителен ECDSA: повторное использование nonce приводит к восстановлению приватного ключа.

PBKDF2 и зависимость от случайности

Хотя PBKDF2 основан на паролях, начальный salt должен быть случайным:

var salt = KJUR.crypto.Util.getRandomHexOfNbytes(16);

Предсказуемый salt делает атаки с радужными таблицами эффективными.

Формирование безопасного состояния генератора

SecureRandom в Jsrsasign стремится минимизировать влияние состояния среды:

  • не использует глобальные seed-значения
  • не зависит от времени как единственного источника
  • комбинирует системные и внутренние источники при необходимости

Это снижает риск восстановления состояния генератора при частичной компрометации.

Практика работы с байтовыми массивами

При низкоуровневых операциях часто требуется работа напрямую с байтами:

var rng = new KJUR.crypto.SecureRandom();
var bytes = [];
rng.nextBytes(bytes);

Далее байты могут быть преобразованы в:

  • hex
  • base64
  • BigInteger
  • ASN.1 структуры

Каждое преобразование сохраняет исходную энтропию без потерь при корректной реализации.

Контроль качества случайности

Косвенные признаки проблем с RNG:

  • повторяющиеся токены при перезапуске приложения
  • корреляции в сгенерированных данных
  • недостаточная длина выходных значений при статистической проверке

Использование системного crypto API минимизирует такие риски, так как генерация переносится на уровень ОС.

Роль случайности в общей безопасности криптосистемы Jsrsasign

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

SecureRandom в Jsrsasign выступает фундаментальным компонентом, от которого зависят:

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