Если имеется две машины и требуется переслать в одну или другую cторону данные в шифрованном виде — библиотека на php, написанная мною несколько месяцев назад и допиленная вчерашним вечером — то чем я хотел бы поделиться.

Условимся, что машина, которая передает шифрованные данные — это всегда машина A, а машина, которая их принимает — имеет условное обозначение B.

Библиотека решает два возможных случая (при необходимости довнесу функционал):

1) Случай, когда имеется машина (B), которой нужны данные от машины A (например, ей нужно получить толкен клиента) и эти данные должны быть получены безопасно. Т.е. инициатором передачи является машина B.
2) Случай, когда имеется машина и ей необходимо передать шифрованные данные на другую машину (B). В этом случае инициатором передачи является первая машина (А).

Библиотека реализует оба варианта, под каждый из которых есть демо:

Для первого случая в папке server_b_1 есть скрипт testGetDataFromA.php
Для второго случая в папке server_a_1 есть скрипт testPushDataToB.php

Библиотека для обоих случаев одна и та же Encode.php, но для первого случая требуются одни дополнительные скрипты, для второго случая другие, поэтому во избежании путаницы я разнес их функционально на папку server_a_1 и server_b_1 (возможно последующие версии библиотеки будут иметь другую структуру). Таким образом если для обоих машинах необходима реализация и первого случая передачи и второго — каждая такая машина будет иметь у себя обе папки.

Теперь о том как реализованы оба решения:

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

Перед началом разбора реализации укажу лишь, что шифрование симметричного ключа происходит с использованием php функций шифрования Mcrypt по следующей схеме:

$encrypted_data = urlencode(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sinc_key, $notice_text, MCRYPT_MODE_ECB)));

$test_decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256,$sinc_key, base64_decode(urldecode($encrypted_data)),MCRYPT_MODE_ECB));

Работа с асимметричным шифрованием происходит с использованием php OpenSSL

Итак:

Рассмотрю сначала упрощенные схемы обоих, указанных в самом начале случаев передачи, а потом более детально.

1) Случай, когда имеется машина (B), которой нужны данные от машины A (например, ей нужно получить толкен клиента) и эти данные должны быть получены безопасно. Т.е. инициатором передачи является машина B.

Упрощенный алгоритм такой передачи сводится к следующему:

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

Нет гарантии, что машина A — именно наша машина, а не ФСБ-шника Анатолия. Поэтому реализация этого алгоритма библиотекой немного изменена подполнительной проверкой:

(Демо скрипта — server_b_1/testGetDataFromA.php)

На обоих машинах прописан секретный ключ SIGNATURE_KEY, который учавствует в дополнительной проверке. Машина B генерирует пару ключей (приватный и публичный), ключ текущей коннекции и делает запрос (http://.../server_a_1/getDataToB.php) на машину A, отослав ключ текущей коннекции и публичный ключ (приватный оставив у себя). Машина А генерирует симметричный ключ, шифрует им требуемую к передаче секретную информацию N. Также формируются допданные M, которые представляют собой md5 от строки содержащей SIGNATURE_KEY и ключ текущей коннекции. После этого машина А возвращает зашифрованную публичным ключом строку из симметричного ключа и допданных М, а так же зашифрованную симметричным ключом секретную информацию N. Машина B расшифровывает данные с симметричным ключом своим приватным ключом, генерирует строку, подданным М (поскольку вполне может вычислить md5 от строки содержащей SIGNATURE_KEY и ключ текущей коннекции). Если допданные совпадают (что является просто дополнительной проверкой для каждой транзакции, что машина A знает SIGNATURE_KEY, а следовательно — наша машина), машина В извлекает симметричный ключ с помощью которого она расшифровывает секретную информацию N.

2)Случай, когда имеется машина и ей необходимо передать шифрованные данные на другую машину (B). В этом случае инициатором передачи является первая машина (А).

Упрощенный алгоритм такой передачи сводится к следующему:

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

Нет гарантии, что мы получили публичный ключ от машины B, а не от ФСБ-шника Петрова. Поэтому реализация этого алгоритма библиотекой немного изменена дополнительными проверками:

(Демо скрипта — server_a_1/testPushDataToB.php)

