ECDH: протокол Диффи-Хеллмана на эллиптических кривых

ECDH (Elliptic Curve Diffie–Hellman) представляет собой механизм установления общего секрета между двумя сторонами через открытый канал связи без предварительного обмена секретами. В основе лежит математика эллиптических кривых, позволяющая получать одинаковый результат у двух участников при обмене публичными ключами, при этом восстановить приватные ключи или общий секрет из перехваченных данных вычислительно крайне затруднительно.

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

Протокол строится вокруг пары ключей:

  • приватный ключ (secret key), известный только владельцу
  • публичный ключ (public key), свободно передаваемый

Каждая сторона генерирует собственную пару ключей. После обмена публичными ключами происходит вычисление общего секрета:

  • сторона A: S = f(privA, pubB)
  • сторона B: S = f(privB, pubA)

Благодаря свойствам эллиптических кривых значения совпадают: S_A = S_B.

Поддерживаемые кривые в Web Crypto API

В SubtleCrypto ECDH использует параметр namedCurve. Наиболее распространённые значения:

  • P-256 (secp256r1)
  • P-384
  • P-521

Каждая кривая определяет уровень криптографической стойкости и производительность. На практике чаще всего используется P-256 как баланс между безопасностью и скоростью.

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

Создание ключей выполняется через crypto.subtle.generateKey.

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

Параметры:

  • name: "ECDH" — указывает алгоритм
  • namedCurve — выбранная эллиптическая кривая
  • extractable: false — ключ нельзя экспортировать (повышение безопасности)
  • keyUsages — допустимые операции, чаще всего deriveKey и deriveBits

Результат содержит:

  • keyPair.publicKey
  • keyPair.privateKey

Экспорт и импорт ключей

Для передачи публичного ключа используется экспорт в формат JWK или SPKI.

Экспорт публичного ключа

const exportedPublicKey = await crypto.subtle.exportKey(
  "jwk",
  keyPair.publicKey
);

JWK (JSON Web Key) удобен для передачи через API или хранение в JSON.

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

const importedPublicKey = await crypto.subtle.importKey(
  "jwk",
  exportedPublicKey,
  {
    name: "ECDH",
    namedCurve: "P-256"
  },
  true,
  []
);

Импортированный ключ может использоваться для вычисления общего секрета.

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

Основная операция ECDH — получение общего массива байтов.

const sharedBits = await crypto.subtle.deriveBits(
  {
    name: "ECDH",
    public: importedPublicKey
  },
  keyPair.privateKey,
  256
);

Параметры:

  • public — публичный ключ второй стороны
  • privateKey — собственный приватный ключ
  • length — длина результата в битах

Результат sharedBitsArrayBuffer, содержащий криптографический секрет.

Использование deriveKey для получения симметричного ключа

Часто результат ECDH не используется напрямую. Вместо этого он применяется для генерации симметрического ключа (например AES-GCM).

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

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

Типичный сценарий обмена ключами

Процесс установления общего секрета включает последовательность шагов:

  1. Генерация пары ключей на стороне клиента A
  2. Генерация пары ключей на стороне клиента B
  3. Обмен публичными ключами через канал связи
  4. Вычисление общего секрета на обеих сторонах
  5. Использование результата для симметричного шифрования

Важно, что приватные ключи никогда не покидают устройство.

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

Web Crypto API использует несколько стандартных форматов:

  • raw — бинарный формат (для некоторых кривых)
  • jwk — JSON Web Key
  • spki — публичный ключ X.509
  • pkcs8 — приватные ключи

ECDH в браузере чаще всего опирается на jwk, поскольку он легко сериализуется.

Безопасность реализации ECDH

Несмотря на математическую стойкость, безопасность зависит от корректного использования API:

Недопустимо:

  • повторное использование приватных ключей в разных контекстах без необходимости
  • передача приватных ключей между устройствами
  • использование слабых или неподдерживаемых кривых
  • применение необработанного shared secret без KDF

Рекомендуется:

  • использовать deriveKey вместо прямого deriveBits
  • применять KDF (например HKDF через Web Crypto API)
  • ограничивать экспорт ключей
  • использовать P-256 или выше

Усиление безопасности через HKDF

Общий секрет ECDH обычно не используется напрямую, поскольку он не имеет равномерного распределения для симметричных алгоритмов. Для нормализации применяется HKDF.

Пример цепочки:

  1. ECDH → shared secret
  2. HKDF → производный ключ AES
const baseKey = await crypto.subtle.importKey(
  "raw",
  sharedBits,
  "HKDF",
  false,
  ["deriveKey"]
);

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

Особенности реализации в браузере

Web Crypto API работает в контексте безопасности:

  • доступен только в secure context (HTTPS)
  • операции асинхронные
  • ключи могут быть неэкспортируемыми
  • криптографические примитивы ограничены стандартом

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

Ошибки и ограничения

На практике часто возникают следующие проблемы:

Несовместимость кривых

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

  • P-256 ≠ P-384 → вычисление невозможно

Неверный тип ключа

ECDH требует строго ключей типа CryptoKey с алгоритмом "ECDH".

Попытка использовать raw secret без обработки

Некоторые реализации ожидают фиксированную длину ключа, что требует нормализации через KDF.

Практическая модель использования в протоколах

ECDH редко используется изолированно. Он является частью более сложных протоколов:

  • TLS (обмен ключами в HTTPS)
  • Signal Protocol (мессенджеры)
  • WebRTC (установление защищённых каналов)
  • end-to-end encryption схемы

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

Структура данных ключей в Web Crypto

Внутреннее представление ключа скрыто, но логически можно выделить:

  • алгоритм (ECDH)
  • кривая (namedCurve)
  • тип (public/private)
  • ограничения использования (keyUsages)
  • возможность экспорта (extractable)

Эта модель унифицирует работу с различными криптографическими алгоритмами через единый API.

Производительность и выбор параметров

Кривые влияют на скорость:

  • P-256 — оптимальный баланс
  • P-384 — выше безопасность, ниже скорость
  • P-521 — максимальная стойкость, более тяжёлые вычисления

На клиентских устройствах чаще выбирается P-256 из-за производительности и широкого аппаратного ускорения.

Взаимодействие с другими алгоритмами Web Crypto

ECDH часто комбинируется с:

  • AES-GCM (симметричное шифрование)
  • SHA-256 / SHA-384 (хеширование)
  • HKDF (деривация ключей)
  • RSA (для гибридных схем)

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