Задать вопрос
Fernus
@Fernus
Техник - Механик :)

E2EE + WEB = поищем безопасность?

Виртуальная задача(упрощённо):

Обмен сообщениями(данными) со сквозным шифрованием через WEB.
"Создатель сервера" - недоверенное лицо - но сервер под контролем разработчика.

Решение "с наскока":

- Стандартная авторизация WEB-приложения;
- Генерируем ключи ECDH(если до этого на устройстве их нет - или они невалидные);
- Храним приватный ключ на клиенте(в браузере в данном случае...или пойти дальше - в приложении);
- Публичный - используем как хотим в пределах системы и обмена между участниками;
- При шифровке/дешифровке данных используем derivedKey(общий ключ шифирования для публичного "получателя" и приватный ключ "отправителя" - и наоборот);
- При смене устройства(где хранится приватный ключ пользователя) - предлагать синхронизировать приватные ключи(аля QR) или введя доп. пинкод(который задаётся при регистрации...или который высылается по другой "довереннной" связи);
- Или предлагать "сбросить всё" - в случае - если нет "пин-кода", НО при этом - пройдена авторизация WEB-приложения(тем самым предыдущая переписка будет недоступна третьим лицам);
- При смене(сброса) публичного ключа - "вторая сторона" будет оповещена об этом - и далее "эта сторона" сама решает о "ликвидности" общения со стороной изменившей "публичный ключ".

Минимальный JS(в виде класса) для этой задачи на стороне клиента состряпал такой:

class msgCrypt {

    constructor(publicKeyJwk, privateKeyJwk) {

        this.publicKeyJwk = publicKeyJwk;
        this.privateKeyJwk = privateKeyJwk;

    }

    static async generateKeyPair() {

        const keyPair = await window.crypto.subtle.generateKey(
            {
                name: "ECDH",
                namedCurve: "P-256",
            },
            true,
            ["deriveKey", "deriveBits"]
        );

        const publicKeyJwk = await window.crypto.subtle.exportKey(
            "jwk",
            keyPair.publicKey
        );

        const privateKeyJwk = await window.crypto.subtle.exportKey(
            "jwk",
            keyPair.privateKey
        );

        return {publicKeyJwk, privateKeyJwk};

    }

    async deriveKey() {

        const publicKeyJwk = this.publicKeyJwk;
        const privateKeyJwk = this.privateKeyJwk;

        const publicKey = await window.crypto.subtle.importKey(
            "jwk",
            publicKeyJwk,
            {
                name: "ECDH",
                namedCurve: "P-256",
            },
            true,
            []
        );

        const privateKey = await window.crypto.subtle.importKey(
            "jwk",
            privateKeyJwk,
            {
                name: "ECDH",
                namedCurve: "P-256",
            },
            true,
            ["deriveKey", "deriveBits"]
        );

        return await window.crypto.subtle.deriveKey(
            {name: "ECDH", public: publicKey},
            privateKey,
            {name: "AES-GCM", length: 256},
            true,
            ["encrypt", "decrypt"]
        );
    };

    async encrypt(text) {

        const derivedKey = await this.deriveKey();

        const encodedText = new TextEncoder().encode(text);

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

        const encryptedData = await window.crypto.subtle.encrypt(
            {name: "AES-GCM", iv: iv},
            derivedKey,
            encodedText
        );

        const uintArray = new Uint8Array(encryptedData);

        const string = String.fromCharCode.apply(null, uintArray);
        const iv_string = String.fromCharCode.apply(null, iv);

        const bDATA = btoa(string);
        const bIV = btoa(iv_string);

        return JSON.stringify({bDATA, bIV});
    };

    async decrypt(messageJSON) {
        try {

            const derivedKey = await this.deriveKey();

            const message = JSON.parse(messageJSON);
            const text = message.bDATA;
            const initializationVector = message.bIV;

            const iv_string = atob(initializationVector);
            const iv = new Uint8Array(
                [...iv_string].map((char) => char.charCodeAt(0))
            );

            const string = atob(text);
            const uintArray = new Uint8Array(
                [...string].map((char) => char.charCodeAt(0))
            );
            const algorithm = {
                name: "AES-GCM",
                iv: iv,
            };
            const decryptedData = await window.crypto.subtle.decrypt(
                algorithm,
                derivedKey,
                uintArray
            );

            return new TextDecoder().decode(decryptedData);
        } catch (e) {
            return `error decrypting message: ${e}`;
        }
    };

}


Остальное - "детали" - если взять за "ядро" - JS выше...

Вопрос:
Какие видятся минусы с точки зрения безопасности пересылаемых данных?
Если "отбросить" самый главный минус - "скомпрометированное устройство" самого обладателя.
  • Вопрос задан
  • 53 просмотра
Подписаться 1 Средний Комментировать
Помогут разобраться в теме Все курсы
  • Skillbox
    JavaScript
    3 месяца
    Далее
  • Яндекс Практикум
    Фронтенд-разработчик расширенный
    13 месяцев
    Далее
  • Академия Eduson
    Fullstack-разработчик на JavaScript
    11 месяцев
    Далее
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы