HKDF (HMAC-based Key Derivation Function) представляет собой
стандартный алгоритм для получения криптографически стойких ключей из
исходного секретного материала. В контексте Web Crypto API он
используется через deriveKey и deriveBits с
алгоритмом HKDF, обеспечивая безопасное расширение или
разделение ключевого материала для разных целей.
HKDF строится поверх HMAC и включает два этапа:
В Web Crypto API HKDF задаётся через объект алгоритма:
{
name: "HKDF",
hash: "SHA-256",
salt: Uint8Array,
info: Uint8Array
}
hash
Определяет хеш-функцию, используемую внутри HMAC. Поддерживаются:
SHA-1 (не рекомендуется)SHA-256SHA-384SHA-512На практике чаще всего используется SHA-256 как баланс
безопасности и производительности.
salt
Случайная или фиксированная соль, используемая на этапе extract.
Ключевые свойства:
info
Контекстная строка, которая “привязывает” получаемые ключи к конкретному назначению.
Примеры использования:
"encryption""authentication""session key""api v1 key"Важно: изменение info приводит к полностью другому
ключу.
HKDF работает с базовым секретом, который импортируется как
CryptoKey.
Обычно используется тип raw:
const baseKeyMaterial = await crypto.subtle.importKey(
"raw",
new TextEncoder().encode("initial-secret"),
"HKDF",
false,
["deriveKey", "deriveBits"]
);
Исходный материал может быть:
Основной сценарий использования HKDF — получение симметричного ключа.
const derivedKey = await crypto.subtle.deriveKey(
{
name: "HKDF",
hash: "SHA-256",
salt: new TextEncoder().encode("unique-salt"),
info: new TextEncoder().encode("encryption-key")
},
baseKeyMaterial,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
Результат — полноценный CryptoKey, пригодный для
шифрования.
Если требуется не ключ, а произвольный байтовый массив:
const derivedBits = await crypto.subtle.deriveBits(
{
name: "HKDF",
hash: "SHA-256",
salt: new TextEncoder().encode("unique-salt"),
info: new TextEncoder().encode("session-id")
},
baseKeyMaterial,
256
);
Результат:
ArrayBufferИсходный ключевой материал проходит через HMAC:
PRK = HMAC(salt, IKM)
где:
IKM — input key materialPRK — pseudorandom keyЕсли salt не задан, используется массив нулей длиной хеша.
Из PRK генерируются блоки:
T(1) = HMAC(PRK, info | 0x01)
T(2) = HMAC(PRK, T(1) | info | 0x02)
...
И так далее до нужной длины.
Одно из наиболее распространённых применений — постобработка результата ECDH.
Схема:
Пример:
const sharedSecret = await crypto.subtle.deriveBits(
{
name: "ECDH",
public: serverPublicKey
},
clientPrivateKey,
256
);
const baseKey = await crypto.subtle.importKey(
"raw",
sharedSecret,
"HKDF",
false,
["deriveKey"]
);
HKDF позволяет из одного master key получать несколько независимых ключей.
Пример разделения:
const encKey = await crypto.subtle.deriveKey(
{
name: "HKDF",
hash: "SHA-256",
salt,
info: new TextEncoder().encode("enc")
},
baseKeyMaterial,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
const macKey = await crypto.subtle.deriveKey(
{
name: "HKDF",
hash: "SHA-256",
salt,
info: new TextEncoder().encode("mac")
},
baseKeyMaterial,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign", "verify"]
);
Ключевое свойство: при одинаковом base key различие только в
info полностью изолирует ключи.
Типичная ошибка — использование одинакового info для
разных задач.
info: "session-key:" + sessionId
Каждая сессия получает уникальный ключевой материал.
info: "api-v2-encryption"
Позволяет менять схему без изменения базового секрета.
masterKey → HKDF → intermediateKey → HKDF → finalKeys
Используется в сложных протоколах (например, мессенджеры, VPN-подобные схемы).
HKDF работает только с буферами, нет streaming API.
Алгоритм строго фиксирован внутри браузера.
Разработчик не может вмешаться в промежуточные шаги.
crypto.getRandomValues(new Uint8Array(16))
Каждый тип ключа должен иметь уникальный namespace:
"enc""auth""sign""backup"HKDF допускает повторное использование, но только при строгом
контроле salt и info.
Любое изменение:
приводит к несовместимости ключей, поэтому параметры должны версионироваться.
Приводит к слабому разделению контекста и потенциальному повторному использованию ключей.
Уменьшает стойкость при массовых атаках.
HKDF не заменяет PBKDF2 или Argon2 для паролей, если нет дополнительного этапа усиления.
HKDF часто используется вместе с:
ECDH — для получения shared secretAES-GCM — для шифрования данныхHMAC — для аутентификации сообщенийТипичный pipeline:
ECDH → HKDF → AES-GCM key + HMAC key
info для разных типов
ключейВ сложных системах HKDF используется как центральный элемент иерархии ключей:
Root Key
├── HKDF → Device Key
│ ├── HKDF → Session Key
│ └── HKDF → Backup Key
└── HKDF → Application Key
├── HKDF → Encryption Key
└── HKDF → Signing Key
Такой подход позволяет: