Браузер и Web Crypto API

В браузерной среде криптографические операции выполняются через встроенный интерфейс Web Crypto API, доступный как window.crypto.subtle. Именно этот слой используется библиотекой Jose для всех операций подписи, проверки, шифрования и расшифрования JSON Web Tokens и JSON Web Encryption объектов.

Ограничения и особенности Web Crypto API

Web Crypto API накладывает ряд архитектурных ограничений, которые напрямую влияют на работу криптографических библиотек:

  • операции выполняются асинхронно и возвращают Promise
  • доступ к «сырым» ключам ограничен: большинство операций требуют объектов CryptoKey
  • поддерживается строго ограниченный набор алгоритмов
  • ключи часто нельзя извлечь в виде приватного значения без явного экспорта
  • работа происходит в контексте происхождения (origin), что повышает безопасность

Jose учитывает эти ограничения и строит API поверх crypto.subtle, сохраняя совместимость между Node.js и браузером.


Архитектура Jose в браузере

Библиотека Jose реализует универсальный слой абстракции над криптографическими примитивами. В браузере этот слой автоматически использует Web Crypto API без необходимости ручной настройки.

Ключевые компоненты:

  • JWS (JSON Web Signature) — цифровые подписи
  • JWE (JSON Web Encryption) — шифрование
  • JWK (JSON Web Key) — представление ключей
  • JWT (JSON Web Token) — контейнер, использующий JWS/JWE

В браузерной среде все операции делегируются crypto.subtle, включая:

  • sign
  • verify
  • encrypt
  • decrypt
  • importKey
  • exportKey
  • generateKey

Генерация ключей в браузере

Web Crypto API предоставляет нативную генерацию ключевых пар. Jose использует её через собственные обёртки.

Пример создания RSA ключевой пары:

const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSASSA-PKCS1-v1_5",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["sign", "verify"]
);

В Jose аналогичная операция упрощается:

import { generateKeyPair } from "jose";

const { publicKey, privateKey } = await generateKeyPair("RS256");

Библиотека автоматически выбирает подходящий алгоритм и конфигурацию для Web Crypto API.


Работа с JWK в браузере

JSON Web Key формат является основным способом представления ключей в Jose.

Web Crypto API поддерживает импорт и экспорт ключей в формате JWK:

const key = await crypto.subtle.importKey(
  "jwk",
  jwkObject,
  {
    name: "ECDSA",
    namedCurve: "P-256"
  },
  true,
  ["sign"]
);

Jose упрощает этот процесс:

import { importJWK } from "jose";

const key = await importJWK(jwk, "ES256");

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

import { exportJWK } from "jose";

const jwk = await exportJWK(publicKey);

Подпись JWT в браузере

Подпись токена выполняется через JWS. В браузере используется Web Crypto API через Jose без прямого вызова crypto.subtle.sign.

Пример создания JWT:

import { SignJWT } from "jose";

const token = await new SignJWT({ role: "admin" })
  .setProtectedHeader({ alg: "HS256" })
  .setIssuedAt()
  .setExpirationTime("2h")
  .sign(secretKey);

Для асимметричных алгоритмов:

import { SignJWT } from "jose";

const token = await new SignJWT({ userId: 123 })
  .setProtectedHeader({ alg: "RS256" })
  .sign(privateKey);

Под капотом:

  • формируется payload
  • сериализуется header
  • выполняется base64url кодирование
  • вызывается crypto.subtle.sign

Проверка JWT

Проверка токена также использует Web Crypto API:

import { jwtVerify } from "jose";

const { payload, protectedHeader } = await jwtVerify(token, publicKey);

Алгоритм работы:

  • декодирование JWT структуры
  • извлечение подписи
  • передача данных в crypto.subtle.verify
  • сравнение результата

При несоответствии подписи операция завершается исключением.


Шифрование JWE в браузере

Web Crypto API поддерживает симметричное и асимметричное шифрование, однако сложные схемы реализуются через Jose.

Пример создания зашифрованного JWT:

import { EncryptJWT } from "jose";

const token = await new EncryptJWT({ data: "secret" })
  .setProtectedHeader({ alg: "A256KW", enc: "A256GCM" })
  .setExpirationTime("1h")
  .encrypt(key);

Расшифрование:

import { jwtDecrypt } from "jose";

const { payload } = await jwtDecrypt(token, key);

Внутри используются:

  • crypto.subtle.encrypt
  • AES-GCM режим
  • key wrapping алгоритмы

Base64URL и совместимость с Web Crypto

Одной из ключевых задач Jose является работа с Base64URL, так как Web Crypto API оперирует ArrayBuffer, а JWT требует текстового представления.

Особенности:

  • отсутствие + и /
  • замена = на отсутствие padding
  • безопасное использование в URL

Jose автоматически выполняет преобразования:

  • Uint8Array ↔︎ Base64URL
  • ArrayBuffer ↔︎ string

Производительность в браузере

Web Crypto API выполняется нативно, часто с использованием аппаратного ускорения. Это делает Jose эффективным даже при большом количестве операций:

  • подпись выполняется быстрее, чем JS-реализация RSA
  • AES-GCM использует системные криптографические инструкции
  • генерация ключей делегируется браузеру

Основные факторы производительности:

  • размер ключа (RSA 2048 vs 4096)
  • алгоритм (HMAC быстрее RSA)
  • количество параллельных операций

Хранение ключей в браузере

Web Crypto API позволяет ограниченно хранить ключи:

  • ключи могут быть non-extractable (extractable: false)
  • возможно хранение в IndexedDB через CryptoKey

Пример ограничения экспорта:

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

Jose работает с такими ключами без необходимости их извлечения.


Ограничения браузерной среды

При использовании Jose в браузере учитываются следующие ограничения:

  • отсутствие синхронных криптографических операций
  • невозможность использования некоторых legacy алгоритмов
  • зависимость от реализации браузера (Chrome, Firefox, Safari)
  • ограничения контекста (secure origin HTTPS)

Некоторые алгоритмы доступны не во всех браузерах, например:

  • Ed25519 (частично поддерживается)
  • RSA-PSS (в зависимости от версии)

Совместимость между Node.js и браузером

Jose использует единый API, абстрагированный от платформы:

  • в Node.js — crypto module
  • в браузере — window.crypto.subtle

Это позволяет переносить код без изменений:

import { jwtVerify } from "jose";

Одинаковая функция работает в обеих средах, различие только в backend-реализации криптографии.


Типичные сценарии использования в браузере

  • аутентификация через JWT в SPA
  • защита API-запросов
  • временные токены доступа
  • клиентское шифрование данных
  • безопасный обмен ключами между фронтендом и сервером

Jose в связке с Web Crypto API позволяет выполнять все операции без сторонних криптографических зависимостей и без утечки ключей в JavaScript-уровень.