Сквозное шифрование (End-to-End Encryption, E2EE) в веб-приложениях строится на принципе, при котором данные шифруются на стороне отправителя и расшифровываются только на стороне получателя. Сервер при этом выполняет роль транспортного узла и не имеет доступа к содержимому сообщений.
В контексте JavaScript-библиотеки SJCL (Stanford JavaScript Crypto Library) реализуются основные криптографические примитивы: симметричное шифрование AES, режимы аутентифицированного шифрования, хэш-функции, генерация случайных чисел, а также эллиптическая криптография для обмена ключами.
SJCL построена вокруг нескольких фундаментальных компонентов:
Основная концепция: все данные приводятся к bitArray —
внутреннему формату представления битовых последовательностей.
В большинстве сценариев E2EE используется AES в режиме CCM (Counter with CBC-MAC), обеспечивающий одновременно конфиденциальность и целостность.
const plaintext = "секретное сообщение";
const password = "сильный пароль";
const ciphertext = sjcl.encrypt(password, plaintext);
const decrypted = sjcl.decrypt(password, ciphertext);
Внутри sjcl.encrypt происходит:
CCM объединяет два механизма:
Ключевая особенность: невозможно изменить ciphertext без обнаружения подделки.
Структура зашифрованного объекта SJCL:
{
"iv": "...",
"v": 1,
"iter": 10000,
"ks": 128,
"ct": "...",
"salt": "...",
"mode": "ccm",
"adata": ""
}
Для пользовательских паролей применяется PBKDF2:
const key = sjcl.misc.pbkdf2(password, salt, iterations, keyLength);
PBKDF2 замедляет подбор пароля атакующим, увеличивая стоимость перебора.
Безопасность E2EE критически зависит от качества случайных чисел.
sjcl.random.addEntropy(window.crypto.getRandomValues(new Uint32Array(32)), 1024);
const randomWord = sjcl.random.randomWords(4);
SJCL использует несколько источников энтропии:
Для безопасного обмена ключами используется ECDH (Elliptic Curve Diffie-Hellman).
const keys = sjcl.ecc.elGamal.generateKeys(256);
const publicKey = keys.pub;
const privateKey = keys.sec;
const sharedSecret = privateKey.dh(publicKeyOther);
Полученный sharedSecret используется как основа для
симметричного ключа AES.
Типовая архитектура E2EE в веб-приложении включает следующие этапы:
Каждый пользователь создаёт:
Публичные ключи передаются через сервер:
Используется ECDH:
const shared = myPrivateKey.dh(remotePublicKey);
Из общего секрета создаётся ключ AES:
const aesKey = sjcl.hash.sha256.hash(shared);
const encrypted = sjcl.encrypt(aesKey, message);
const decrypted = sjcl.decrypt(aesKey, encrypted);
HTTPS защищает транспорт, но E2EE добавляет дополнительный уровень: даже сервер не видит содержимое.
Если подмена публичных ключей возможна, E2EE ломается.
Решения:
CCM включает nonce (IV), предотвращающий повторное использование ciphertext.
Защита обеспечивается:
В веб-приложениях ключи могут храниться:
IndexedDBlocalStorage (не рекомендуется для приватных
ключей)Пример хранения:
localStorage.setItem("privateKey", JSON.stringify(sjcl.codec.base64.fromBits(privateKey)));
SJCL использует внутренний тип bitArray, представляющий
данные как массив 32-битных слов.
const bits = sjcl.codec.utf8String.toBits("data");
const str = sjcl.codec.utf8String.fromBits(bits);
Преобразования:
SJCL не использует WebAssembly и полностью написан на JavaScript, что влияет на производительность:
Оптимизация достигается за счёт:
Сервер:
Сервер не имеет:
Несмотря на функциональность, существуют ограничения:
Надёжность E2EE на SJCL зависит не только от криптографии, но и от архитектуры приложения:
Основная уязвимость веб-E2EE чаще находится не в алгоритмах, а в слое приложения.