Основы симметричного шифрования

Симметричное шифрование в Web Crypto API основано на использовании одного и того же секретного ключа для шифрования и расшифрования данных. В браузере вся криптографическая функциональность доступна через интерфейс crypto.subtle, который предоставляет низкоуровневые операции над бинарными данными.

В отличие от асимметричных алгоритмов, где используются пара ключей (открытый и закрытый), симметричное шифрование опирается на один общий секрет. Это делает его значительно быстрее, но накладывает требования на безопасное хранение ключа.

Ключевые характеристики:

  • один ключ используется для шифрования и дешифрования
  • высокая производительность
  • необходимость безопасной передачи ключа между сторонами
  • чувствительность к утечке ключа

В Web Crypto API наиболее часто используется алгоритм AES-GCM (Advanced Encryption Standard в режиме Galois/Counter Mode), который обеспечивает одновременно конфиденциальность и целостность данных.

Генерация симметричного ключа

Создание ключа выполняется через crypto.subtle.generateKey. Важно явно указать алгоритм, длину ключа и назначение.

const key = await crypto.subtle.generateKey(
  {
    name: "AES-GCM",
    length: 256
  },
  true,
  ["encrypt", "decrypt"]
);

Параметры:

  • name: алгоритм шифрования
  • length: размер ключа (128, 192 или 256 бит)
  • extractable: возможность экспорта ключа
  • keyUsages: допустимые операции

Ключ возвращается как объект CryptoKey, который нельзя напрямую прочитать или вывести в виде строки.

Подготовка данных к шифрованию

Web Crypto API работает только с бинарными данными (ArrayBuffer или TypedArray). Строки необходимо преобразовывать.

Стандартный подход:

const encoder = new TextEncoder();
const data = encoder.encode("секретное сообщение");

Для обратного преобразования используется TextDecoder.

const decoder = new TextDecoder();
const text = decoder.decode(buffer);

Шифрование данных с AES-GCM

Основная операция выполняется через crypto.subtle.encrypt.

const iv = crypto.getRandomValues(new Uint8Array(12));

const encrypted = await crypto.subtle.encrypt(
  {
    name: "AES-GCM",
    iv: iv
  },
  key,
  data
);

Важность IV (инициализирующего вектора)

IV должен быть:

  • уникальным для каждого шифрования
  • случайным
  • длиной обычно 12 байт для AES-GCM

Повторное использование IV с тем же ключом критически нарушает безопасность.

Расшифрование данных

Для восстановления исходного сообщения используется crypto.subtle.decrypt.

const decrypted = await crypto.subtle.decrypt(
  {
    name: "AES-GCM",
    iv: iv
  },
  key,
  encrypted
);

const decoded = new TextDecoder().decode(decrypted);

Важно: тот же IV, который использовался при шифровании, обязателен для корректного дешифрования.

Работа с бинарными данными

Результаты шифрования — это ArrayBuffer. Для хранения или передачи часто требуется преобразование.

Преобразование в Base64

function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  let binary = "";
  bytes.forEach(b => binary += String.fromCharCode(b));
  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;
}

Импорт и экспорт ключей

Web Crypto API позволяет сохранять и восстанавливать ключи в стандартизированном формате.

Экспорт ключа

const exported = await crypto.subtle.exportKey("raw", key);

Импорт ключа

const importedKey = await crypto.subtle.importKey(
  "raw",
  exported,
  "AES-GCM",
  true,
  ["encrypt", "decrypt"]
);

Формат "raw" используется для симметричных ключей AES.

Типичные ошибки при работе с AES-GCM

Повторное использование IV

Одна из наиболее критичных ошибок. Если один и тот же IV используется с одним ключом для разных сообщений, криптосистема теряет стойкость.

Неверная длина IV

AES-GCM оптимально работает с 12 байтами. Использование других значений может привести к снижению безопасности или несовместимости.

Потеря метаданных

Для расшифрования необходимо хранить:

  • ключ
  • IV
  • зашифрованные данные

Часто IV добавляют в начало массива:

const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encrypted), iv.length);

Хранение и структура зашифрованных данных

Практическая схема хранения часто включает упаковку:

  • первые 12 байт — IV
  • остальная часть — ciphertext

При расшифровании:

const iv = data.slice(0, 12);
const encrypted = data.slice(12);

Безопасность ключей в браузере

CryptoKey защищён от прямого чтения. Однако безопасность зависит от:

  • корректного управления жизненным циклом ключа
  • отсутствия экспорта при extractable: false
  • защиты от XSS-уязвимостей

Даже сильное шифрование теряет смысл при компрометации JavaScript-контекста страницы.

Производительность симметричного шифрования

AES-GCM реализуется на уровне браузера с использованием аппаратного ускорения, если оно доступно. Это делает его пригодным для:

  • шифрования файлов
  • защищённой передачи сообщений
  • локального хранения данных
  • потоковой обработки больших массивов данных

Пример полного цикла шифрования

const encoder = new TextEncoder();
const decoder = new TextDecoder();

const key = await crypto.subtle.generateKey(
  { name: "AES-GCM", length: 256 },
  true,
  ["encrypt", "decrypt"]
);

const iv = crypto.getRandomValues(new Uint8Array(12));

const data = encoder.encode("пример текста");

const encrypted = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  key,
  data
);

const decrypted = await crypto.subtle.decrypt(
  { name: "AES-GCM", iv },
  key,
  encrypted
);

const result = decoder.decode(decrypted);

Особенности использования в реальных приложениях

Симметричное шифрование редко применяется изолированно. Чаще используется в связке с:

  • асимметричным обменом ключами (например, RSA или ECDH)
  • серверной генерацией ключей
  • защищёнными каналами передачи (TLS)

Основная роль AES-GCM в браузере — обеспечение конфиденциальности данных на уровне клиента и временное хранение информации в зашифрованном виде.