Работа с файлами в браузере опирается на объектную модель File API и бинарные структуры ArrayBuffer/TypedArray, поскольку Web Crypto API не работает со строками напрямую и требует строго бинарного представления данных.
Любой файл, полученный через <input type="file">,
представляет собой объект File, который является
наследником Blob. Для криптографических операций файл
необходимо преобразовать в ArrayBuffer.
Чаще всего используется FileReader:
const file = input.files[0];
const reader = new FileReader();
reader.onl oad = function () {
const arrayBuffer = reader.result;
};
reader.readAsArrayBuffer(file);
Альтернативный современный подход — использование
file.arrayBuffer():
const arrayBuffer = await file.arrayBuffer();
Этот вариант предпочтителен, так как избавляет от колбэков и упрощает поток обработки.
Web Crypto API предоставляет ограниченный набор безопасных алгоритмов. Для шифрования файлов в браузере стандартным решением выступает AES-GCM:
Основной интерфейс — crypto.subtle.
Ключ может быть сгенерирован автоматически:
const key = await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
length: 256 — рекомендуемый уровень стойкостиextractable: true позволяет экспортировать ключ при
необходимости["encrypt", "decrypt"] задаёт разрешённые операцииAES-GCM требует уникальный IV для каждого шифрования. Его нельзя повторно использовать с одним ключом.
Обычно применяется 12-байтовый случайный вектор:
const iv = crypto.getRandomValues(new Uint8Array(12));
IV не является секретом, но должен сохраняться вместе с зашифрованными данными.
После подготовки данных выполняется шифрование через
crypto.subtle.encrypt:
const encryptedBuffer = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
key,
arrayBuffer
);
Результатом является ArrayBuffer, содержащий
зашифрованные данные.
Поскольку для расшифрования необходим IV, он обычно сохраняется вместе с шифротекстом.
Простейшая схема упаковки:
[ IV (12 байт) ][ encrypted data ]
Реализация:
const ivAndData = new Uint8Array(iv.byteLength + encryptedBuffer.byteLength);
ivAndData.set(iv, 0);
ivAndData.set(new Uint8Array(encryptedBuffer), iv.byteLength);
Для сохранения результата используется Blob:
const blob = new Blob([ivAndData], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "encrypted.bin";
a.click();
URL.revokeObjectURL(url);
Такой подход позволяет выгрузить файл без серверного участия.
При обратной операции сначала извлекается IV и данные:
const combined = new Uint8Array(await encryptedFile.arrayBuffer());
const iv = combined.slice(0, 12);
const data = combined.slice(12);
Затем выполняется расшифрование:
const decryptedBuffer = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
key,
data
);
Результат снова представляет собой ArrayBuffer, который
можно преобразовать обратно в Blob:
const blob = new Blob([decryptedBuffer]);
Сгенерированный ключ существует только в памяти. При перезагрузке страницы он теряется. Для практических приложений используется один из подходов:
Экспорт ключа:
const exported = await crypto.subtle.exportKey("raw", key);
Для пользовательских паролей применяется PBKDF2 с солью:
const encoder = new TextEncoder();
const baseKey = await crypto.subtle.importKey(
"raw",
encoder.encode(password),
"PBKDF2",
false,
["deriveKey"]
);
Генерация соли:
const salt = crypto.getRandomValues(new Uint8Array(16));
Производный ключ:
const derivedKey = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: salt,
iterations: 100000,
hash: "SHA-256"
},
baseKey,
{
name: "AES-GCM",
length: 256
},
false,
["encrypt", "decrypt"]
);
Такой подход позволяет восстанавливать ключ из пароля без хранения секретных данных.
Web Crypto API работает с целыми буферами, поэтому большие файлы требуют внимания:
arrayBuffer() загружает весь файл в
памятьПрактический подход:
Криптографические операции часто приводят к ошибкам, связанным с типами данных:
Передача строки вместо ArrayBuffer Все входные данные должны быть бинарными.
Повторное использование IV Нарушает безопасность AES-GCM и может привести к компрометации ключа.
Неверная длина ключа AES-GCM требует строго 128/192/256 бит.
Потеря IV при хранении Без IV расшифрование невозможно.
Использование неподдерживаемых алгоритмов Web Crypto API работает только с ограниченным набором проверенных алгоритмов.
Типовой поток выглядит как последовательность операций:
Такая структура обеспечивает детерминированную работу и совместимость с API браузеров без внешних зависимостей.