Криптографические протоколы поверх Web Crypto API

Web Crypto API предоставляет только низкоуровневые криптографические примитивы: генерацию ключей, симметричное и асимметричное шифрование, хеширование, подписи и работу с ключевым материалом. Он не реализует готовые протоколы безопасности. Любая реальная система — TLS-подобное соединение, end-to-end шифрование сообщений, защищённое хранение токенов — строится как композиция этих примитивов.

Криптографический протокол в этом контексте — это формализованный набор шагов, определяющий:

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

Web Crypto API становится строительным материалом, а протокол — архитектурой поверх него.


Ограничения Web Crypto API как основы протоколов

Перед построением протоколов важно учитывать фундаментальные ограничения API:

Отсутствие состояния протоколов

Web Crypto API не хранит контекст соединения. Он не знает:

  • какой этап рукопожатия выполняется
  • какой ключ относится к какому сообщению
  • как устроена сессия

Все состояния должны поддерживаться на уровне приложения.

Отсутствие транспорта

API не передаёт данные по сети. Он работает только с буферами:

  • ArrayBuffer
  • TypedArray

Любой протокол обязан самостоятельно определять формат сообщений и их передачу.

Отсутствие встроенного обмена ключами

Нет реализации Diffie-Hellman handshake как процесса. Есть лишь примитивы:

  • deriveKey
  • generateKey
  • importKey

Логика обмена ключами строится вручную.


Базовые строительные блоки протоколов

Любой криптографический протокол поверх Web Crypto API опирается на несколько ключевых операций.

Симметричное шифрование как основа сессии

Чаще всего используется AES-GCM:

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

AES-GCM одновременно обеспечивает:

  • конфиденциальность
  • целостность
  • аутентификацию данных

Это делает его стандартным выбором для защищённых каналов.


Асимметричная криптография для обмена ключами

Типичный выбор — ECDH:

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

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


Хеширование как инструмент целостности

const hash = await crypto.subtle.digest("SHA-256", data);

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

  • проверки целостности сообщений
  • построения идентификаторов
  • защиты от подмены

HMAC для аутентификации сообщений

const key = await crypto.subtle.importKey(
  "raw",
  secret,
  { name: "HMAC", hash: "SHA-256" },
  false,
  ["sign", "verify"]
);

HMAC применяется в протоколах для защиты от подделки сообщений без использования асимметрии.


Протокол обмена ключами на основе ECDH

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

Генерация ключевых пар

Каждая сторона создаёт свою пару:

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

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

Обмен публичными ключами

Публичные ключи экспортируются:

const clientPublic = await crypto.subtle.exportKey(
  "raw",
  clientKeyPair.publicKey
);

Передача происходит по открытому каналу.


Вычисление общего секрета

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

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

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


Протокол защищённого канала поверх AES-GCM

После установки общего секрета строится сессионный протокол.

Структура сообщения

Типичная структура:

  • nonce (уникальное значение)
  • ciphertext
  • authentication tag (встроен в AES-GCM)

Генерация nonce

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

Nonce должен быть уникальным для каждого сообщения в рамках одного ключа.


Шифрование сообщения

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

Дешифрование

const decrypted = await crypto.subtle.decrypt(
  {
    name: "AES-GCM",
    iv: nonce
  },
  sessionKey,
  encrypted
);

Протокол аутентификации сообщений

Для предотвращения подмены сообщений часто используется HMAC поверх шифрования.

Схема Encrypt-then-MAC

  1. Шифрование сообщения AES-GCM
  2. Вычисление HMAC от ciphertext
const mac = await crypto.subtle.sign(
  "HMAC",
  hmacKey,
  ciphertext
);

Проверка целостности

const valid = await crypto.subtle.verify(
  "HMAC",
  hmacKey,
  mac,
  ciphertext
);

Если проверка не проходит, сообщение отклоняется до расшифрования.


Протокол защиты от повторных атак (Replay Protection)

Web Crypto API не предоставляет встроенной защиты от replay-атак, поэтому она реализуется на уровне протокола.

Использование счетчика сообщений

Каждое сообщение содержит индекс:

{
  "seq": 42,
  "payload": "..."
}

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

Протокол хранит последний принятый индекс:

  • если seq <= lastSeq → сообщение отклоняется
  • если seq > lastSeq → обновление состояния

Протокол подписанных сообщений

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

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

const keyPair = await crypto.subtle.generateKey(
  { name: "Ed25519" },
  true,
  ["sign", "verify"]
);

Подписание сообщения

const signature = await crypto.subtle.sign(
  "Ed25519",
  privateKey,
  data
);

Проверка подписи

const isValid = await crypto.subtle.verify(
  "Ed25519",
  publicKey,
  signature,
  data
);

Подписи используются в протоколах:

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

Гибридные протоколы

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

Стандартная схема гибридного протокола

  1. ECDH — обмен ключами
  2. HKDF — вывод ключей сессии
  3. AES-GCM — шифрование данных
  4. HMAC или встроенная аутентификация — защита целостности

Вывод ключей через HKDF

const derivedKey = await crypto.subtle.deriveKey(
  {
    name: "HKDF",
    salt,
    info,
    hash: "SHA-256"
  },
  sharedSecret,
  { name: "AES-GCM", length: 256 },
  false,
  ["encrypt", "decrypt"]
);

HKDF позволяет избежать использования сырых ECDH-значений напрямую.


Протоколы поверх WebSocket + Web Crypto API

Частый сценарий — защищённый WebSocket-канал.

Этапы протокола:

  1. установление WebSocket соединения
  2. обмен ECDH публичными ключами
  3. вывод session key
  4. шифрование всех сообщений AES-GCM

Формат сообщения

{
  "iv": "...",
  "data": "...",
  "tag": "...",
  "seq": 10
}

Ошибки проектирования криптографических протоколов

При построении протоколов на Web Crypto API часто возникают критические ошибки:

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

В AES-GCM это полностью разрушает безопасность.

Отсутствие проверки целостности

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

Использование неподходящих алгоритмов

RSA-OAEP или SHA-1 в современных протоколах создают слабые места.

Отсутствие защиты от replay

Даже идеально зашифрованные сообщения можно повторно отправить.


Слой протокола как архитектурная единица

Поверх Web Crypto API всегда существует абстракция:

  • транспортный слой (WebSocket / HTTP)
  • криптографический слой (Web Crypto API)
  • протокольный слой (handshake, сессии, ключи)
  • прикладной слой (логика приложения)

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


Принципы построения устойчивых протоколов

Любая реализация должна учитывать:

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

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