В этом руководстве мы создадим пример приложения для зашифрованного чата / обмена сообщениями для Android. Для этого мы объединим платформы Stream Chat и Virgil Security. Stream и Virgil упрощают создание решения с превосходной безопасностью, объединяя все функции, которые вы ожидаете как разработчик при создании приложения для обмена сообщениями.

Эти две службы позволяют разработчикам интегрировать чат, о котором ничего не известно, в ваш бэкэнд или Stream. Пример приложения включает в себя eThree Kit от Virgil Security - платформу безопасного зашифрованного обмена сообщениями - с компонентами Stream Chat Android.

Обратите внимание, что весь исходный код для этого примера приложения для Android доступен на GitHub. Кроме того, прежде чем переходить к этому руководству, я рекомендую ознакомиться с Руководством по Stream Chat для Android, в котором вы узнаете, как реализовать Stream Chat, на основе общего обзора.

Что такое сквозной шифрованный обмен сообщениями?

Сквозной обмен зашифрованными сообщениями означает, что пользователи в этом конкретном чате могут читать только сообщения, отправленные между двумя людьми. Для этого отправляемые сообщения шифруются перед отправкой с устройства пользователя и могут быть расшифрованы только предполагаемым получателем (конечным пользователем).

Virgil Security - поставщик, который позволяет разработчикам создавать сквозное шифрование с помощью технологии открытого / закрытого ключа с помощью надежной и безопасной службы обмена зашифрованными сообщениями. С помощью Android SDK Вирджила разработчики могут безопасно создавать, хранить и обеспечивать надежное сквозное шифрование.

В этом руководстве мы узнаем, как создать приложение Stream Chat, использующее платформу шифрования / дешифрования Virgil, чтобы никто, кроме предполагаемых сторон, не мог читать сообщения. Эти сообщения не могут прочитать никто в вашей компании или какой-либо облачный провайдер, которого вы используете. По сути, даже если злоумышленник получит доступ к базе данных, содержащей сообщения, этот человек увидит только зашифрованный текст, называемый зашифрованным текстом.

Создание приложения для обмена зашифрованными сообщениями чата

Чтобы создать это приложение, мы в основном будем полагаться на две библиотеки: Stream Chat Android и Virgil Security для Kotlin. Наш результат зашифрует текст на устройстве перед отправкой сообщения. И дешифрование, и проверка будут происходить на устройстве получателя. Messaging API Stream будет видеть только зашифрованный текст, гарантируя, что данные наших пользователей никогда не будут видны никому, включая нас.

Для этого приложение выполняет следующие шаги:

  1. Пользователь проходит аутентификацию на вашем сервере.
  2. Приложение пользователя запрашивает токен аутентификации Stream и ключ API у серверной части. Приложение Android создает для этого пользователя Клиент потокового чата.
  3. Приложение пользователя запрашивает токен аутентификации Virgil у серверной части и регистрируется у Virgil. Это генерирует их закрытый и открытый ключи. Закрытый ключ хранится локально, а открытый ключ хранится в Вирджиле.
  4. После того, как пользователь решит, с кем он хочет общаться, приложение создает и присоединяется к Каналу потокового чата.
  5. Приложение запрашивает у Вирджила открытый ключ получателя.
  6. Пользователь набирает сообщение и отправляет его в Stream. Перед отправкой приложение передает Вирджилу открытый ключ получателя для шифрования сообщения. Сообщение передается получателю через Stream Chat. Stream получает зашифрованный текст, что означает, что они никогда не смогут увидеть исходное сообщение.
  7. Принимающий пользователь расшифровывает отправленное сообщение с помощью Вирджила. Когда сообщение получено, приложение расшифровывает сообщение с помощью Virgil, и оно передается компонентам пользовательского интерфейса Stream. Вирджил проверяет подлинность сообщения, используя открытый ключ отправителя.

Хотя это выглядит сложным, за нас большую часть работы делают Стрим и Вирджил. Мы будем использовать готовые компоненты пользовательского интерфейса Stream для визуализации пользовательского интерфейса чата, а Вирджил будет выполнять все криптографические операции и управление ключами. Мы просто совмещаем эти услуги.

Код разделен между интерфейсом Android, содержащимся в каталоге android, и серверной частью Express (Node.js), находящейся в каталоге backend. См. README.md в каждом каталоге, чтобы увидеть инструкции по установке и запуску. Если вы хотите продолжить выполнение кода, убедитесь, что у вас запущены как backend, так и android, прежде чем продолжить.

