Условимся, что машина, которая передает шифрованные данные — это всегда машина 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)
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-битным ключём.NeLexa
14.06.2017 13:04Сервер A и B лучше переименовать в Alice и Bob по всем канонам криптографии (https://ru.wikipedia.org/wiki/Алиса_и_Боб).
augur
14.06.2017 13:28Я правильно понимаю, что SIGNATURE_KEY попадает заранее на обе машины, притом по некоему безопасному каналу? А почему бы тогда не использовать его как постоянный ключ для симметричного шифра?
whirl
14.06.2017 13:53Потому что его относительно легко подобрать при перехвате достаточно большого количества сообщений и расшифровывать все подряд. В данном варианте у каждой сессии будет свой ключ для блочного шифрования. Подобрав SIGNATURE_KEY можно будет расшифровать публичный ключ которым обмениваются сервера, а потом еще ведь необходимо как то расшифровать содержимое. В общем задача по дешифровке усложнится во много много раз
augur
14.06.2017 15:43Так сделать его достаточно длинным, чтобы сделать подбор экономически нецелесообразным?
Да, и, если не ошибаюсь, проще подобрать два ключа по 60 бит, чем один по 120.
var_null
14.06.2017 16:38да, я именно с этим расчетом SIGNATURE_KEY не симметричный ключ, хотя мог бы им быть
var_null
14.06.2017 17:25«можно будет расшифровать публичный ключ которым обмениваются сервера» — публичный ключ тоже генерируется для каждого экземпляра класса новый, тут все впорядке
kireevmp
14.06.2017 16:40Алгоритм, безусловно, хороший, но есть два замечания-предложения:
1) Способ с дополнительной проверкой отправителя сделан правильно, но для полной защиты
от ФБРот вмешательства (MITM) было бы правильно включить в сообщение хэш от всего. Не знаю как правльно, но я делал бы от SIGNATURE_KEY + симметричный ключ. Поправьте если что.
2) Это уже больше не замечание, но если пишешь на PHP то это вроде как сервер. И тогда правильнее писать вторую сторону не на этом же PHP, а JS. В итоге получается клиент-серверная инфраструктура с шифрованием.
var_null
14.06.2017 16:48касательно связки PHP и JS — вы прямо в точку… я просто не хотел в тексте упоминать, чтобы это не было рекламой, но в комментарии, допускаю небольшой самопиар — у меня есть еще проект (чисто для себя и тех кому интересно) именно работающий на связке js и Php — чат мессенджер со сквозным шифрованием. Даже значок карандашика сквозным передается. Время разработки 2,5 недели (но это я плотно вчитывался в крипто литературу… так-то я далек от нее — сейчас, боюсь не смогу воспроизвести — нужно время на погружение), поэтому возможны легкие косяки… код открыт и тоже на гите. Проект — криптограм.впрограмме.рф
Sersoftin
14.06.2017 16:40Фига тут велосипедисты) Мне кажется, что такую проблему решили уже огромное количество раз. Да и протокол Диффи Хельмана не просто так придумали.
var_null
14.06.2017 16:42это действительно велосипед. Просто люблю делать все сам. Хотелось немного разобраться как это все работает, ну и поделиться по возможности
mbait
14.06.2017 20:30+4толкен
От слова talken что ли?
Ваша криптография — ерунда. Видно, что вы не читали ни одной книги по теме, потому что в любой нормальной на первой странице жирным написано: "Не придумывайте свою криптографию, если вы не специалист". Толковые статьи начинаются со слов: "Существует открытая проблема в области криптографии. Ниже я предлагаю своё решение". Вы же пытаетесь реализовать решение уже давно решённой задачи, то есть новызны никакой нет. Хорошо, что вы попытались сами разобраться, но, к сожалению, вам этого не удалось. Можно было бы написать побробно об уязвимостях вашего алгоритма, но размер такой статьи превысил бы размер вашей. Рекомендую продолжить обучения с чтения классических трудов и прохождения онлайн-курсов.
tmnhy
Но, зачем?! Ведь есть TLS.