Шифрование для нескольких получателей — это задача, при которой один отправитель должен передать зашифрованные данные сразу нескольким сторонам так, чтобы:
В библиотеке SJCL (Stanford Javascript Crypto Library) отсутствует готовая высокоуровневая реализация мультиадресного шифрования, однако она предоставляет все необходимые примитивы для построения такой схемы вручную.
Классическая схема шифрования для нескольких получателей строится на гибридном подходе:
Генерируется случайный симметричный ключ (session key).
Сообщение шифруется этим ключом (например, с помощью AES).
Для каждого получателя:
Отправляется:
Такой подход позволяет:
В SJCL используется криптографически стойкий генератор случайных чисел:
const sessionKey = sjcl.random.randomWords(8, 0); // 256 бит
Ключ представляется в виде массива слов (bitArray), что является стандартным форматом SJCL.
Для шифрования данных применяется AES:
const plaintext = "Секретное сообщение";
const encryptedData = sjcl.encrypt(sessionKey, plaintext);
Важно:
sessionKey здесь используется как пароль (SJCL
автоматически обрабатывает его);SJCL поддерживает эллиптическую криптографию (ECC), включая ECDH и ElGamal-подобные схемы.
const keys = sjcl.ecc.elGamal.generateKeys(256);
const publicKey = keys.pub;
const privateKey = keys.sec;
const encryptedKey = publicKey.kem();
Метод kem() (Key Encapsulation Mechanism):
генерирует общий секрет;
возвращает:
key: симметричный ключ;tag: данные для передачи получателю.Однако здесь есть важный момент: нужно использовать полученный
key для шифрования sessionKey.
const sessionKey = sjcl.random.randomWords(8, 0);
const encryptedMessage = sjcl.encrypt(sessionKey, "Секретное сообщение");
const recipients = [pubKey1, pubKey2, pubKey3];
const encryptedKeys = recipients.map(pubKey => {
const kem = pubKey.kem();
const wrappedKey = sjcl.encrypt(kem.key, sessionKey);
return {
tag: kem.tag,
wrappedKey: wrappedKey
};
});
В результате:
kem.key — общий секрет (уникальный для каждого
получателя);wrappedKey — sessionKey, зашифрованный этим
секретом.const payload = {
data: encryptedMessage,
keys: encryptedKeys
};
Структура:
data — зашифрованное сообщение;
keys — массив объектов для каждого получателя:
tag — данные для восстановления ключа;wrappedKey — зашифрованный session key.Получатель перебирает элементы массива keys, используя
свой приватный ключ:
const kem = privateKey.unkem(tag);
Если ключ подходит:
kem даст тот же симметричный ключ, который
использовался для шифрования sessionKey.const sessionKey = sjcl.decrypt(kem, wrappedKey);
const plaintext = sjcl.decrypt(sessionKey, payload.data);
Каждый вызов kem() генерирует новый ключ. Это
гарантирует:
Session key должен быть:
Повторное использование ключа снижает безопасность.
SJCL по умолчанию использует режимы с аутентификацией (например, CCM), но важно учитывать:
При реализации необходимо учитывать:
Иначе возможны атаки типа:
При большом числе получателей:
массив keys растёт линейно;
каждый элемент содержит:
tag;wrappedKey.Оптимизации:
Шифрование для каждого получателя может выполняться параллельно:
Promise.all(recipients.map(pubKey => {
return new Promise(resolve => {
const kem = pubKey.kem();
const wrappedKey = sjcl.encrypt(kem.key, sessionKey);
resolve({
tag: kem.tag,
wrappedKey
});
});
}));
Недостатки:
message → encrypt(pubKey1)
message → encrypt(pubKey2)
...
Недостатки:
Более сложные схемы:
Для подтверждения подлинности сообщения:
payload:const signature = senderPrivateKey.sign(hash(payload));
senderPublicKey.verify(hash(payload), signature);
Это защищает от:
Схема шифрования для нескольких получателей в SJCL строится из следующих компонентов:
Такой подход сочетает: