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

В реальных системах редко применяется одиночный криптографический примитив. Даже стойкие алгоритмы становятся уязвимыми при неправильном использовании или недостаточном наборе гарантий. Поэтому практическая криптография строится на композиции нескольких механизмов: шифрования, обмена ключами, аутентификации, деривации ключей и защиты целостности.

Web Crypto API предоставляет низкоуровневые операции, из которых собираются составные схемы. Библиотека не навязывает архитектуру, но предоставляет строительные блоки: SubtleCrypto.encrypt, decrypt, sign, verify, deriveKey, importKey, wrapKey, unwrapKey.


Принцип композиции криптографических примитивов

Составная схема объединяет несколько задач:

  • конфиденциальность (encryption)
  • целостность (integrity)
  • аутентичность (authentication)
  • управление ключами (key management)

Один алгоритм редко покрывает всё сразу. Например:

  • AES-GCM обеспечивает и шифрование, и целостность
  • RSA-OAEP обеспечивает только шифрование ключей
  • ECDH обеспечивает согласование ключа, но не защиту данных

Композиция позволяет строить безопасные системы из ограниченных компонентов.


Гибридное шифрование (KEM-DEM)

Классическая модель составных схем — гибридное шифрование:

  • KEM (Key Encapsulation Mechanism) — передача или согласование ключа
  • DEM (Data Encapsulation Mechanism) — симметричное шифрование данных

В Web Crypto API это реализуется через комбинацию ECDH или RSA + AES-GCM.


Схема ECDH + AES-GCM (envelope encryption)

Один из наиболее распространённых паттернов — согласование общего секрета через ECDH и дальнейшее шифрование симметричным ключом.

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

const aliceKeys = await crypto.subtle.generateKey(
  {
    name: "ECDH",
    namedCurve: "P-256"
  },
  true,
  ["deriveKey"]
);

const bobKeys = await crypto.subtle.generateKey(
  {
    name: "ECDH",
    namedCurve: "P-256"
  },
  true,
  ["deriveKey"]
);

Получение общего ключа AES-GCM

const sharedKey = await crypto.subtle.deriveKey(
  {
    name: "ECDH",
    public: bobKeys.publicKey
  },
  aliceKeys.privateKey,
  {
    name: "AES-GCM",
    length: 256
  },
  false,
  ["encrypt", "decrypt"]
);

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

const iv = crypto.getRandomValues(new Uint8Array(12));

const ciphertext = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv
  },
  sharedKey,
  new TextEncoder().encode("секретное сообщение")
);

Такая схема обеспечивает:

  • секретность через AES-GCM
  • согласование ключа через ECDH
  • отсутствие передачи ключа по сети

RSA-OAEP + AES-GCM (гибрид с асимметрией)

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

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

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

Создание симметричного ключа

const aesKey = await crypto.subtle.generateKey(
  {
    name: "AES-GCM",
    length: 256
  },
  true,
  ["encrypt", "decrypt"]
);

Оборачивание ключа (key wrapping)

const wrappedKey = await crypto.subtle.wrapKey(
  "raw",
  aesKey,
  rsaKeys.publicKey,
  {
    name: "RSA-OAEP"
  }
);

Разворачивание ключа

const unwrappedKey = await crypto.subtle.unwrapKey(
  "raw",
  wrappedKey,
  rsaKeys.privateKey,
  {
    name: "RSA-OAEP"
  },
  {
    name: "AES-GCM",
    length: 256
  },
  true,
  ["encrypt", "decrypt"]
);

Такая схема часто используется в:

  • защищённой передаче файлов
  • PGP-подобных системах
  • TLS-подобных архитектурах на прикладном уровне

Аутентифицированное шифрование как основа композиции

Современные схемы стремятся к минимизации количества примитивов. AES-GCM объединяет:

  • шифрование
  • контроль целостности
  • проверку подлинности
const encrypted = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv,
    additionalData: new TextEncoder().encode("header")
  },
  key,
  data
);

Параметр additionalData позволяет включать несекретные, но защищённые данные (AAD), которые участвуют в проверке целостности.


Encrypt-then-MAC как составная модель

До появления AEAD-режимов часто применялась схема:

  1. шифрование данных
  2. вычисление HMAC
const ciphertext = await crypto.subtle.encrypt(
  { name: "AES-CBC", iv },
  encKey,
  data
);

const mac = await crypto.subtle.sign(
  { name: "HMAC" },
  macKey,
  ciphertext
);

Проверка:

const valid = await crypto.subtle.verify(
  { name: "HMAC" },
  macKey,
  mac,
  ciphertext
);

Web Crypto API поддерживает HMAC отдельно, что позволяет строить такие композиции, хотя AES-GCM делает их избыточными в новых системах.


HKDF и производные ключи в составных схемах

Ключи редко используются напрямую. Обычно они выводятся из мастер-секрета через HKDF.

const derivedKey = await crypto.subtle.deriveKey(
  {
    name: "HKDF",
    salt: crypto.getRandomValues(new Uint8Array(16)),
    info: new TextEncoder().encode("encryption context"),
    hash: "SHA-256"
  },
  masterKey,
  {
    name: "AES-GCM",
    length: 256
  },
  false,
  ["encrypt", "decrypt"]
);

HKDF используется для:

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

Ключевые обёртки и иерархия ключей

Составные схемы часто строятся как иерархия:

  • root key (мастер-ключ)
  • key encryption key (KEK)
  • data encryption key (DEK)

DEK шифрует данные, KEK защищает DEK.

const wrappedDEK = await crypto.subtle.wrapKey(
  "raw",
  dek,
  kek,
  { name: "AES-KW" }
);

AES-KW (Key Wrap) используется для безопасного хранения ключей без необходимости RSA.


Sign-then-encrypt и encrypt-then-sign

Композиция подписи и шифрования требует строгого порядка операций.

Sign-then-encrypt

const signature = await crypto.subtle.sign(
  { name: "ECDSA", hash: "SHA-256" },
  privateKey,
  data
);

const payload = new Uint8Array([...data, ...new Uint8Array(signature)]);

Encrypt-then-sign

const encrypted = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  encKey,
  data
);

const signature = await crypto.subtle.sign(
  { name: "ECDSA", hash: "SHA-256" },
  signingKey,
  encrypted
);

В современных архитектурах предпочтение отдается encrypt-then-sign, так как подпись применяется к защищённым данным.


Слоистые схемы и контекстная изоляция

Составные криптографические схемы часто включают несколько уровней защиты:

  • транспортный уровень (TLS)
  • прикладное шифрование (WebCrypto)
  • шифрование полей (field-level encryption)

Каждый слой использует собственные ключи и контекст.

Пример изоляции:

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

Ошибки при построении составных схем

Типовые проблемы возникают не в алгоритмах, а в композиции:

  • повторное использование IV в AES-GCM
  • отсутствие изоляции ключей по контекстам
  • смешивание подписанных и неподписанных данных
  • неправильный порядок sign/encrypt
  • хранение мастер-ключей без иерархии

Web Crypto API снижает вероятность ошибок, но не устраняет архитектурные риски.


Практическая модель составной схемы в Web Crypto API

Наиболее устойчивый подход включает:

  • ECDH для согласования ключа
  • HKDF для деривации
  • AES-GCM для шифрования
  • ECDSA для подписи
  • wrapKey для хранения ключей

Такая комбинация формирует типовую современную криптографическую архитектуру в браузерных приложениях без зависимости от внешних библиотек.