Метод subtle.deriveBits

Метод subtle.deriveBits используется для получения сырых байтов ключевого материала из базового криптографического ключа и параметров алгоритма. В отличие от deriveKey, который сразу возвращает объект CryptoKey, этот метод возвращает ArrayBuffer с бинарными данными, которые затем можно использовать для собственных схем ключеобразования, HMAC-секретов, seed-значений или любых других криптографических конструкций.

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


Сигнатура метода

const bits = await crypto.subtle.deriveBits(
  algorithm,
  baseKey,
  length
);

Параметры:

  • algorithm — объект, описывающий алгоритм и его параметры
  • baseKey — исходный CryptoKey, из которого выводятся биты
  • length — количество бит, которые необходимо получить

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

  • Promise<ArrayBuffer> — буфер с производными битами

Когда используется deriveBits

Метод применяется в ситуациях, когда:

  • необходимо получить общий секрет из протоколов обмена ключами (например, ECDH)
  • требуется промежуточное значение для дальнейшего KDF (Key Derivation Function)
  • нужно вручную реализовать схему деривации ключей
  • требуется контроль над форматом результата (в отличие от deriveKey)

Поддерживаемые алгоритмы

На практике deriveBits чаще всего используется с двумя типами алгоритмов:

ECDH (Elliptic Curve Diffie-Hellman)

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

const algorithm = {
  name: "ECDH",
  public: publicKey
};

Здесь baseKey — это приватный ключ локальной стороны, а public — публичный ключ другой стороны.


PBKDF2

Используется для деривации ключей из пароля.

const algorithm = {
  name: "PBKDF2",
  salt: saltBuffer,
  iterations: 100000,
  hash: "SHA-256"
};

baseKey в этом случае — ключ, полученный из пароля через importKey.


HKDF

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

const algorithm = {
  name: "HKDF",
  salt: saltBuffer,
  info: infoBuffer,
  hash: "SHA-256"
};

ECDH: базовый пример использования deriveBits

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

const privateKey = await crypto.subtle.importKey(
  "pkcs8",
  privateKeyBuffer,
  {
    name: "ECDH",
    namedCurve: "P-256"
  },
  true,
  ["deriveBits"]
);

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

Результат sharedBits — это ArrayBuffer длиной 256 бит, содержащий общий секрет.


PBKDF2: получение ключевого материала из пароля

const baseKey = await crypto.subtle.importKey(
  "raw",
  new TextEncoder().encode("user-password"),
  "PBKDF2",
  false,
  ["deriveBits"]
);

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

const derivedBits = await crypto.subtle.deriveBits(
  {
    name: "PBKDF2",
    salt: salt,
    iterations: 150000,
    hash: "SHA-256"
  },
  baseKey,
  256
);

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

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

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

HKDF строится вокруг идеи извлечения и расширения ключа. В deriveBits чаще используется стадия expansion.

const baseKey = await crypto.subtle.importKey(
  "raw",
  inputKeyMaterial,
  "HKDF",
  false,
  ["deriveBits"]
);

const derived = await crypto.subtle.deriveBits(
  {
    name: "HKDF",
    salt: saltBuffer,
    info: new TextEncoder().encode("session-key"),
    hash: "SHA-256"
  },
  baseKey,
  256
);

Отличие deriveBits от deriveKey

Характеристика deriveBits deriveKey
Результат ArrayBuffer CryptoKey
Уровень низкий высокий
Использование кастомные схемы готовые алгоритмы
Гибкость максимальная ограниченная

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


Ограничения метода

  • требует корректных прав доступа у CryptoKey (["deriveBits"])
  • поддержка алгоритмов зависит от браузера
  • длина результата может быть ограничена алгоритмом
  • нельзя получить криптографический ключ напрямую — только байты

Работа с результатом

Результат ArrayBuffer часто преобразуется в удобный формат:

const bytes = new Uint8Array(derivedBits);

Для использования в криптографических операциях часто применяют:

  • Base64 кодирование
  • Hex представление
  • повторное импортирование через importKey

Пример преобразования в hex:

function toHex(buffer) {
  return [...new Uint8Array(buffer)]
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

Практический сценарий: построение session key

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

  1. ECDH → получение shared secret
  2. HKDF → расширение секретa
  3. AES-GCM → шифрование данных

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


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

Неправильная длина

Некоторые алгоритмы ожидают длину, кратную 8 битам. Передача нестандартных значений может привести к исключению.

Использование неподходящего ключа

baseKey должен быть создан с нужными usages:

["deriveBits"]

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

Например, использование ECDH с неподдерживаемой кривой приведёт к ошибке выполнения.


Важные особенности безопасности

  • deriveBits не хранит результат — его нужно сразу использовать или безопасно сохранять
  • секреты в памяти JavaScript доступны для утечек через XSS
  • криптографическая стойкость зависит от выбранного алгоритма (особенно PBKDF2)

Типичный паттерн использования в архитектуре приложений

  • генерация ключевого материала на клиенте
  • передача производного значения в API (в виде хэша или токена)
  • использование как input для симметричного шифрования
  • создание уникальных session keys без передачи секретов по сети