Гибридное шифрование: RSA + AES на практике

Криптографические алгоритмы с асимметричными ключами, такие как RSA, обладают фундаментальным ограничением — их производительность и допустимый размер шифруемых данных. В практических системах это делает невозможным использование RSA для шифрования больших объёмов информации напрямую. AES, напротив, предназначен для быстрого симметричного шифрования больших данных, но требует безопасного обмена ключом.

Гибридное шифрование объединяет преимущества обеих схем: RSA используется для защиты симметричного ключа, а AES — для шифрования данных.

Библиотека Jsrsasign и криптографический стек

Jsrsasign представляет собой чисто JavaScript-библиотеку, реализующую криптографические алгоритмы, включая:

  • RSA (шифрование, подпись, проверка подписи)
  • AES (шифрование симметричных данных)
  • SHA, HMAC и другие хеш-функции
  • PKCS#1, PKCS#5, PKCS#8 форматы ключей
  • работу с X.509 сертификатами

Основные пространства имён:

  • KEYUTIL — работа с ключами
  • KJUR.crypto.Cipher — симметричное шифрование
  • KJUR.crypto.CipherUtil — утилиты для криптографии
  • KJUR.asn1 — работа с ASN.1 структурами

Общая архитектура гибридного шифрования

Гибридная модель строится вокруг разделения ответственности:

  1. Генерация случайного AES-ключа

  2. Шифрование данных AES-ключом

  3. Шифрование AES-ключа с помощью RSA публичного ключа

  4. Передача:

    • зашифрованных данных
    • зашифрованного AES-ключа

На стороне получателя:

  1. Расшифровка AES-ключа приватным RSA-ключом
  2. Расшифровка данных с помощью AES

Генерация RSA ключей в Jsrsasign

Для начала требуется создать RSA ключевую пару.

const rsaKeypair = KEYUTIL.generateKeypair("RSA", 2048);

const publicKey = rsaKeypair.pubKeyObj;
const privateKey = rsaKeypair.prvKeyObj;

Альтернативный вариант — импорт PEM-ключей:

const privateKey = KEYUTIL.getKey(pemPrivateKey);
const publicKey = KEYUTIL.getKey(pemPublicKey);

RSA-2048 остаётся базовым уровнем безопасности для большинства прикладных задач, хотя в современных системах часто используется RSA-3072 или выше.

Генерация AES ключа

AES-ключ должен быть случайным и криптографически стойким. В Jsrsasign используется генерация через KJUR.crypto.CipherUtil.

const aesKeyHex = KJUR.crypto.Cipher.generateRandomKey(256);

Здесь 256 бит — стандарт для AES-256.

IV (инициализационный вектор) также генерируется отдельно:

const ivHex = KJUR.crypto.Cipher.generateRandomIV();

Шифрование данных AES

Для симметричного шифрования используется AES-CBC или AES-GCM (в зависимости от реализации и требований).

Пример AES-CBC:

const encryptedData = KJUR.crypto.Cipher.encrypt(
  plaintext,
  aesKeyHex,
  "aes",
  "cbc",
  ivHex
);

Результат обычно кодируется в Base64 или Hex.

Шифрование AES ключа RSA

RSA применяется только к ключу, а не к основным данным.

const encryptedAesKey = publicKey.encrypt(aesKeyHex);

В Jsrsasign используется PKCS#1 v1.5 или OAEP padding:

const encryptedAesKey = KJUR.crypto.Cipher.encrypt(
  aesKeyHex,
  publicKey,
  "RSAOAEP"
);

OAEP предпочтительнее из-за устойчивости к атакам.

Формирование итогового пакета данных

Гибридная схема требует упаковки всех элементов в единый объект:

const payload = {
  key: encryptedAesKey,
  iv: ivHex,
  data: encryptedData
};

Для передачи обычно применяется сериализация:

const jsonPayload = JSON.stringify(payload);

Далее возможна дополнительная кодировка в Base64:

const encoded = KJUR.crypto.CipherUtil.stringtob64(jsonPayload);

Расшифровка AES ключа

На стороне получателя первым шагом является восстановление симметричного ключа:

const decryptedAesKey = privateKey.decrypt(encryptedAesKey);

Если используется OAEP:

const decryptedAesKey = KJUR.crypto.Cipher.decrypt(
  encryptedAesKey,
  privateKey,
  "RSAOAEP"
);

Расшифровка данных AES

После получения ключа выполняется расшифрование данных:

const decryptedData = KJUR.crypto.Cipher.decrypt(
  data,
  decryptedAesKey,
  "aes",
  "cbc",
  ivHex
);

Результат преобразуется обратно в строку:

const originalText = KJUR.crypto.CipherUtil.b64toutf8(decryptedData);

Особенности режимов AES в Jsrsasign

CBC (Cipher Block Chaining)

  • Требует IV
  • Обеспечивает базовую защиту
  • Широко используется в совместимых системах

GCM (Galois/Counter Mode)

  • Поддерживает аутентификацию данных
  • Защищает от модификации ciphertext
  • Предпочтителен в современных приложениях

Пример GCM:

const encrypted = KJUR.crypto.Cipher.encrypt(
  plaintext,
  aesKeyHex,
  "aes",
  "gcm",
  ivHex
);

Работа с форматами ключей PKCS

Jsrsasign поддерживает стандартные форматы:

  • PKCS#1 — RSA ключи
  • PKCS#8 — универсальный формат приватных ключей
  • X.509 — сертификаты

Импорт ключа из PEM:

const key = KEYUTIL.getKey(pemString);

Экспорт:

const pem = KEYUTIL.getPEM(privateKey, "PKCS8PRV");

Типовые ошибки при реализации гибридного шифрования

Неправильная генерация IV

Использование фиксированного IV полностью ломает безопасность AES-CBC.

Шифрование больших данных RSA

RSA не предназначен для этого. Ограничение связано с размером модуля и padding.

Повторное использование AES ключа

Один ключ на множество сообщений увеличивает риск криптоанализа.

Отсутствие OAEP

PKCS#1 v1.5 считается устаревшим в защищённых системах.

Оптимизированная схема передачи

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

  • RSA-OAEP для ключа
  • AES-256-GCM для данных
  • случайный IV для каждого сообщения
  • Base64 кодирование всех бинарных данных

Структура пакета:

{
  k: encryptedKey,
  i: iv,
  d: ciphertext,
  a: authTag
}

Пример полной цепочки шифрования

Клиентская сторона

const aesKey = KJUR.crypto.Cipher.generateRandomKey(256);
const iv = KJUR.crypto.Cipher.generateRandomIV();

const encryptedData = KJUR.crypto.Cipher.encrypt(
  message,
  aesKey,
  "aes",
  "gcm",
  iv
);

const encryptedKey = publicKey.encrypt(aesKey);

const packet = {
  key: encryptedKey,
  iv: iv,
  data: encryptedData
};

Серверная сторона

const aesKey = privateKey.decrypt(packet.key);

const decrypted = KJUR.crypto.Cipher.decrypt(
  packet.data,
  aesKey,
  "aes",
  "gcm",
  packet.iv
);

Криптографические свойства схемы

Гибридная модель обеспечивает:

  • конфиденциальность данных (AES)
  • безопасную передачу ключа (RSA)
  • масштабируемость для больших объёмов данных
  • независимость от длины сообщения

Производственные аспекты использования Jsrsasign

Jsrsasign работает полностью в JavaScript, что делает его применимым в:

  • браузерных приложениях
  • Node.js сервисах
  • изолированных средах без OpenSSL

При этом важно учитывать:

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