В Web Crypto API экспорт и импорт ключей основаны на стандартизированных форматах представления криптографических ключей. Для RSA-ключей используются два основных контейнера: SPKI (SubjectPublicKeyInfo) для публичных ключей и PKCS#8 (PrivateKeyInfo) для приватных ключей. Эти форматы определяют структуру бинарного представления ключей в соответствии с ASN.1 и широко применяются в криптографических системах за пределами браузера.
SPKI содержит только публичную часть ключа вместе с алгоритмическими параметрами. PKCS#8, напротив, инкапсулирует приватный ключ и дополнительные метаданные, включая идентификатор алгоритма.
Перед экспортом и импортом необходимо создать ключевую пару с помощью
crypto.subtle.generateKey. Важно указать, что ключи должны
быть извлекаемыми (extractable: true), иначе экспорт будет
невозможен.
const keyPair = await crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
Параметр true во втором аргументе означает разрешение на
экспорт ключей. Без него операции exportKey завершатся
ошибкой безопасности.
Формат SPKI используется для передачи публичного ключа между
системами. В Web Crypto API экспорт выполняется через
crypto.subtle.exportKey.
const spkiBuffer = await crypto.subtle.exportKey(
"spki",
keyPair.publicKey
);
Результатом является ArrayBuffer, содержащий бинарное
представление ключа. Этот формат не предназначен для чтения человеком и
обычно кодируется в Base64 при сохранении или передаче.
Для хранения или передачи по текстовым каналам используется кодирование:
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);
}
const spkiBase64 = arrayBufferToBase64(spkiBuffer);
Base64-представление часто используется в JSON API, HTTP-заголовках и конфигурационных файлах.
Импорт осуществляется через crypto.subtle.importKey.
Необходимо точно указать алгоритм и параметры, совпадающие с теми, что
использовались при генерации.
const publicKey = await crypto.subtle.importKey(
"spki",
spkiBuffer,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
true,
["encrypt"]
);
Важно соблюдать соответствие алгоритма: ключ RSA-OAEP нельзя импортировать как RSASSA-PKCS1-v1_5 и наоборот.
PKCS#8 представляет приватный ключ в стандартизированном формате. Экспорт выполняется аналогично публичному ключу:
const pkcs8Buffer = await crypto.subtle.exportKey(
"pkcs8",
keyPair.privateKey
);
Полученный ArrayBuffer содержит не только сам ключ, но и
структуру ASN.1 с идентификатором алгоритма и параметрами.
Приватные ключи часто сохраняются в зашифрованных хранилищах или конфигурационных файлах:
const pkcs8Base64 = arrayBufferToBase64(pkcs8Buffer);
В реальных системах рекомендуется дополнительно шифровать такие данные перед сохранением.
Импорт приватного ключа требует указания алгоритма и разрешённых операций.
const privateKey = await crypto.subtle.importKey(
"pkcs8",
pkcs8Buffer,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
true,
["decrypt"]
);
Если ключ предназначен для подписи (например, RSASSA-PKCS1-v1_5), набор операций будет другим:
["sign"]
SPKI и PKCS#8 не зависят от конкретной реализации браузера, но зависят от криптографического алгоритма. Основные правила:
Несовпадение алгоритма
Попытка импортировать ключ RSA-OAEP как RSASSA-PKCS1-v1_5 приводит к
ошибке DataError.
extractable: false
Если ключ был создан с extractable: false, экспорт
завершится исключением InvalidAccessError.
Некорректный формат данных
Передача повреждённого Base64 или обрезанного ArrayBuffer приводит к ошибке декодирования ASN.1 структуры.
Web Crypto API всегда работает с бинарными буферами
(ArrayBuffer), а не строками. Это требует явного
преобразования при интеграции с внешними системами.
Для декодирования Base64 обратно в ArrayBuffer:
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;
}
Типичный сценарий использования SPKI и PKCS#8 в приложениях:
RSA-ключи в Web Crypto API защищены изоляцией контекста браузера. Однако при экспорте в SPKI или PKCS#8 ключи становятся уязвимыми к компрометации, если хранятся в открытом виде.
Критические моменты:
Форматы SPKI и PKCS#8 не зависят от схемы использования RSA, однако поведение ключей определяется при импорте:
Пример различий:
// шифрование
["encrypt", "decrypt"]
// подпись
["sign", "verify"]
После импорта ключи могут храниться в памяти или сериализоваться через IndexedDB. Однако повторная сериализация невозможна без повторного экспорта, так как Web Crypto API не предоставляет прямого доступа к структуре ключа.