Симметричное и асимметричное шифрование. Разбор алгоритма передачи шифрованных данных между серверами +5


Если имеется две машины и требуется переслать в одну или другую 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, сверяет с полученным, что дает гарантию, что данные не от ФСБ-шника Николая. Если все в порядке и проверка пройдена — с помощью приватного ключа извлекается симметричный ключ, которым уже расшифровывается сообщение.

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

> Код на гите (там же демо)
-->


К сожалению, не доступен сервер mySQL