Давайте рассмотрим важный код, необходимый для каждого шага.

Предпосылки

Чтобы следовать этому руководству, необходимы базовые знания Android (Kotlin) и Node.js.

Обратите внимание, что это приложение для безопасного обмена сообщениями предназначено только для локального запуска на вашем компьютере, и мы не будем рассказывать, как развернуть пример приложения в магазинах приложений iOS или Android.

Мы используем Anko для упрощения нашего асинхронного кода. Обратите внимание, что эта библиотека недавно устарела. Также вероятны ошибки в нашей реализации async. Однако в этом уроке мы решили свести шум к минимуму, используя Anko и сохранив простую асинхронность. Пожалуйста, используйте лучшие практики для асинхронного кода.

Вам понадобится аккаунт в Стрим и Вергилий. Создав учетные записи, вы можете ввести свои учетные данные в backend/.env, если хотите запустить код. Вы можете использовать backend/.env.example как справочную информацию о том, какие учетные данные требуются. Вам также необходимо поместить свой ключ Stream API в MainActivity.kt:60.

Шаг 0. Настройте Backend

Чтобы наш интерфейс Android мог взаимодействовать со Stream и Virgil, приложение предоставляет три конечные точки:

  • POST /v1/authenticate: эта конечная точка генерирует токен аутентификации, который позволяет приложению Android взаимодействовать с /v1/stream-credentials и /v1/virgil-credentials. Для простоты эта конечная точка позволяет клиенту быть любым пользователем. Фронтенд сообщает бэкэнду, под кем он хочет пройти аутентификацию. В вашем приложении это должно быть заменено конечной точкой аутентификации вашего API.
  • POST /v1/stream-credentials: возвращает данные, необходимые для связи приложения Android со Stream. Чтобы вернуть эту информацию, нам нужно сообщить Stream, что этот пользователь существует, и попросить их создать действительный токен аутентификации:

Полезные данные ответа имеют такую ​​форму:

  • apiKey - идентификатор учетной записи потока для вашего экземпляра Stream. Требуется определить, с какой учетной записью пытается подключиться ваш интерфейс.
  • token Токен JWT для авторизации внешнего интерфейса с помощью Stream.
  • user: этот объект содержит данные, необходимые интерфейсу для подключения и визуализации представления пользователя.
  • POST /v1/virgil-credentials: возвращает токен аутентификации, используемый для подключения внешнего интерфейса к Вирджилу. Мы используем Virgil Crypto SDK, чтобы сгенерировать для нас действительный токен аутентификации:

В этом случае интерфейсу нужен только токен авторизации.

Шаг 1. Пользователь аутентифицируется с помощью серверной части

Сначала мы авторизуемся под пользователем. Для простоты у нас будет пустая форма, в которой вы сможете войти под любым именем:

Это простая форма, которая принимает любое произвольное имя, что позволяет нам войти в систему как кто угодно. Мы настроили это в нашем MainActivity:

И макет:

Когда мы отправляем форму, мы входим в наш бэкэнд, получаем токен аутентификации Stream и Virgil, генерируем наш закрытый ключ и регистрируемся у Virgil, а затем начинаем наше следующее действие. Мы рассмотрим каждый из них по очереди.

Давайте посмотрим, как мы выполняем вход и генерируем токены:

Поскольку наш backend (см. Шаг 1) генерирует токены, это простые вызовы REST. Возвращенные токены являются токенами аутентификации внешнего интерфейса, которые позволяют нашему клиенту напрямую общаться со Stream и Вирджилом. Помимо возврата списка пользователей, нам больше не нужен наш бэкэнд для выполнения какой-либо работы.

Теперь, когда у нас есть токены внешнего интерфейса, давайте сгенерируем наши закрытые ключи и зарегистрируем наши открытые ключи у Вирджила:

Клиента Вирджила зовут eThree. Мы инициализируем EThree экземпляр и регистрируемся. Этот вызов генерирует закрытый ключ, сохраняет его на устройстве и отправляет наш открытый ключ Вирджилу. Если мы получим RegistrationException, мы уже зарегистрировали этого пользователя. Имейте в виду, что вы не можете войти в систему одного и того же пользователя на другом устройстве, поскольку мы не передаем закрытый ключ другому устройству! Это возможно, но выходит за рамки данного руководства. Если вы хотите этого добиться, см. Документацию Вирджила.

