В прикладной криптографии почти всегда используется сочетание асимметричных и симметричных алгоритмов. Причина проста: асимметричные алгоритмы (RSA, ECDH) удобны для обмена ключами, но слишком медленные для шифрования больших объёмов данных. Симметричные алгоритмы (AES-GCM, AES-CBC) быстрые, но требуют безопасной доставки ключа.
Гибридная схема объединяет сильные стороны обоих подходов:
Web Crypto API реализует все необходимые примитивы на уровне браузера
через объект crypto.subtle.
Ключевая точка входа — window.crypto.subtle. Этот
интерфейс предоставляет асинхронные операции:
Особенность API — строгая работа с ArrayBuffer и
TypedArray, что исключает небезопасные преобразования
строк.
RSA в Web Crypto используется чаще всего в режиме
RSA-OAEP, который предназначен именно для шифрования
ключей.
const rsaKeyPair = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
Ключевые параметры:
modulusLength — длина ключа, минимум 2048 битpublicExponent — стандартное значение 65537hash — хэш-функция для OAEP paddingРезультат — объект с publicKey и
privateKey.
Для шифрования данных используется AES-GCM как наиболее безопасный и современный режим.
const aesKey = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"]
);
AES-GCM обеспечивает:
Чтобы передать AES-ключ через RSA, его нужно экспортировать в бинарный формат:
const rawAesKey = await crypto.subtle.exportKey("raw", aesKey);
На этом этапе ключ становится обычным ArrayBuffer,
который можно зашифровать RSA.
RSA используется для шифрования небольших данных — в данном случае AES ключа.
const encryptedAesKey = await crypto.subtle.encrypt(
{
name: "RSA-OAEP",
},
rsaKeyPair.publicKey,
rawAesKey
);
Результат encryptedAesKey можно безопасно передавать по
сети или сохранять вместе с зашифрованными данными.
Перед шифрованием необходимо создать уникальный nonce (IV). В AES-GCM это критически важно: повтор IV полностью ломает безопасность.
const iv = crypto.getRandomValues(new Uint8Array(12));
const encodedData = new TextEncoder().encode("Секретное сообщение");
const encryptedData = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
aesKey,
encodedData
);
Важно:
Процесс шифрования данных выглядит следующим образом:
Генерация RSA ключевой пары
Генерация AES ключа
Экспорт AES ключа
Шифрование AES ключа через RSA public key
Шифрование данных через AES-GCM
Передача:
На стороне получателя используется RSA private key:
const decryptedAesKeyRaw = await crypto.subtle.decrypt(
{
name: "RSA-OAEP",
},
rsaKeyPair.privateKey,
encryptedAesKey
);
После этого необходимо импортировать ключ обратно в Web Crypto:
const decryptedAesKey = await crypto.subtle.importKey(
"raw",
decryptedAesKeyRaw,
{
name: "AES-GCM",
},
false,
["decrypt"]
);
После восстановления AES ключа выполняется расшифровка:
const decryptedBuffer = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv,
},
decryptedAesKey,
encryptedData
);
const decodedText = new TextDecoder().decode(decryptedBuffer);
Практическая реализация почти всегда использует структурированный формат:
{
"key": "encryptedAesKey (base64)",
"iv": "base64",
"data": "encryptedData (base64)"
}
Такой формат удобен для:
Web Crypto работает с бинарными данными, поэтому часто требуется преобразование:
function arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
И обратное преобразование:
function base64ToArrayBuffer(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes.buffer;
}
AES-GCM стал стандартом благодаря встроенной аутентификации. В отличие от CBC:
Даже при использовании Web Crypto API возможны критические ошибки:
Повтор IV в AES-GCM
Использование RSA без OAEP
Слабый размер RSA
Отсутствие проверки целостности
В реальных веб-приложениях гибридное шифрование обычно встроено в слой передачи данных:
Такой подход снижает нагрузку на сервер и повышает масштабируемость.
Web Crypto API работает асинхронно и часто использует нативные реализации:
Ограничения:
Криптография в браузере зависит от окружения:
crypto.subtle отсутствует в insecure
contextДополнительно:
На уровне архитектуры система выглядит так:
Такая структура используется в мессенджерах, финтех-приложениях и системах защищённого документооборота.