Метод subtle.sign для HMAC

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

Метод subtle.sign выполняет вычисление цифровой подписи или MAC (message authentication code) в зависимости от выбранного алгоритма. В случае HMAC он не создает классическую асимметричную подпись, а генерирует код аутентификации сообщения с использованием общего секретного ключа.

В Web Crypto API HMAC реализуется как часть алгоритмов симметричной криптографии. Ключевой элемент — объект CryptoKey, который создается или импортируется заранее и используется как секретный материал.

Основные характеристики HMAC в WebCrypto:

  • Используется симметричный ключ
  • Поддерживаются хеш-функции SHA-256, SHA-384, SHA-512
  • Результат операции — ArrayBuffer
  • Подпись зависит от входных данных и ключа

Алгоритм задается объектом:

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

Метод subtle.sign: общая концепция

crypto.subtle.sign() применяется для вычисления криптографического значения на основе входных данных и ключа.

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

crypto.subtle.sign(algorithm, key, data)

Параметры:

  • algorithm — объект с настройками HMAC
  • key — объект CryptoKey с назначением sign
  • data — данные в виде ArrayBuffer, TypedArray или DataView

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

  • Promise<ArrayBuffer> — бинарная подпись

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

Перед использованием subtle.sign необходимо создать ключ.

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

Параметры:

  • extractable: false — ключ нельзя экспортировать
  • keyUsages — определяет допустимые операции

Подготовка данных

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

const encoder = new TextEncoder();
const data = encoder.encode("Сообщение для подписи");

TextEncoder преобразует строку в Uint8Array, который совместим с subtle.sign.

Выполнение HMAC через subtle.sign

Основная операция вычисления HMAC:

const signature = await crypto.subtle.sign(
  {
    name: "HMAC"
  },
  key,
  data
);

Результат — ArrayBuffer, содержащий бинарный MAC.

Для удобства часто преобразуют результат в hex:

const hashArray = Array.from(new Uint8Array(signature));
const hex = hashArray
  .map(b => b.toString(16).padStart(2, "0"))
  .join("");

Проверка подписи через subtle.verify

HMAC в WebCrypto обычно используется вместе с проверкой:

const isValid = await crypto.subtle.verify(
  {
    name: "HMAC"
  },
  key,
  signature,
  data
);

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

  • true — подпись корректна
  • false — данные или ключ изменены

Важные особенности subtle.sign для HMAC

1. Симметричность ключа

В отличие от RSA или ECDSA, HMAC использует один и тот же ключ для подписи и проверки. Это упрощает реализацию, но требует безопасного хранения ключа.

2. Невозможность частичного восстановления данных

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

3. Зависимость от алгоритма хеширования

Выбор SHA-256, SHA-384 или SHA-512 влияет на:

  • длину результата
  • криптостойкость
  • производительность

SHA-256 является наиболее распространенным вариантом.

Работа с разными типами входных данных

subtle.sign принимает несколько типов бинарных данных:

Uint8Array

const data = new Uint8Array([1, 2, 3, 4]);

ArrayBuffer

const buffer = new ArrayBuffer(16);

DataView

const view = new DataView(buffer);

Во всех случаях данные интерпретируются как последовательность байтов.

Импорт существующего HMAC-ключа

Часто ключ не генерируется, а импортируется:

const rawKey = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);

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

Импорт используется при:

  • синхронизации с сервером
  • восстановлении ключей
  • межсистемной совместимости

Типичные ошибки при использовании subtle.sign

Неверный формат данных

Передача строки вместо бинарного массива приводит к ошибке. WebCrypto не выполняет автоматическое преобразование.

Несовместимый ключ

Ключ должен быть создан или импортирован с usages: ["sign"]. Иначе вызов завершится исключением.

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

Алгоритм при вызове sign должен совпадать с алгоритмом ключа.

Практический пример полного цикла HMAC

const encoder = new TextEncoder();
const data = encoder.encode("auth message");

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

const signature = await crypto.subtle.sign(
  "HMAC",
  key,
  data
);

const isValid = await crypto.subtle.verify(
  "HMAC",
  key,
  signature,
  data
);

Производительность subtle.sign при HMAC

HMAC в Web Crypto API оптимизирован на уровне браузерной реализации и обычно работает быстрее, чем пользовательские реализации на JavaScript. Однако производительность зависит от:

  • длины входных данных
  • выбранной хеш-функции
  • загрузки основного потока

Операции выполняются асинхронно, не блокируя UI.

Безопасность использования HMAC через WebCrypto

HMAC считается криптографически стойким при соблюдении условий:

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

Web Crypto API обеспечивает:

  • изоляцию ключей внутри браузера
  • невозможность прямого извлечения ключа (при extractable: false)
  • защиту от прямого доступа JavaScript к криптографическому материалу

Сравнение subtle.sign и альтернативных подходов

До появления Web Crypto API HMAC часто реализовывался вручную на JavaScript. Это приводило к:

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

subtle.sign устраняет эти проблемы, так как:

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