Метод subtle.encrypt для RSA-OAEP

Метод encrypt интерфейса SubtleCrypto используется для выполнения асимметричного шифрования данных с использованием открытого ключа. В случае RSA-OAEP он реализует схему шифрования RSA с оптимальным асимметричным заполнением (Optimal Asymmetric Encryption Padding), обеспечивающую устойчивость к выбранным атакам на основе открытого текста.

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


crypto.subtle.encrypt(algorithm, key, data)

Параметры:

  • algorithm — объект конфигурации, описывающий используемый алгоритм
  • key — объект CryptoKey, содержащий публичный RSA-ключ
  • data — данные для шифрования (ArrayBuffer или TypedArray)

Возвращаемое значение:

Promise<ArrayBuffer> — зашифрованный результат в бинарном виде


Конфигурация RSA-OAEP

При использовании RSA-OAEP объект алгоритма выглядит следующим образом:

const algorithm = {
  name: "RSA-OAEP",
  hash: "SHA-256",
  label: new Uint8Array([])
};

Поле name

Всегда фиксировано:

  • "RSA-OAEP" — указывает на использование схемы OAEP поверх RSA

Поле hash

Определяет хеш-функцию, используемую внутри OAEP для генерации маски.

Поддерживаемые значения:

  • "SHA-1" (устаревающее, но всё ещё поддерживается в некоторых окружениях)
  • "SHA-256" (рекомендуемое значение)
  • "SHA-384"
  • "SHA-512"

Современные реализации Web Crypto API требуют явного указания hash, так как выбор влияет на криптостойкость и совместимость ключей.

Поле label

Необязательный параметр. Представляет собой дополнительные ассоциированные данные (Optional Label), которые включаются в процесс OAEP-паддинга.

  • Тип: Uint8Array
  • По умолчанию: пустая строка (new Uint8Array([]))

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


Подготовка данных к шифрованию

Метод encrypt работает только с бинарными данными. Строки необходимо преобразовывать вручную.

Стандартный способ:

const encoder = new TextEncoder();
const data = encoder.encode("Секретное сообщение");

Результат — Uint8Array, пригодный для передачи в encrypt.


Пример базового шифрования RSA-OAEP

const publicKey = await crypto.subtle.importKey(
  "spki",
  publicKeyBuffer,
  {
    name: "RSA-OAEP",
    hash: "SHA-256"
  },
  false,
  ["encrypt"]
);

const encoder = new TextEncoder();
const data = encoder.encode("Конфиденциальные данные");

const encrypted = await crypto.subtle.encrypt(
  {
    name: "RSA-OAEP",
    hash: "SHA-256"
  },
  publicKey,
  data
);

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


Особенности работы RSA-OAEP в Web Crypto API

RSA-OAEP в браузерной реализации имеет ряд ограничений, связанных с природой алгоритма:

Ограничение размера входных данных

RSA не предназначен для шифрования больших объёмов информации.

Максимальный размер входных данных зависит от:

  • длины ключа (2048, 3072, 4096 бит)
  • выбранного хеша
  • OAEP overhead

Пример для 2048-битного ключа и SHA-256:

  • максимальный размер ≈ 190–214 байт

Попытка зашифровать больший буфер приводит к ошибке OperationError.


Формат результата

Метод возвращает ArrayBuffer, который не содержит метаданных.

Для передачи по сети или хранения обычно используется Base64-кодирование:

function arrayBufferToBase64(buffer) {
  let binary = "";
  const bytes = new Uint8Array(buffer);
  for (let i = 0; i < bytes.byteLength; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary);
}

Типичные ошибки

InvalidAccessError

Возникает, если:

  • ключ не предназначен для шифрования (["encrypt"] не указан при импорте)
  • используется приватный ключ вместо публичного

DataError

Возникает при:

  • превышении допустимого размера входных данных
  • некорректном формате ArrayBuffer

OperationError

Обобщённая криптографическая ошибка, часто связанная с:

  • несоответствием параметров OAEP
  • несовместимостью ключа и алгоритма
  • повреждёнными данными

Важные нюансы совместимости

  1. Не все браузеры одинаково поддерживают "SHA-512" в RSA-OAEP
  2. Ключи, созданные с SHA-1, не всегда совместимы с SHA-256
  3. Импорт ключа требует строго соответствующего алгоритма:
{
  name: "RSA-OAEP",
  hash: "SHA-256"
}

Любое расхождение между ключом и параметрами encrypt приводит к ошибке выполнения.


Взаимосвязь с генерацией ключей

Корректная работа encrypt невозможна без согласованной генерации пары ключей:

const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["encrypt", "decrypt"]
);

Публичный ключ используется в encrypt, приватный — в decrypt.


Поток выполнения шифрования

  1. Подготовка данных (TextEncoder → ArrayBuffer)
  2. Выбор RSA-OAEP параметров (name, hash, label)
  3. Использование публичного ключа
  4. Вызов crypto.subtle.encrypt
  5. Получение бинарного шифротекста
  6. При необходимости — кодирование (Base64, Hex)

Особенности безопасности OAEP

RSA-OAEP включает несколько уровней защиты:

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

Это делает результат шифрования недетерминированным: одинаковый текст при повторном шифровании даёт разные выходные данные.


Практические ограничения архитектуры

RSA-OAEP используется не как основной механизм шифрования больших данных, а как способ защиты симметричных ключей.

Типичный паттерн:

  • RSA-OAEP → шифрование AES-ключа
  • AES-GCM → шифрование основного payload

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


Обработка результата на стороне расшифрования

Результат encrypt совместим только с:

crypto.subtle.decrypt({
  name: "RSA-OAEP",
  hash: "SHA-256"
}, privateKey, encryptedData);

Несовпадение параметров приводит к невозможности восстановления исходного сообщения, даже при корректном ключе.