Жизненный цикл ключа в Web Crypto API

Жизненный цикл ключа в Web Crypto API начинается с его создания. Для этого используется метод crypto.subtle.generateKey(), который позволяет генерировать как симметричные, так и асимметричные ключи.

Симметричные ключи применяются в алгоритмах шифрования (например, AES), а асимметричные — в алгоритмах подписи и обмена ключами (RSA, ECDSA, ECDH).

Пример генерации симметричного ключа:

const key = await crypto.subtle.generateKey(
  {
    name: "AES-GCM",
    length: 256
  },
  true,
  ["encrypt", "decrypt"]
);

Ключевые параметры:

  • name — алгоритм
  • length — длина ключа (в битах)
  • extractable — возможность экспорта ключа
  • keyUsages — допустимые операции

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

const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["encrypt", "decrypt"]
);

Результат — объект с publicKey и privateKey.


Представление ключей

Ключи в Web Crypto API представлены объектами типа CryptoKey. Они не являются обычными JavaScript-объектами и не могут быть напрямую сериализованы.

Свойства CryptoKey:

  • type: "secret", "public", "private"
  • extractable: можно ли экспортировать
  • algorithm: параметры алгоритма
  • usages: список разрешённых операций

Важно: ключи хранятся в защищённой форме и не доступны для прямого чтения.


Экспорт ключей

Экспорт используется для сохранения или передачи ключа. Доступен только если extractable: true.

Метод: crypto.subtle.exportKey()

Форматы:

  • "raw" — для симметричных ключей
  • "pkcs8" — для приватных ключей
  • "spki" — для публичных ключей
  • "jwk" — JSON Web Key

Пример:

const exported = await crypto.subtle.exportKey("jwk", key);

JWK-формат удобен для передачи через JSON:

{
  "kty": "oct",
  "k": "base64url-encoded-key",
  "alg": "A256GCM",
  "ext": true
}

Импорт ключей

Импорт выполняется через crypto.subtle.importKey(). Это позволяет восстановить ключ из внешнего представления.

Пример импорта JWK:

const key = await crypto.subtle.importKey(
  "jwk",
  jwkObject,
  {
    name: "AES-GCM"
  },
  true,
  ["encrypt", "decrypt"]
);

Важно соблюдать соответствие:

  • формат данных
  • алгоритм
  • разрешённые операции

Использование ключей

Ключи применяются в операциях шифрования, дешифрования, подписи и верификации.

Пример шифрования:

const encrypted = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv: crypto.getRandomValues(new Uint8Array(12))
  },
  key,
  data
);

Пример подписи:

const signature = await crypto.subtle.sign(
  {
    name: "RSASSA-PKCS1-v1_5"
  },
  privateKey,
  data
);

Ограничения:

  • ключ должен иметь соответствующие usages
  • алгоритм должен совпадать с параметрами ключа

Хранение ключей

Web Crypto API не предоставляет встроенного постоянного хранилища. Для этого используются внешние механизмы:

IndexedDB — основной способ хранения:

const dbRequest = indexedDB.open("crypto-store", 1);

Перед сохранением ключ необходимо экспортировать (если он extractable).

Альтернатива:

  • хранение в памяти (временное)
  • передача через Service Workers

Ключи с extractable: false невозможно сохранить напрямую — это повышает безопасность.


Ограничение использования ключей

При создании или импорте задаётся список keyUsages, который строго ограничивает операции:

Примеры:

  • "encrypt", "decrypt"
  • "sign", "verify"
  • "deriveKey", "deriveBits"

Попытка использовать ключ вне этих разрешений приведёт к ошибке.

Это важный элемент модели безопасности:

  • предотвращает неправильное использование
  • снижает риск компрометации

Уничтожение ключей

В Web Crypto API отсутствует явный метод удаления ключа. Жизненный цикл завершается через:

  • удаление ссылок на объект
  • очистку памяти сборщиком мусора
  • удаление данных из IndexedDB (если ключ был сохранён)

Пример удаления из IndexedDB:

const transaction = db.transaction("keys", "readwrite");
transaction.objectStore("keys").delete(keyId);

Для повышения безопасности:

  • использовать extractable: false
  • минимизировать время жизни ключа в памяти
  • избегать хранения приватных ключей

Ротация ключей

Практика регулярной смены ключей (key rotation) критична для безопасности.

Подход:

  1. Генерация нового ключа
  2. Постепенный переход на него
  3. Удаление старого ключа

Пример сценария:

  • старый ключ используется для дешифрования
  • новый — для шифрования

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


Производные ключи

Иногда ключ не хранится напрямую, а выводится из пароля или другого секрета.

Используется deriveKey:

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

Преимущества:

  • не нужно хранить ключ
  • можно восстановить при необходимости

Безопасность жизненного цикла

Ключевые принципы:

Минимизация доступности

  • использовать extractable: false
  • ограничивать keyUsages

Изоляция

  • ключи не покидают защищённую среду
  • нет прямого доступа к байтам

Контроль хранения

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

Ротация и удаление

  • регулярная смена
  • удаление неиспользуемых ключей

Поток жизненного цикла

Типичный жизненный цикл ключа:

  1. Генерация или импорт
  2. Использование в криптографических операциях
  3. (Опционально) экспорт и хранение
  4. Ограничение доступа и времени жизни
  5. Ротация или удаление

Каждый этап влияет на безопасность всей системы. Ошибки на любом уровне могут привести к компрометации данных.