Web Crypto API построена вокруг интерфейса SubtleCrypto,
который предоставляет криптографические операции в полностью асинхронной
форме. Почти все методы crypto.subtle возвращают промисы,
что делает модель выполнения принципиально неблокирующей и хорошо
совместимой с событийным циклом JavaScript.
Основные операции:
generateKey)encrypt,
decrypt)sign, verify)digest)importKey,
exportKey)wrapKey, unwrapKey)Каждый из этих методов возвращает Promise, который
разрешается в соответствующий результат или отклоняется с ошибкой
криптографического уровня.
Криптографические вычисления могут быть:
Синхронное выполнение таких операций привело бы к блокировке UI. Промисная модель решает эту проблему:
Каждый метод crypto.subtle возвращает промис с
конкретным типом результата.
const data = new TextEncoder().encode("message");
const hashPromise = crypto.subtle.digest("SHA-256", data);
hashPromise.then(hashBuffer => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
});
Сигнатура:
digest(algorithm, data) -> Promise<ArrayBuffer>
const keyPromise = crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
keyPromise.then(keyPair => {
// keyPair содержит CryptoKey
});
Сигнатура:
generateKey(algorithm, extractable, keyUsages) -> Promise<CryptoKey>
Промисы Web Crypto API напрямую интегрируются с
async/await, что делает код более линейным.
async function hashMessage(message) {
const data = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
return new Uint8Array(hashBuffer);
}
async function encryptData(key, data) {
const encoded = new TextEncoder().encode(data);
return await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: crypto.getRandomValues(new Uint8Array(12))
},
key,
encoded
);
}
Промисы позволяют писать криптографические операции как последовательный поток, несмотря на их асинхронную природу.
Web Crypto API часто используется как набор последовательных преобразований:
Пример цепочки:
crypto.subtle.digest("SHA-256", data)
.then(hash => crypto.subtle.sign(
"HMAC",
key,
hash
))
.then(signature => {
return { signature };
});
При использовании async/await цепочка становится
линейной:
const hash = await crypto.subtle.digest("SHA-256", data);
const signature = await crypto.subtle.sign("HMAC", key, hash);
Поскольку промисы независимы, операции можно выполнять параллельно
через Promise.all.
const messages = [
"one",
"two",
"three"
];
const hashes = await Promise.all(
messages.map(msg =>
crypto.subtle.digest("SHA-256", new TextEncoder().encode(msg))
)
);
Каждый вызов digest выполняется независимо, что
позволяет эффективно использовать ресурсы CPU.
Любая криптографическая операция может завершиться ошибкой, возвращаемой через отклонение промиса.
Причины ошибок:
crypto.subtle.decrypt(params, key, data)
.catch(error => {
console.error(error.name, error.message);
});
try {
const result = await crypto.subtle.decrypt(params, key, data);
} catch (error) {
console.error(error);
}
Типичные ошибки:
InvalidAccessErrorDataErrorOperationErrorПромисы Web Crypto API подчиняются стандартному циклу микрозадач Jav * aScript:
.then() всегда откладывается до завершения текущего
стека вызововconsole.log("start");
crypto.subtle.digest("SHA-256", data)
.then(() => console.log("done"));
console.log("end");
Порядок вывода:
start
end
done
Промисы Web Crypto API не поддерживают отмену через
AbortController.
Это означает:
Это важное ограничение модели, особенно при массовой обработке данных.
Поскольку промисы возвращают ArrayBuffer, часто
требуется преобразование:
async function toHex(buffer) {
const bytes = new Uint8Array(buffer);
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
const hash = await crypto.subtle.digest("SHA-256", data);
const hex = await toHex(hash);
Промисная модель отделяет вычисление от представления результата, что требует явной работы с бинарными данными.
Операции с ключами также асинхронны:
const key = await crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256"
},
true,
["sign", "verify"]
);
const exported = await crypto.subtle.exportKey("jwk", key);
Каждый этап — отдельный промис, даже если операция концептуально “локальная”.
Типичный поток Web Crypto API почти всегда представляет собой цепочку промисов:
const encoded = new TextEncoder().encode("data");
const hash = await crypto.subtle.digest("SHA-256", encoded);
const key = await crypto.subtle.generateKey(
{ name: "HMAC", hash: "SHA-256" },
true,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, hash);
Промисы позволяют выбирать стратегию выполнения:
const [hash1, hash2] = await Promise.all([
crypto.subtle.digest("SHA-256", data1),
crypto.subtle.digest("SHA-256", data2)
]);
Промисы Web Crypto API обрабатываются внутри браузерного движка и могут:
Это делает их поведение отличным от пользовательских промисов, хотя интерфейс остаётся одинаковым.