ECDH (Elliptic Curve Diffie–Hellman) в Web Crypto API используется
для выработки общего секрета между двумя сторонами без передачи
секретного значения по сети. Метод crypto.subtle.deriveKey
позволяет не только получить сырое значение общего секрета, но и сразу
преобразовать его в ключ для симметричного шифрования, например AES-GCM
или AES-CBC.
ECDH в контексте Web Crypto реализуется через объектный интерфейс ключей, где публичная и приватная части строго разделены и управляются браузером или средой исполнения.
Процесс обмена ключами состоит из нескольких этапов:
deriveKeyКлючевой момент: общий секрет напрямую не используется в приложении, он превращается в криптографический ключ для выбранного алгоритма.
ECDH в Web Crypto требует использования именованных кривых, чаще
всего P-256, P-384 или P-521.
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"]
);
Каждый объект содержит:
privateKey — секретная частьpublicKey — открытая частьДля того чтобы стороны могли использовать ECDH, публичный ключ необходимо передать другой стороне.
const alicePublic = await crypto.subtle.exportKey(
"raw",
aliceKeys.publicKey
);
const bobPublic = await crypto.subtle.exportKey(
"raw",
bobKeys.publicKey
);
Далее данные передаются через сеть, WebSocket, HTTP или любой другой канал.
После получения они импортируются обратно:
const importedBobPublic = await crypto.subtle.importKey(
"raw",
bobPublic,
{
name: "ECDH",
namedCurve: "P-256"
},
true,
[]
);
subtle.deriveKey с ECDHОсновная операция происходит через deriveKey. Она
принимает приватный ключ одной стороны и публичный ключ другой
стороны.
Сигнатура:
crypto.subtle.deriveKey(
algorithm,
baseKey,
derivedKeyType,
extractable,
keyUsages
)
Для ECDH:
algorithm:{
name: "ECDH",
public: CryptoKey
}
baseKey: приватный ключ ECDHderivedKeyType: алгоритм результата (например
AES-GCM)extractable: можно ли извлечь ключkeyUsages: назначение ключа (encrypt, decrypt и
т.д.)const sharedKeyAlice = await crypto.subtle.deriveKey(
{
name: "ECDH",
public: importedBobPublic
},
aliceKeys.privateKey,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
Аналогично на стороне B:
const sharedKeyBob = await crypto.subtle.deriveKey(
{
name: "ECDH",
public: alicePublicKeyImported,
},
bobKeys.privateKey,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
Результат:
sharedKeyAlice и sharedKeyBob будут
одинаковымиПосле деривации ключ можно использовать как обычный симметричный ключ:
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv
},
sharedKeyAlice,
new TextEncoder().encode("секретное сообщение")
);
Расшифрование:
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv
},
sharedKeyBob,
encrypted
);
deriveKey
от deriveBitsВ ECDH Web Crypto API доступны два подхода:
deriveBits — возвращает «сырой» массив байтов общего
секретаderiveKey — сразу создаёт CryptoKey для симметричного
алгоритмаconst rawBits = await crypto.subtle.deriveBits(
{
name: "ECDH",
public: importedBobPublic
},
aliceKeys.privateKey,
256
);
deriveKey предпочтительнее, потому что:
Чаще всего используются:
Пример:
{
name: "AES-GCM",
length: 256
}
Важно: длина ключа должна соответствовать требованиям алгоритма.
Обе стороны обязаны использовать одну и ту же кривую:
deriveKey требует:
CryptoKey с usage
"deriveKey"Если extractable: false, ключ нельзя экспортировать —
это нормальное поведение для защиты.
ECDH в Web Crypto безопасен при соблюдении базовых условий:
Дополнительно:
Сценарий взаимодействия обычно выглядит так:
deriveKeyТакой подход обеспечивает сквозное шифрование без передачи симметричного ключа.
deriveKey при повторном вызовеЕсли повторно вызвать deriveKey с теми же ключами:
Хотя WebCrypto не требует ручного KDF, ECDH можно рассматривать как базу для дальнейшего вывода ключей:
В более сложных системах поверх ECDH могут использоваться
дополнительные слои (например, протоколы наподобие Signal), но WebCrypto
скрывает часть этой сложности внутри deriveKey.