Привет!
Мы уже показывали вам код, с помощью которого можно легко и просто реализовать end-to-end шифрование. Давайте пойдем дальше и, используя сервисы Virgil Security, разработаем безопасный IP мессенджер. Реализация безопасного IP мессенджера со встроенной функцией шифрования – задача далеко не тривиальная. В этой статье мы хотим рассказать как, используя Virgil API и Тwilio IP Messaging API, сделать этот процесс намного проще.

Часть 1: подготовительная


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

  1. Создать аккаунт Twilio. Twilio — компания, предоставляющая в качестве сервиса средства коммуникации. С помощью Twilio задача разработки IP мессенджера сильно упрощается. Однако в этой статье наша цель гораздо более амбициозная. Напоминаем, что нам требуется безопасный(не путайте с анонимным) IP мессенджер с функцией end-to-end шифрования. Поэтому нам потребуется…

  2. Создать аккаунт Virgil Security. Аккаунт открывает возможность зарегистрировать приложение и получить VIRGIL_ACCESS_TOKEN — секретный ключ, позволяющий пользоваться сервисами Virgil Security.

После выполнения вышеперечисленных шагов вы станете счастливым обладателем следующего набора параметров:
  1. TWILIO_ACCOUNT_SID — идентификатор вашего аккаунта Twilio (доступен в вашем аккаунте).
  2. TWILIO_API_KEY — секретный ключ, используемый для аутентификации(сгенерировать его можно здесь).
  3. TWILIO_API_SECRET — дополнительная секретная информация, необходимая для аутентификации(создается тут).
  4. TWILIO_IPM_SERVICE_SID — экземпляр службы Twilio, в котором хранятся все данные о вашем приложении(создать можно по ссылке).
  5. VIRGIL_ACCESS_TOKEN — уникальный маркер, позволяющий получить доступ к Virgil Security API. Каждый запрос к API должен содержать VIRGIL_ACCESS_TOKEN. Получить его можно после создания Virgil приложения тут.
  6. VIRGIL_APP_PRIVATE_KEY — секретный ключ вашего Virgil приложения. Используется для создания validation token для аутентификации публичных ключей ваших пользователей в случае, если использование Identity Service нежелательно. Секретный ключ генерируется на вашем компьютере на странице вашего приложения после входа на сайт Virgil.
  7. VIRGIL_APP_PRIVATE_KEY_PASSWORD — все секретные ключи хранятся в зашифрованном виде, пароль для шифрования необходим при каждом использовании ключа.

Часть 2: основная


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