На обоих машинах прописан секретный ключ SIGNATURE_KEY, который участвует в дополнительной проверке. Машина A, сгенерировав md5 от ключа текущей коннекции и SIGNATURE_KEY отправляет эти данные (вместе с незашифрованым ключом текущей коннекции) машине B (http://.../server_b_1/get_public_key.php), которая генерирует публичный ключ только если у нее получается такой же md5 от своего SIGNATURE_KEY и полученного ключа текущей коннекции. Это не решает вопроса, что публичный ключ будет получен именно от машины A, а не от машины ФСБ-шника Василия, но гарантирует машеине B, что она генерирует публичный ключ именно для машины A (хотя генерация публичного ключа — дело вообще говоря произвольное, но даже тут лучше перестраховаться). Вместе с публичным ключом генерируется md5 от SIGNATURE_KEY и вторым ключом текущей коннекции. Второй ключ текущей коннекции — произвольный хеш. Данные публичного ключа, второго ключа произвольной коннекции и указанный md5 возвращаются на машину A. Получив второй ключ произвольной коннекции машина A, зная SIGNATURE_KEY генерирует проверочный md5 и если он совпадает с тем, что машина получила — публичный ключ считается именно от машины B, а не от Василия.

Далее машина A (тут уже схема аналогична первому случаю передачи данных) генерирует симметричный ключ и доп проверку, которая представляет собой md5 от SIGNATURE_KEY и ключа текущей коннекции. Эти данные шифруются публичным ключом от машины B. Далее данные вместе с ключом текущей коннекции отправляются на машину B (http://.../server_b_1/pushDataFromA.php), которая генерирует на основе полученного из этих данных ключа текущей коннекции и SIGNATURE_KEY md5, сверяет с полученным, что дает гарантию, что данные не от ФСБ-шника Николая. Если все в порядке и проверка пройдена — с помощью приватного ключа извлекается симметричный ключ, которым уже расшифровывается сообщение.

Буду рад, если кому-то пригодится эта информация.

> Код на гите (там же демо)
Поделиться с друзьями
-->

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


  1. tmnhy
    13.06.2017 14:05
    +5

    Но, зачем?! Ведь есть TLS.


  1. NeLexa
    14.06.2017 13:00

    Зачем вы используете mcrypt для симметричного шифрования, а openssl для асимметричного?
    От mcrypt давно уже пора отказаться в пользу openssl, к тому же в версии PHP 7.1 он объявлен, как deprecated.

    Почему вы выбрали шифр MCRYPT_RIJNDAEL_256? Если вы хотели использовать AES-256, то ошиблись.
    AES-256 — это MCRYPT_RIJNDAEL_128 с 256-битным ключём.


    1. NeLexa
      14.06.2017 13:04

      Сервер A и B лучше переименовать в Alice и Bob по всем канонам криптографии (https://ru.wikipedia.org/wiki/Алиса_и_Боб).


      1. var_null
        14.06.2017 16:39

        хорошая мысль, если буду довносить функционал — переименую ))


  1. augur
    14.06.2017 13:28

    Я правильно понимаю, что SIGNATURE_KEY попадает заранее на обе машины, притом по некоему безопасному каналу? А почему бы тогда не использовать его как постоянный ключ для симметричного шифра?


    1. whirl
      14.06.2017 13:53

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


      1. augur
        14.06.2017 15:43

        Так сделать его достаточно длинным, чтобы сделать подбор экономически нецелесообразным?
        Да, и, если не ошибаюсь, проще подобрать два ключа по 60 бит, чем один по 120.


      1. var_null
        14.06.2017 16:38

        да, я именно с этим расчетом SIGNATURE_KEY не симметричный ключ, хотя мог бы им быть


      1. var_null
        14.06.2017 17:25

        «можно будет расшифровать публичный ключ которым обмениваются сервера» — публичный ключ тоже генерируется для каждого экземпляра класса новый, тут все впорядке


  1. kireevmp
    14.06.2017 16:40

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


    1) Способ с дополнительной проверкой отправителя сделан правильно, но для полной защиты от ФБР от вмешательства (MITM) было бы правильно включить в сообщение хэш от всего. Не знаю как правльно, но я делал бы от SIGNATURE_KEY + симметричный ключ. Поправьте если что.


    2) Это уже больше не замечание, но если пишешь на PHP то это вроде как сервер. И тогда правильнее писать вторую сторону не на этом же PHP, а JS. В итоге получается клиент-серверная инфраструктура с шифрованием.


    1. var_null
      14.06.2017 16:48

      касательно связки PHP и JS — вы прямо в точку… я просто не хотел в тексте упоминать, чтобы это не было рекламой, но в комментарии, допускаю небольшой самопиар — у меня есть еще проект (чисто для себя и тех кому интересно) именно работающий на связке js и Php — чат мессенджер со сквозным шифрованием. Даже значок карандашика сквозным передается. Время разработки 2,5 недели (но это я плотно вчитывался в крипто литературу… так-то я далек от нее — сейчас, боюсь не смогу воспроизвести — нужно время на погружение), поэтому возможны легкие косяки… код открыт и тоже на гите. Проект — криптограм.впрограмме.рф


      1. D3fl4t3
        15.06.2017 09:58

        Чем не устроили torchat и другие готовые решения?


        1. var_null
          15.06.2017 09:59

          просто хотел написать лично для себя шифрованный мессенджер. давно хотел. вот и сделал)


  1. Sersoftin
    14.06.2017 16:40

    Фига тут велосипедисты) Мне кажется, что такую проблему решили уже огромное количество раз. Да и протокол Диффи Хельмана не просто так придумали.


    1. var_null
      14.06.2017 16:42

      это действительно велосипед. Просто люблю делать все сам. Хотелось немного разобраться как это все работает, ну и поделиться по возможности


  1. mbait
    14.06.2017 20:30
    +4

    толкен

    От слова talken что ли?


    Ваша криптография — ерунда. Видно, что вы не читали ни одной книги по теме, потому что в любой нормальной на первой странице жирным написано: "Не придумывайте свою криптографию, если вы не специалист". Толковые статьи начинаются со слов: "Существует открытая проблема в области криптографии. Ниже я предлагаю своё решение". Вы же пытаетесь реализовать решение уже давно решённой задачи, то есть новызны никакой нет. Хорошо, что вы попытались сами разобраться, но, к сожалению, вам этого не удалось. Можно было бы написать побробно об уязвимостях вашего алгоритма, но размер такой статьи превысил бы размер вашей. Рекомендую продолжить обучения с чтения классических трудов и прохождения онлайн-курсов.


    1. var_null
      15.06.2017 10:01

      token