Безопасный обмен ключами с libsodium
Я хочу сделать тестовое приложение, которое использует libsodium для связи от клиента к серверу.
Есть много портов для многих языков: C#, PHP,...
и всегда есть пример с "Боб" и "Алиса". Это нормально, но они никогда не показывают, как безопасно обмениваться открытыми ключами по сети.
Так как же рекомендовать способ обмена открытыми ключами для "Алиса / клиент" и "Боб / сервер".
Они всегда используют один и тот же файл или один и тот же компьютер для генерации пар ключей.
Вот выдержка из расширения libsodium-php:
$alice_kp = crypto_box_keypair();
$alice_secretkey = crypto_box_secretkey($alice_kp);
$alice_publickey = crypto_box_publickey($alice_kp);
$bob_kp = crypto_box_keypair();
$bob_secretkey = crypto_box_secretkey($bob_kp);
$bob_publickey = crypto_box_publickey($bob_kp);
$alice_to_bob_kp = crypto_box_keypair_from_secretkey_and_publickey
($alice_secretkey, $bob_publickey);
$bob_to_alice_kp = crypto_box_keypair_from_secretkey_and_publickey
($bob_secretkey, $alice_publickey);
$alice_to_bob_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);
$alice_to_bob_ciphertext = crypto_box('Hi, this is Alice',
$alice_to_bob_message_nonce,
$alice_to_bob_kp);
$alice_message_decrypted_by_bob = crypto_box_open($alice_to_bob_ciphertext,
$alice_to_bob_message_nonce,
$bob_to_alice_kp);
$bob_to_alice_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);
$bob_to_alice_ciphertext = crypto_box('Hi Alice! This is Bob',
$bob_to_alice_message_nonce,
$bob_to_alice_kp);
$bob_message_decrypted_by_alice = crypto_box_open($bob_to_alice_ciphertext,
$bob_to_alice_message_nonce,
$alice_to_bob_kp);
1 ответ
Libsodium не предоставляет прямых методов обмена ключами. Вы можете безопасно обменяться ключами несколькими способами:
Посредством защищенной от несанкционированного доступа "внеполосной связи" доставьте открытый ключ или, если ком-канал защищен от слушателей, общий секрет. (например, BittorrentSync, QR-код, содержащий общий секрет, который сканируется с устройства на устройство)
Попросите доверенную третью сторону подписать открытые ключи перед их отправкой в незашифрованном виде. (напр. Центры сертификации в SSL/TLS)
Закрепление сертификатов (например, Google Chrome для URL-адресов Google)
Если вы контролируете клиента
Поскольку вы, вероятно, не хотите повторно внедрять SSL / TLS, первый или третий вариант, вероятно, будет лучшим. Однако первое довольно сложно в формате клиент / сервер, поскольку сервер PHP, вероятно, имеет только Интернет для связи.
Однако, если вы контролируете клиента, вы можете сделать закрепление сертификата. То есть вставьте открытый ключ Боба в исполняемый файл Алисы. Алиса тогда только зашифрует сообщение с открытым ключом Боба.
Например, Алиса использует открытый ключ Боба и собственный закрытый ключ для шифрования некоторых данных и отправляет их (вместе с уникальным одноразовым номером и открытым ключом Алисы) Бобу.
Боб использует полученный им открытый ключ и собственный закрытый ключ для расшифровки и проверки зашифрованного пакета. Для Боба было бы безопасно использовать любые данные внутри, так как только он и Алиса могут расшифровать их.
Как насчет человека посередине?
Что ж, Ева могла бы блокировать сообщения Алисы и отправлять открытый ключ Евы вместо этого, но она работала бы вслепую, поскольку Алиса будет отправлять только сообщения, зашифрованные с помощью личного ключа Алисы и открытого ключа Боба.
В глазах Боба она будет просто еще одним клиентом. В глазах Алисы она расстроится, потому что Боб, похоже, не отвечает.
Если Ева отправляет сообщения Алисы, они всегда будут помечены как недействительные, поскольку Алиса использует закрытый ключ Алисы и открытый ключ Боба, чтобы попытаться расшифровать их, что не удастся, поскольку у Евы нет личного ключа Боба.
Если у Алисы нет проверенного исполняемого файла, Ева могла бы изменить его, загрузив (и вставив открытый ключ Евы вместо Боба). Проверка может быть осуществлена через ее менеджер пакетов, магазин приложений (большинство используют некоторые для подписи кода и SSL / TLS для загрузок) или подписанный исполняемый файл / архив с исходным кодом.
Примечание об утечке закрытых ключей см. В последнем разделе этого ответа.
Защита от взлома
Вы можете разместить QR-код на веб-сайте по протоколу HTTPS после того, как пользователь клиента каким-либо образом подтвердил свою подлинность (с помощью некоторого пользовательского пароля или, возможно, в будущем, используя SQRL). QR-код показывает, что клиент может сканировать или ссылку с пользовательский протокол путем регистрации себя в ОС в качестве обработчика для этого протокола URI.
Менее сложная альтернатива вышеуказанному методу QR-кода / uri будет состоять в том, чтобы просто скопировать и вставить общий секретный ключ с вышеупомянутого HTTPS-сайта, который будет использоваться для первоначального обмена открытым ключом с помощью метода crypto_secretbox() натрия.
Обратите внимание, что общий секретный / открытый ключ, вероятно, должен быть base64, base32 или шестнадцатеричным, закодированным во время передачи, так как некоторые байты в ключах могут быть искажены различными кодировками символов.
Что делать, если утечка секретного ключа сервера?
Если вам нужна прямая секретность в случае утечки частного ключа, то исходную пару открытого / закрытого ключа следует использовать только для обмена вторым циклом открытых ключей, которые никогда не сохраняются на постоянном носителе (по сути, без диска или базы данных).). Поскольку ваше дело - только тестовое приложение, Forward Secrecy может быть не очень важным.
Второй раунд ключей отбрасывается очень часто (как каждые 24 часа), уменьшая риски для прошлых данных стать уязвимыми из-за ошибок, таких как Heartbleed OpenSSL.
В традиционном использовании PHP веб-сервера это может быть трудно сделать, поскольку все переменные сбрасываются для каждого HTTP-запроса. Правильно настроенный экземпляр memcached может быть вариантом, но есть риск утечки временных закрытых ключей.