Метод subtle.deriveKey

subtle.deriveKey — метод Web Crypto API, предназначенный для получения криптографического ключа на основе исходного ключевого материала. Используется в сценариях, где требуется преобразование общего секрета или пароля в ключ для симметричного шифрования или HMAC.

Метод работает асинхронно и возвращает Promise, который резолвится в объект CryptoKey. Все операции выполняются внутри криптографического контекста браузера и не позволяют извлекать сырой материал ключа при корректных параметрах (extractable: false).


crypto.subtle.deriveKey(
  algorithm,
  baseKey,
  derivedKeyAlgorithm,
  extractable,
  keyUsages
)

Параметры

algorithm

Объект, описывающий используемый алгоритм деривации ключа. Поддерживаемые варианты:

  • PBKDF2
  • ECDH
  • HKDF

Каждый алгоритм требует собственную структуру параметров.

PBKDF2

{
  name: "PBKDF2",
  salt: Uint8Array,
  iterations: Number,
  hash: "SHA-256" | "SHA-384" | "SHA-512"
}

HKDF

{
  name: "HKDF",
  salt: Uint8Array,
  info: Uint8Array,
  hash: "SHA-256" | "SHA-384" | "SHA-512"
}

ECDH

{
  name: "ECDH",
  public: CryptoKey
}

baseKey

Исходный ключ (CryptoKey), из которого выполняется деривация.

Тип зависит от алгоритма:

  • PBKDF2 → importKey с типом "raw" (обычно пароль в виде байтов)
  • ECDH → приватный ключ ECDH
  • HKDF → ключ, полученный через importKey

derivedKeyAlgorithm

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

{
  name: "AES-GCM" | "AES-CBC" | "HMAC",
  length: 128 | 192 | 256
}

Для HMAC:

{
  name: "HMAC",
  hash: "SHA-256",
  length: 256
}

extractable

Булевое значение, определяющее возможность извлечения ключа:

  • true — ключ может быть экспортирован
  • false — ключ остаётся недоступным для экспорта

В криптографических системах безопасности чаще используется false.


keyUsages

Массив строк, определяющий допустимые операции с ключом:

  • "encrypt"
  • "decrypt"
  • "sign"
  • "verify"
  • "deriveKey"
  • "deriveBits"
  • "wrapKey"
  • "unwrapKey"

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

Promise<CryptoKey>

Результатом является симметрический ключ, пригодный для операций шифрования или вычисления HMAC.


PBKDF2: деривация из пароля

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

Пример

const password = "user-password";
const enc = new TextEncoder();

const baseKey = await crypto.subtle.importKey(
  "raw",
  enc.encode(password),
  "PBKDF2",
  false,
  ["deriveKey"]
);

const salt = crypto.getRandomValues(new Uint8Array(16));

const key = await crypto.subtle.deriveKey(
  {
    name: "PBKDF2",
    salt: salt,
    iterations: 100000,
    hash: "SHA-256"
  },
  baseKey,
  {
    name: "AES-GCM",
    length: 256
  },
  false,
  ["encrypt", "decrypt"]
);

Особенности PBKDF2

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

HKDF: производная ключей из исходного секрета

HKDF применяется для безопасного расширения ключевого материала, например после ECDH.

Пример

const enc = new TextEncoder();

const baseKey = await crypto.subtle.importKey(
  "raw",
  enc.encode("shared-secret"),
  "HKDF",
  false,
  ["deriveKey"]
);

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

Особенности HKDF

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

ECDH: получение ключа из эллиптической криптографии

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

Пример

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

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

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

Особенности ECDH

  • требует пары ключей (приватный + публичный)
  • используется в защищённых каналах обмена данными
  • обеспечивает прямую криптографическую согласованность между сторонами

Ограничения и поведение

  • операция полностью асинхронна
  • доступна только в secure context (HTTPS)
  • базовый ключ должен иметь право "deriveKey"
  • итоговый ключ привязан к контексту выполнения и не может быть восстановлен вне Web Crypto API

Типовые ошибки использования

Неверный алгоритм базового ключа

Если ключ импортирован без "deriveKey":

InvalidAccessError

Несовместимость алгоритмов

Например, попытка получить AES-GCM ключ из ECDH без корректной структуры параметров приводит к ошибке OperationError.


Ошибки PBKDF2

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

Модель безопасности

subtle.deriveKey опирается на следующие принципы:

  • неизвлекаемость ключей при extractable: false
  • изоляция криптографического контекста браузера
  • аппаратное ускорение при наличии поддержки CPU/OS
  • запрет прямого доступа к сырому секрету при операциях ECDH/HKDF/PBKDF2

Практическое применение

  • генерация ключей шифрования файлов на основе пароля пользователя
  • создание сессионных ключей в защищённых каналах связи
  • производные ключи в протоколах обмена сообщениями
  • изоляция ключей по назначению (encryption / signing / authentication)