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

Метод crypto.subtle.decrypt() в Web Crypto API выполняет асимметричное или симметричное расшифрование данных, в зависимости от выбранного алгоритма. Для схемы RSA-OAEP он применяется с приватным ключом и строго определённой конфигурацией параметров, соответствующей стандарту OAEP (Optimal Asymmetric Encryption Padding).

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


Сигнатура метода

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

Параметры:

  • algorithm — объект конфигурации алгоритма
  • key — приватный ключ типа CryptoKey
  • data — зашифрованные данные в формате ArrayBuffer

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

  • Promise<ArrayBuffer> с расшифрованным содержимым

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

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

{
  name: "RSA-OAEP",
  label: ArrayBuffer | Uint8Array (необязательный параметр)
}

Поле name

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

"RSA-OAEP"

Поле label

label используется как дополнительная аутентифицируемая информация (AAD). Если при шифровании использовалась метка, она должна совпадать при расшифровании.

  • может быть ArrayBuffer
  • может быть Uint8Array
  • может отсутствовать (чаще всего используется undefined)

Несовпадение label приводит к ошибке расшифрования.


Требования к ключу

Метод subtle.decrypt для RSA-OAEP требует приватный ключ:

CryptoKey {
  type: "private",
  algorithm: {
    name: "RSA-OAEP",
    hash: { name: "SHA-256" }
  },
  extractable: false,
  usages: ["decrypt"]
}

Ключ должен быть импортирован или сгенерирован через crypto.subtle.generateKey().


Импорт приватного ключа

Приватный ключ чаще всего импортируется в формате PKCS#8:

const privateKey = await crypto.subtle.importKey(
  "pkcs8",
  pemBuffer,
  {
    name: "RSA-OAEP",
    hash: "SHA-256"
  },
  false,
  ["decrypt"]
);

Ключевой момент — обязательное совпадение параметра hash с тем, который использовался при шифровании.


Процесс расшифрования

Типовой процесс RSA-OAEP-дешифрования состоит из нескольких этапов:

  1. получение зашифрованных данных (ciphertext)
  2. импорт приватного RSA ключа
  3. вызов crypto.subtle.decrypt
  4. декодирование результата из ArrayBuffer

Пример использования subtle.decrypt с RSA-OAEP

const decryptedBuffer = await crypto.subtle.decrypt(
  {
    name: "RSA-OAEP"
  },
  privateKey,
  encryptedData
);

const decoder = new TextDecoder();
const decryptedText = decoder.decode(decryptedBuffer);

Работа с label

Если при шифровании использовалась метка:

const algorithm = {
  name: "RSA-OAEP",
  label: new TextEncoder().encode("context-1")
};

то при расшифровании она должна быть идентичной:

const decrypted = await crypto.subtle.decrypt(
  {
    name: "RSA-OAEP",
    label: new TextEncoder().encode("context-1")
  },
  privateKey,
  encryptedData
);

Несовпадение байтового представления label приводит к ошибке OperationError.


Обработка ошибок

Метод возвращает Promise, который может быть отклонён при следующих условиях:

  • неверный ключ (не соответствует алгоритму RSA-OAEP)
  • повреждённые зашифрованные данные
  • несовпадение параметров OAEP (включая hash и label)
  • попытка использовать публичный ключ вместо приватного
  • неподдерживаемый формат данных

Типичная ошибка:

OperationError

Она является универсальной и не раскрывает деталей, что соответствует модели безопасности Web Crypto API.


Особенности алгоритма RSA-OAEP

RSA-OAEP в Web Crypto API использует гибридную схему:

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

Поддерживаемые хэш-алгоритмы:

  • SHA-1 (устаревший, но поддерживается)
  • SHA-256
  • SHA-384
  • SHA-512

Современные реализации используют SHA-256 как базовый стандарт.


Формат входных данных

data в subtle.decrypt должен быть строго ArrayBuffer.

Часто данные приходят в виде Base64:

function base64ToArrayBuffer(base64) {
  const binary = atob(base64);
  const bytes = new Uint8Array(binary.length);

  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }

  return bytes.buffer;
}

Преобразование результата

Расшифрованный результат также возвращается как ArrayBuffer. Для текстовых данных используется TextDecoder:

const text = new TextDecoder("utf-8").decode(decryptedBuffer);

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


Типовые ошибки реализации

Несовпадение параметров ключа

Ключ RSA-OAEP должен быть создан с тем же hash, что и при шифровании.

hash: "SHA-256"

Любое несоответствие делает расшифрование невозможным.


Использование неподходящего типа ключа

TypeError

возникает при передаче:

  • публичного ключа
  • неподдерживаемого CryptoKey
  • ключа с отсутствующим usage "decrypt"

Повреждение ciphertext

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


Безопасностные аспекты

RSA-OAEP считается криптографически стойким при корректной реализации:

  • обеспечивает семантическую безопасность
  • предотвращает детерминированность RSA
  • защищён от атак на padding при правильной настройке

Ключевые требования:

  • использование современных hash-функций
  • защита приватного ключа
  • отсутствие повторного использования nonce (в гибридных схемах)
  • корректная обработка ошибок без утечек информации

Совместимость и ограничения

Web Crypto API накладывает ряд ограничений:

  • доступен только в secure context (HTTPS)
  • недоступен в некоторых старых браузерах
  • приватные ключи нельзя экспортировать, если extractable: false
  • операции полностью асинхронные

Типовой сценарий RSA-OAEP decrypt

async function decryptRSA(ciphertext, privateKey) {
  const encryptedBuffer = base64ToArrayBuffer(ciphertext);

  const result = await crypto.subtle.decrypt(
    {
      name: "RSA-OAEP"
    },
    privateKey,
    encryptedBuffer
  );

  return new TextDecoder().decode(result);
}

Взаимодействие с encrypt

RSA-OAEP в Web Crypto API всегда используется парно:

  • crypto.subtle.encrypt() с публичным ключом
  • crypto.subtle.decrypt() с приватным ключом

Оба метода должны использовать:

  • одинаковый name
  • одинаковый hash
  • одинаковый label (если задан)

Несоответствие любой из этих частей делает систему несовместимой.