#e2e-encryption #signal-protocol
#e2e-шифрование #сигнальный протокол
Вопрос:
Я использую libsignal-protocol-java в соответствии с документацией Signal для реализации сквозного шифрования в приложении для обмена сообщениями.
Допустим, Алиса отправляет Бобу (попарно) сериализованное зашифрованное текстовое сообщение. Откуда Боб знает, как его десериализовать? Не нужно ли ему сначала узнать тип зашифрованного текстового сообщения? Насколько я понимаю, попарное зашифрованное текстовое сообщение может быть либо сигнальным сообщением ( WHISPER_TYPE
), либо сигнальным сообщением перед ключом ( PREKEY_TYPE
). Итак, как Боб узнает, какой это тип?
Предполагается ли, что Алиса также отправит Бобу тип (в виде открытого текста) зашифрованного текстового сообщения?
Или есть другой способ, которым Боб мог бы определить тип? Например, предполагается ли, что Боб попытается десериализовать его, как если бы это было сигнальное сообщение, и если это не удается, то попробуйте десериализовать его, как если бы это было сигнальное сообщение до ключа?
Ответ №1:
После просмотра исходного кода Signal Android, я думаю, что Алиса должна также отправить Бобу тип (в виде открытого текста) зашифрованного текстового сообщения.
В классе SignalServiceCipher
:
-
Метод экземпляра
encrypt(SignalProtocolAddress, Optional<UnidentifiedAccess>, byte[])
возвращает экземплярOutgoingPushMessage
соtype
свойством, равным свойству зашифрованного текстового сообщения, и егоbody
свойством, равным сериализованному зашифрованному текстовому сообщению в кодировке Base64. -
Метод экземпляра
decrypt(SignalServiceEnvelope, byte[])
получает тип зашифрованного текстового сообщения из своего первого аргумента, который является экземпляром SignalServiceEnvelope.
Ответ №2:
1 — Алиса генерирует identityKeyPair (долгосрочный), signedPreKey (среднесрочный) и эфемерные предварительные ключи и сохраняет эти ключи в хранилище в base64. например
public static String generateIdentityKeyPair() {
IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair();
return encodeToBase64(identityKeyPair.serialize());
}
2 — Отправить
- Список идентификаторов предварительного ключа и открытого ключа для сервера в сериализованном формате
- Подписанный предварительный ключ Id, подписанный предварительный ключ Publickey, подписанный предварительный ключ Recordsignature
- Открытый ключ IdentityKeyPair
- Регистрационный идентификатор
Для шифрования и дешифрования сначала необходимо выполнить зашифрованный сеанс
private void initSessionFromPreKey() throws UntrustedIdentityException, InvalidKeyException {
InMemorySignalProtocolStore protocolStore = new InMemorySignalProtocolStore(localUser.getIdentityKeyPair(), localUser.getRegistrationId());
protocolStore.storePreKey(localUser.getPreKeys().get(0).getId(), localUser.getPreKeys().get(0));
protocolStore.storeSignedPreKey(localUser.getSignedPreKey().getId(), localUser.getSignedPreKey());
this.protocolStore = protocolStore;
//Session
SessionBuilder sessionBuilder = new SessionBuilder(protocolStore, remoteUser.getSignalProtocolAddress());
PreKeyBundle preKeyBundle = new PreKeyBundle(
remoteUser.getRegistrationId(),
remoteUser.getSignalProtocolAddress().getDeviceId(),
remoteUser.getPreKeyId(),
remoteUser.getPreKeyPublicKey(),
remoteUser.getSignedPreKeyId(),
remoteUser.getSignedPreKeyPublicKey(),
remoteUser.getSignedPreKeySignature(),
remoteUser.getIdentityKeyPairPublicKey()
);
sessionBuilder.process(preKeyBundle);
mSessionCipher = new SessionCipher(protocolStore, protocolAddress);
}
Шифрование и дешифрование
public String encrypt(String message) throws InvalidVersionException, InvalidMessageException, UntrustedIdentityException, InvalidKeyException {
createSession(Operation.ENCRYPT);
CiphertextMessage ciphertextMessage = mSessionCipher.encrypt(message.getBytes());
PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(ciphertextMessage.serialize());
return KeyUtils.encodeToBase64(preKeySignalMessage.serialize());
}
public String decrypt(String message) throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException {
createSession(Operation.DECRYPT);
byte[] bytes = KeyUtils.decodeToByteArray(message);
byte[] decryptedMessage = mSessionCipher.decrypt(new PreKeySignalMessage(bytes));
return new String(decryptedMessage, StandardCharsets.UTF_8);
}
Вы также можете посмотреть другой исходный код, который доступен на github
https://github.com/lvijay/DemoSignal
https://github.com/signalapp/libsignal-protocol-java/pull/21/commits/3496ed996359f6d3d8ee52dcecb8f8b0d45b3cbc (Автор библиотеки использует оболочку сигнального протокола, вы можете изменить библиотеку оболочки на сигнальный протокол)