Прежде всего отметим, что использование сервисов Virgil Security возможно практически с любой платформы и на любом языке программирования. Для разработчиков доступен широкий набор SDK(C#, C/C++, Objective-C, Python, Java), облегчающих работу с Virgil Services. Однако их использование невозможно без VIRGIL_ACCESS_TOKEN. Т.к. мы уже получили VIRGIL_ACCESS_TOKEN на предварительном этапе, переходим непосредственно к разработке.
Наш мессенджер будет иметь клиент серверную структуру. Но все самое интересное(шифрование, отправление и расшифровка сообщений) будет происходит на стороне клиента, на ней мы и сосредоточим большую часть нашего внимания.

  1. Подключаем JavaScript SDK для Twilio и Virgil API:

    <script src="https://cdn.virgilsecurity.com/packages/javascript/sdk/1.4.6/virgil-sdk.min.js" 
            integrity="sha256-6gsCF73jFoEAcdAmVE8n+LCtUgzQ7j6svoCQxVxvmZ8=" 
            crossorigin="anonymous"></script>
    
    <script src="https://media.twiliocdn.com/sdk/js/common/v0.1/twilio-common.min.js"></script>
    <script src="https://media.twiliocdn.com/sdk/rtc/js/ip-messaging/v0.10/twilio-ip-messaging.min.js"></script>
    

  2. Инициализируем работу с сервисами(токены для работы с API получаем от сервера):

    // Инициализация Twilio
    var accessManager = new Twilio.AccessManager('%TWILIO_TOKEN%');
    var messagingClient = new Twilio.IPMessaging.Client(accessManager);
    // Инициализация Virgil
    var virgil = new VirgilSDK("%VIRGIL_ACCESS_TOKEN%");
    

  3. С помощью Virgil SDK создадим пару ключей для нового пользователя Алиса:

    var keyPair = virgil.crypto.generateKeyPair();
    

    И получим следующее:
    -----BEGIN PUBLIC KEY-----
    MFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAAQO8ohmBRyclmcfQ38Lwmvv4Cau
    jyX6vWn8kJrR0RRfFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    -----END PUBLIC KEY-----
    ?
    -----BEGIN EC PRIVATE KEY-----
    MHkCAQEEIFB+lOUvbb4WX+e3zLkAcYpvZR3qpQI8Ru/tcnciCMkIoAwGCisGAQQB
    l1UBBQGhRANCAAQO8ohmBRyclmcfQ38Lwmvv4CaujyX6vWn8kJrR0RRfFQAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    -----END EC PRIVATE KEY-----
    

  4. Для того чтобы другие участники переписки могли отправлять Алисе зашифрованные сообщения, её публичный ключ должен быть размещен в хранилище ключей Virgil Keys Service. Процесс публикации ключа требует специального validation token, предотвращающего создание неподтвержденных ключей. Получить validation token можно с помощью нашего сервиса проверки личности Identity Service, либо сгенерировав его самостоятельно(на стороне сервера), используя VIRGIL_APP_PRIVATE_KEY:

    VirgilSDK.utils.generateValidationToken(
        'Alice', // значение идентификатора
        'username',    // тип идентификатора (любая строка)
        VIRGIL_APP_PRIVATE_KEY
    );
    

    Получив validation token загрузим публичный ключ пользователя в хранилище:

    var options = {
         public_key: keyPair.publicKey,
         private_key: keyPair.privateKey,
         identity: {
             type: 'username',
             value: 'Alice',
             validation_token: '%VALIDATION_TOKEN%'
         }
    };
    
    virgil.cards.create(options).then(function (card){
        myCard = card;
        console.log(card);
    });
    

    После выполнения вышеописанных шагов в хранилище ключей Virgil Keys Service будет создана специальная структура данных, доступная любому пользователю вашего приложения, называемая Virgil Card и включающая следующие поля:

    Пример созданной структуры данных Virgil Card:
    {  
       "id":"3e5a5d8b-e0b9-4be6-aa6b-66e3374c05b3",
       "authorized_by":"com.virgilsecurity.twilio-ip-messaging-demo",
       "hash":"QiWtZjZyIQhqZK7+3nZmIEWFBU+qI64EzSuqBcY+E7ZtKPwd4ZyU6gdfU/VzbTn6dHtfahCzHasN...",
       "created_at":"2016-05-03T14:34:08+0000",
       "public_key":{  
          "id":"359abe31-3344-453a-a292-fd98a83e500a",
          "public_key":"-----BEGIN PUBLIC KEY-----\nMFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAAQ...",
          "created_at":"2016-05-03T14:34:08+0000"
       },
       "identity":{  
          "id":"965ea277-ab78-442c-93fe-6bf1d70aeb4b",
          "type":"username",
          "value":"Alice",
          "created_at":"2016-05-03T14:34:08+0000"
       }
    }
    

  5. Теперь пользователь Алиса готов к безопасному общению. Создадим для этого канал 'general':

    // Создаем новый канал
    twilioClient.createChannel({ friendlyName: 'general' }).then(function(channel) {
        generalChannel = channel;
    });
    

  6. Любой желающий способен зайти в канал 'general' и отправить Алисе зашифрованное сообщение. Для этого находим в хранилище Virgil Keys Service публичный ключ Алисы и зашифровываем с его помощью секретное сообщение:

    // Получаем список участников канала
    Promise.all(generalChannel.getMembers().map(function(member) {
        // Ищем Virgil Card всех участников переписки
        return virgil.cards.search({ value: member.identity, type: 'username' })
            .then(function(cards){
                return { 
                    recipientId: cards[0].id, 
                    publicKey: cards[0].public_key.public_key
                };
        });
    }).then(function(recipients) {
        var message = $('#chat-input').val();
        var encryptedMessage = virgil.crypto.encryptStringToBase64(message, recipients);
    
        generalChannel.sendMessage(encryptedMessage);    
        console.log(encryptedMessage);
    });
    

    Зашифрованное сообщение имеет следующий вид:
    MIIDBQIBADCCAv4GCSqGSIb3DQEHA6CCAu8wggLrAgECMYICvDCCAVoCAQKgJgQkMDg3YjgwYmMtMzNjYi00MTI1LWI4YTgtYTE
    3OTEwM2Y3ZjRkMBUGByqGSM49AgEGCisGAQQBl1UBBQEEggEUMIIBEAIBADBbMBUGByqGSM49AgEGCisGAQQBl1UBBQEDQgAEcd
    8fhKqYlZxvcmmodg7Z3PNhE1LXLJqobouEcRfZaRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAYBgcogYxxAgUCM
    A0GCWCGSAFlAwQCAgUAMEEwDQYJYIZIAWUDBAICBQAEMEaJMAvX7S+52BpI5hYyFOc0noIc+qdFFrQanNAtNGBAX/Pxeg5yJ2iA
    JijyZ8ut9zBRMB0GCWCGSAFlAwQBKgQQ81bklcNOyU/QTatCigSzoAQwHnAcbXk0daExIIS+sr6aIvVuF/o6j+1Rs5bvq2WVN41
    k/Oir5x7KZTSR7v3nx+fTMIIBWgIBAqAmBCRmNzM4YTUwNi1hMDYwLTQ1MDgtYTJkYS04NjY1NjZlYzg0ODMwFQYHKoZIzj0CAQ
    YKKwYBBAGXVQEFAQSCARQwggEQAgEAMFswFQYHKoZIzj0CAQYKKwYBBAGXVQEFAQNCAARJ5C3hsYuI2Sf14k60Dz5Mv5yD/AsVA
    zPfsmlreGTC2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBgGByiBjHECBQIwDQYJYIZIAWUDBAICBQAwQTANBglg
    hkgBZQMEAgIFAAQwhu7WM1rff9RYsQ+dmfX9Os3Irwm4cm5bIvUlcGXlCfmEsrjTyTg5MGjYLtxbYtL9MFEwHQYJYIZIAWUDBAE
    qBBCfKdP/gZnkVwJvv4Hdf2eWBDC3czBjV/yPGeGTqBIilHSsrqwK7lVMTBuKR+mR3eNdh+yBIAcOk4rveSUbDuWagDIwJgYJKo
    ZIhvcNAQcBMBkGCWCGSAFlAwQBLgQMfjkCvK3UgXdorcYUmtCHHuSm4yfBacMsniMADAeos7qN7OmNsFU1

    При этом не стоит забывать, что несмотря на то, что речь в данной статье идет о текстовых сообщения, таким же образом вы сможете зашифровать любые бинарные данные: изображение, аудио или видео.

  7. Чтобы расшифровать адресованное ей сообщение, Алиса должна воспользоваться приватным ключом, который хранится только у нее:

    // Прослушиваем канал и получаем новое сообщение
    generalChannel.on('messageAdded', function(message) {
    
        // Расшифровываем сообщение, используя Card Id и секретный ключ
        var decryptedMessage = virgil.crypto.decryptStringFromBase64(
            message.body, 
            myCard.id, 
            keyPair.privateKey
        );
    
        console.log(message.author + ': ' + decryptedMessage);
    });
    

    И получит текст сообщения
    Bob: Привет, Алиса!


Часть 3: заключительная


Как видите, создать IP мессенджер со встроенной функцией end-to-end шифрования достаточно просто. Разумеется, ваш мессенджер может быть написан на любом языке программирования и для любой платформы, при этом реализация шифрования всегда останется такой же простой.

Посмотреть демо версию мессенжера с возможностью создавать новые каналы и шифровать сообщения можно по ссылке.

Исходный код демо версии Virgil+Twilio IP мессенжера можно найти на GitHub.

Все наши тьюториалы по работе с Virgil Security под различными языками программирования доступны тут.

UPD
По просьбам трудящихся перечислим несколько вариантов использования описанного мессенджера:
  1. Встраивание системы мгновенных сообщение в ваше собственное приложение. Это позволит пользователям общаться без привлечения стороннего приложения.

  2. Добавление мессенджера на веб сайт для прямого общения со службой продаж или тех. поддержкой.

  3. Добавление мессенджера в любой инструмент используемый организацией(к примеру система документооборота). Это позволит избежать необходимости использовать дополнительные инструменты для общения внутри компании.

Поделиться с друзьями
-->

Комментарии (10)


  1. Ivan_83
    08.06.2016 11:46
    +1

    Итого две конторы которые могут в любой момент отозвать свои ключи и безопасный чат превратится в бесполезный, за ещё меньшее время чем было потрачено на его создание.
    Не говоря о том, что остаётся принципиальный вопрос доверия к криптолибе.

    А чего у вас в ключах и зашифрованном сообщении так много «AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA»?
    Там должно быть равномерное распределение.
    И публичный ключ как то много приватного содержит в конце.


    1. VirgilSecurity
      08.06.2016 15:00
      +1

      Большое спасибо за неугасающий интерес.
      Действительно, когда речь заходит о сервисе предоставляющим криптографические услуги, вопрос доверия встает ребром. Понимая это, мы пытаемся как можно дальше дистанцироваться от такой вещи как управление ключами. Все приватные ключи генерируются исключительно на стороне клиентов, так что мы не можем прочитать ни одно зашифрованное сообщение.
      Кроме того, мы дали возможность нашим клиентам самостоятельно подтверждать ключи пользователей(подписав их своим секретным ключом), что избавит нас от возможности подмены публичных ключей. Одним словом мы постарались сделать так, чтобы ваше доверие не было беспочвенным.
      Что касается нашей криптобиблиотеки, она выложена в открытом доступе. Любой желающий может провести аудит на предмет бэкдоров и потенциальных уязвимостей. Как видите, с этой стороны доверие тоже подлежит проверке.
      Да, конечно, в случае форс мажора мы можем отозвать ключи. И тогда мессенджер перестанет функционировать, но давайте начистоту, от непредсказуемых ситуаций никто не застрахован. Тоже самое можно сказать про абсолютно любой сервис и услугу, вы же не будете отказываться от удобств интернета из за риска его отключения?

      А чего у вас в ключах и зашифрованном сообщении так много «AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA»?

      Публичный ключ представляет собой точку эллиптической кривой. В edDSA точки представлены координатами Монтгомери. Т.е. имеет значение только x-координата, y-координата представляется последовательностью нулей, которые и отображаются вот в таком пугающем виде.

      И публичный ключ как то много приватного содержит в конце.

      На самом деле это приватный ключ содержит слишком много публичного:)
      В rfc5915 прописана структура приватного ключа, в котором должно быть поле PublicKey.


      1. VirgilSecurity
        08.06.2016 15:06

        В edDSA точки представлены

        В Curve25519 конечно же!


      1. Ivan_83
        10.06.2016 12:25

        Всегда пожалуйста.

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

        Битмессадж и токс вполне себе функционируют почти без инфраструктуры, и я подозреваю что можно относительно не сложно существовать на чужих открытых инфраструктурах типа торрента (собственно от них нужны пиры/ноды DHT).

        Если координата Х не нужна то зачем её писать?
        А если пишите то почему не писать реальные значения?

        Почему в зашифрованном сообщении много AAAA последовательностей?

        Аутентификация сообщений отсутствует?


        1. sseroshtan
          10.06.2016 13:35

          Если координата Х не нужна то зачем её писать?


          Используется представление точки на эллиптической кривой в байтовой последовательности без сжатия
          SEC 1 Ver. 2.0 см. секцию "2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion"

          А если пишите то почему не писать реальные значения?


          Пишется на самом деле реальное зачине, просто для Curve25519 оно всегда равно нулю.

          Почему в зашифрованном сообщении много AAAA последовательностей?


          Зашифрованное сообщение хранит в себе открытую часть эфемерного ключа, который генерируется в соответствии с алгоритмом ECIES, а раз так, то "AAAA" попадают и туда.

          Аутентификация сообщений отсутствует?


          Аутентификация сообщения обеспечивается режимом GCM алгоритма AES


  1. sheknitrtch
    09.06.2016 22:31
    +1

    Здорово, что кто-то пытается сделать простую и удобную криптографию. По поводу вашего API: получается у пользователя может быть несколько Virgil Cards для одного и того же «identity»? Как тогда оправляющая сторона поймёт какой публичный ключ использовать?

    И ещё один вопрос из-за которого Я не могу спокойно спать: почему Python 2.7? Вы же не Dropbox! Вы же можете писать под Python 3.x ))


    1. sheknitrtch
      09.06.2016 22:41

      Ой, про Python снимается вопрос (Я не туда посмотрел в PyPI). Bitte entschuldigen :)


    1. denzzel
      10.06.2016 04:26
      +1

      Спасибо, это действительно хороший вопрос, на самом деле все зависит от конкретной задачи. Бывает так, что в приложении требуется несколько одновременно активных публичных ключей. И мы сделали так, что разработчик в праве сам решать, сколько ключей пользователь может иметь в рамках своего приложения.
      Так же ничего не мешает отправителю зашифровать данные для нескольких публичных ключей получателя, основываясь на данных в Virgil Card. Мы постарались не ограничивать разработчика в полете его фантазий :)


  1. neopug
    10.06.2016 02:12

    Поддержка у twilio работает, как бы это помягче сказать, — сомнительно.
    Клиент просил настроить SIP trunk на CISCO ISR именно через этот сервис. Вылезла проблема — исходящие звонки идут, а входящие не поступают, хотя регистрация висит.
    Создал тикет в поддержку: оборудование такое-то, проблема такая-то, дайте дебаги. На следующий день (через 23 часа) приходит ответ — А что у тебя, мил человек, за оборудование и АТС? И вообще — кури мануалы. На мое, резонное, отвеченное сразу, — уже курил, ответ пришел через _10_ дней: извинились за задержку и сообщили, что не видят от меня попыток регистрации (к этому времени подключил клиента к другому провайдеру, у которого все завелось с первого раза).

    Может это, конечно, только что касается SIP trunk'ов, но осадочек остался.


  1. Scratch
    10.06.2016 11:34

    А мне с twilio на Tele2 СМС приходят бывает через 3 дня. У нас там алертилка настроена, иногда получаю сообщения о падении серверов много после того, как их уже подняли