В Web Crypto API криптографические операции строятся вокруг объекта CryptoKey, который представляет собой абстракцию над криптографическим ключом, управляемым браузером или средой выполнения. Этот объект не содержит «сырого» ключевого материала в явном виде в JavaScript-коде, если только он не был специально экспортирован.
Ключевая особенность CryptoKey заключается в том, что он является
безопасным контейнером для криптографического ключа,
который используется через интерфейс SubtleCrypto.
Объект CryptoKey не является обычным объектом с набором методов для работы с данными. Он содержит только метаданные, необходимые для работы криптографического движка:
"secret",
"public", "private")Поле type определяет природу ключа:
"secret" — симметричный ключ (один ключ для шифрования
и дешифрования)"public" — публичная часть асимметричной пары"private" — приватная часть асимметричной парыЭто поле критически важно, так как браузер строго ограничивает операции в зависимости от типа ключа.
Свойство extractable определяет, можно ли извлечь
ключевой материал:
true — ключ может быть экспортирован (например, в
формате JWK или raw)false — ключ остается внутри криптографического
контекста и не может быть получен в виде байтовВ большинстве безопасных сценариев приватные ключи создаются с
extractable: false, чтобы исключить утечку.
Поле algorithm описывает криптографический алгоритм,
связанный с ключом. Оно может содержать параметры, зависящие от
конкретного алгоритма.
Примеры:
"AES-GCM" — симметричное шифрование"RSA-OAEP" — асимметричное шифрование"ECDSA" — цифровая подпись"ECDH" — обмен ключамиКаждый алгоритм накладывает собственные ограничения на использование ключа и его параметры.
Массив usages определяет, для каких операций ключ
разрешен:
"encrypt" — шифрование"decrypt" — дешифрование"sign" — создание подписи"verify" — проверка подписи"deriveKey" — вывод нового ключа"deriveBits" — получение битового материала"wrapKey" — упаковка ключа"unwrapKey" — распаковка ключаЕсли попытаться использовать ключ не по назначению, операция завершится ошибкой, даже если сам ключ корректен.
Ключи не создаются напрямую через конструктор. Вместо этого
используется crypto.subtle.
Пример генерации симметричного ключа:
const key = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
Результатом будет объект CryptoKey.
Для асимметричной криптографии используется структура CryptoKeyPair, которая объединяет два объекта CryptoKey:
publicKeyprivateKeyЭта структура возвращается, например, при генерации RSA или ECDSA ключей.
const keyPair = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
Результат:
{
publicKey: CryptoKey,
privateKey: CryptoKey
}
В отличие от симметричного ключа, где один объект выполняет обе функции, CryptoKeyPair разделяет обязанности:
CryptoKeyPair не является отдельным классом — это структурированный
объект. Он не обладает методами и существует только как результат работы
generateKey.
Особенности:
newusagesextractable может различаться для public и private
ключаCryptoKey и CryptoKeyPair не дают доступа к сырому байтовому представлению ключа в оперативной памяти. Это сделано для повышения безопасности.
Даже если ключ используется в нескольких операциях:
Несмотря на изоляцию, ключи могут быть сериализованы при условии, что
extractable: true.
const exported = await crypto.subtle.exportKey("jwk", key);
Возможные форматы:
"raw""jwk""spki""pkcs8"const key = await crypto.subtle.importKey(
"jwk",
jwkData,
{
name: "AES-GCM"
},
true,
["encrypt", "decrypt"]
);
При импорте создается новый CryptoKey, который снова становится управляемым объектом Web Crypto API.
Каждый CryptoKey жестко связан с конкретным криптографическим алгоритмом. Например:
Это обеспечивает строгую типизацию на уровне криптографического API.
Внутри algorithm хранится информация, позволяющая
движку:
CryptoKey используется как входной параметр во всех основных методах
subtle:
encryptdecryptsignverifyderiveKeyderiveBitsПример:
const encrypted = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv
},
key,
data
);
Здесь key — это CryptoKey, а не массив байтов.
Типичный жизненный цикл включает несколько этапов:
Важно, что разработчик не управляет освобождением памяти напрямую — это делает среда выполнения.
CryptoKey и CryptoKeyPair проектировались как средство минимизации утечек ключей.
Основные защитные механизмы:
usagesextractable: falseДаже при доступе к объекту через консоль браузера невозможно получить приватный ключ, если он не был явно экспортирован.
CryptoKey принципиально отличается от типичных JavaScript объектов:
Это делает его частью системного уровня браузера, а не пользовательского пространства данных.
CryptoKeyPair используется в сценариях:
Разделение на public/private ключи обеспечивает:
Если CryptoKey используется некорректно, возникают типичные ошибки:
InvalidAccessError — ключ не поддерживает операциюOperationError — алгоритм не может выполнить
операциюDataError — некорректные входные данныеПричина почти всегда связана с несовпадением:
CryptoKey существует только внутри контекста
window.crypto или self.crypto (в Web Worker).
Перенос ключей между контекстами возможен только через structured clone,
и только если это разрешено свойством extractable.
Модель CryptoKey накладывает жесткие ограничения:
Это делает API безопасным, но менее гибким по сравнению с низкоуровневыми криптобиблиотеками.