Теперь, когда у нас есть токены и регистрация, давайте найдем пользователя, с которым можно будет поговорить!

Шаг 2. Составьте список пользователей

Чтобы не усложнять задачу, мы получим всех зарегистрированных пользователей из нашего бэкэнда и отобразим их в виде простого списка:

Вот действие:

И макет:

Мы делаем вызов API через BackendService.getUsers и отфильтровываем вошедшего в систему пользователя. Мы добавляем ответ к простому ArrayAdapter и отображаем наши результаты в ListView. Когда пользователь щелкает элемент списка, мы запускаем ChannelActivity, который представляет собой канал чата 1: 1.

Шаг 3. Создайте частный канал 1: 1

Во-первых, нам нужно создать наш канал для нашего приватного чата. Давайте посмотрим на нашу деятельность и макет:

И макет:

Мы используем готовые компоненты пользовательского интерфейса Stream с двумя небольшими вариациями. Сначала мы подключаем пользовательский EncryptedMessageInputView, который позволяет нам зашифровать сообщение перед его отправкой. Мы также подключаем пользовательский EncryptedMessageViewHolderFactory, который позволяет расшифровывать сообщения (мы рассмотрим это немного позже). Важнейшие биты начинаются внутри doAsync. Сначала мы ищем открытый ключ другого пользователя. Это позволяет использовать шифрование наших сообщений и проверять их подлинность. Затем мы создаем канал в Stream через .query. Как только канал создан, мы загружаем сообщения. Прежде чем мы посмотрим, как мы загружаем сообщения, нам нужно сначала отправить сообщение.

Шаг 4: Отправка зашифрованного сообщения

Давайте посмотрим на EncryptedMessageInputView, который привязан к макету и настроен в нашей деятельности. Вот код:

Мы переопределяем MessageInputView Stream и просто расшифровываем сообщение перед его отправкой. MessageInputView вызывает prepareMessage перед отправкой в ​​API, поэтому мы переопределяем это и шифруем перед отправкой сообщения.

Обратите внимание, что при обновлении сообщения этот метод не используется, поэтому имейте в виду, если вы хотите поддерживать функцию редактирования. Вы можете подумать о создании своего собственного MessageInputView. Для краткости в этом уроке мы не будем вдаваться в подробности.

Шаг 5: просмотр сообщений

Поскольку все наши сообщения в Stream - это наш теперь зашифрованный текст, нам необходимо расшифровать их перед отображением. Чтобы подключиться к компонентам пользовательского интерфейса Stream, мы выполняем привязку через binding.messageList.setViewHolderFactory(EncryptedMessageViewHolderFactory(eThree)). С помощью нашей настраиваемой фабрики мы можем инициализировать настраиваемый объект дешифрования для каждого сообщения:

Эта фабрика проверяет, есть ли у нас тип сообщения (по сравнению с другим типом, например, разделителем даты), и инициализирует наш EncryptedMessageViewHolder. Давайте посмотрим:

Сначала мы проверяем, есть ли у нас обычное сообщение, и если да, то расшифровываем его. Мы копируем наш объект сообщения, чтобы избежать изменения исходной версии (Stream кеширует это, и мы все испортим, если будем манипулировать этим). С помощью этой копии мы проверяем, является ли сообщение нашим или их. В нашем случае мы знаем, как расшифровать напрямую, так как мы это создали. Если это их, нам нужно найти их открытый ключ, чтобы проверить сообщение. Мы передаем его Вергилию и делаем расшифровку.

Объединив эти последние шаги, мы увидим наш конечный продукт:

Последние мысли

Вот и все. Теперь у нас есть частное и чрезвычайно безопасное приложение для обмена сообщениями со сквозным шифрованием, созданное для Android с помощью Stream Chat и Virgil Security. Вы должны иметь полное представление об основах сквозного шифрования с использованием Stream Chat и Virgil Security, а также фундаментальное понимание того, как работает процесс E2EE при его реализации с Android, Virgil и Stream Chat.

Если вы хотите сделать еще один шаг вперед, Stream Chat предлагает несколько ресурсов, которые помогут вам перейти на следующий уровень в работе с Android. Просмотрите ссылки ниже:

Удачного кодирования, и, как всегда, не стесняйтесь оставлять любые мысли или вопросы в комментариях ниже.

Первоначально опубликовано в блоге Stream по адресу https://getstream.io/blog/encrypted-messaging-app-android/ .