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

HMAC (Hash-based Message Authentication Code) в Web Crypto API реализуется через интерфейс SubtleCrypto и используется для создания симметричных ключей, предназначенных для проверки целостности и подлинности данных на основе криптографической хэш-функции.

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


Алгоритм HMAC в контексте WebCrypto

В Web Crypto API HMAC определяется через объект алгоритма:

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

Ключевым параметром является hash, который определяет используемую хэш-функцию. Поддерживаемые варианты:

  • SHA-1 (не рекомендуется)
  • SHA-256 (наиболее распространённый выбор)
  • SHA-384
  • SHA-512

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

Для создания HMAC-ключа используется метод:

crypto.subtle.generateKey(algorithm, extractable, keyUsages)

Основная структура вызова

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

Параметры generateKey для HMAC

algorithm

Описывает тип ключа и параметры хэширования:

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

Значение name строго фиксировано как "HMAC".


extractable

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

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

На практике для продакшн-систем часто выбирается false, чтобы минимизировать риск утечки ключа.


keyUsages

Определяет, какие операции разрешены с ключом:

  • "sign" — создание HMAC подписи
  • "verify" — проверка подписи
["sign", "verify"]

Если указать лишние или отсутствующие значения, браузер выбросит InvalidAccessError.


Полный пример генерации HMAC-ключа

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

  return key;
}

Тип возвращаемого значения

generateKey возвращает объект типа CryptoKey.

Для HMAC он имеет следующие свойства:

  • type: "secret"
  • extractable: true | false
  • algorithm: { name: "HMAC", hash: ... }
  • usages: ["sign", "verify"]

Использование с подписями

После генерации ключа он используется через:

Подпись данных

async function signData(key, data) {
  const encoder = new TextEncoder();
  const encoded = encoder.encode(data);

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

  return signature;
}

Проверка подписи

async function verifySignature(key, signature, data) {
  const encoder = new TextEncoder();
  const encoded = encoder.encode(data);

  const result = await crypto.subtle.verify(
    "HMAC",
    key,
    signature,
    encoded
  );

  return result;
}

Особенности HMAC-ключей в WebCrypto

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

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


2. Невозможность прямого просмотра ключа

Если extractable установлен в false, ключ невозможно получить в виде строки или массива байт.

Попытка экспорта:

crypto.subtle.exportKey("raw", key);

приведёт к ошибке InvalidAccessError.


3. Зависимость от хэш-функции

HMAC-ключ жёстко связан с выбранной хэш-функцией. Ключ, созданный для SHA-256, нельзя использовать с SHA-512 без повторной генерации.


Экспортируемые HMAC-ключи

Если ключ создан с extractable: true, его можно экспортировать:

const rawKey = await crypto.subtle.exportKey("raw", key);

Результат — ArrayBuffer.


Импорт HMAC-ключа

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

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

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

Проверка целостности сообщений

HMAC часто применяется для проверки неизменности данных при передаче:

  • API-запросы
  • локальные токены
  • обмен сообщениями между клиентом и сервером

Защита локальных данных

В браузере HMAC может использоваться для:

  • проверки localStorage
  • защиты sessionStorage
  • контроля целостности конфигураций

Подпись запросов

Пример формирования подписи:

const data = "userId=42&action=login";
const signature = await signData(key, data);

Далее подпись передаётся вместе с запросом и проверяется на сервере.


Ошибки при генерации ключа

Unsupported algorithm

Возникает при неправильном названии алгоритма:

name: "hmac" // ошибка

Правильно:

name: "HMAC"

Invalid hash parameter

Ошибка появляется, если хэш не поддерживается:

hash: { name: "MD5" } // не поддерживается

Incorrect usages

Если не указать "sign" или "verify", операции будут недоступны:

[] // приведёт к невозможности использования ключа

Сравнение SHA-алгоритмов для HMAC

  • SHA-1 — устаревший, небезопасный
  • SHA-256 — стандарт де-факто
  • SHA-384 — повышенная стойкость
  • SHA-512 — максимальная криптостойкость, но более медленный

Жизненный цикл HMAC-ключа

  1. Генерация через generateKey
  2. Использование в sign
  3. Проверка через verify
  4. (опционально) экспорт или хранение
  5. уничтожение при потере контекста выполнения

Ограничения Web Crypto при работе с HMAC

  • асинхронная модель (Promise-based API)
  • невозможность синхронного доступа
  • отсутствие прямого доступа к байтам ключа (при extractable: false)
  • ограниченный набор хэш-функций
  • работа только в secure context (HTTPS или localhost)