Что такое handshake (рукопожатие) в HTTPS?

Ответ

Handshake (рукопожатие) в HTTPS — это процесс установления безопасного TLS/SSL-соединения между клиентом (например, браузером) и сервером перед началом обмена зашифрованными данными. Его цель — аутентификация сервера, согласование криптографических алгоритмов и безопасная генерация общего секретного ключа для симметричного шифрования.

Основные этапы TLS handshake (упрощенно):

  1. Client Hello: Клиент отправляет серверу свою максимальную поддерживаемую версию TLS, список поддерживаемых шифр-наборов (cipher suites) и случайное число (client random).
  2. Server Hello: Сервер отвечает выбранной версией TLS, выбранным шифр-набором, своим случайным числом (server random) и своим SSL-сертификатом, содержащим его публичный ключ.
  3. Проверка сертификата: Клиент проверяет подлинность сертификата сервера (цепочка доверия, срок действия, имя домена).
  4. (Key Exchange): Клиент генерирует Pre-Master Secret, шифрует его публичным ключом сервера и отправляет. Либо используется Ephemeral Diffie-Hellman (DHE/ECDHE), когда обе стороны обмениваются параметрами для вычисления общего секрета без его передачи по сети.
  5. Генерация ключей: И клиент, и сервер независимо вычисляют одинаковый Master Secret из client random, server random и Pre-Master Secret, а затем на его основе генерируют ключи для симметричного шифрования и MAC (коды аутентификации сообщений).
  6. Finished: Обе стороны обмениваются зашифрованными сообщениями, подтверждающими, что handshake прошел успешно и ключи вычислены корректно.

Практический взгляд (C++ разработчика): При реализации сетевого клиента на C++ с использованием библиотек вроде OpenSSL или Boost.Asio + SSL, handshake — это вызов соответствующей функции установки соединения. После его успешного завершения можно писать/читать данные в/из SSL-сокета, и библиотека будет автоматически их шифровать/расшифровывать.

// Псевдокод с использованием Boost.Asio + SSL
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_stream(io_context, ssl_context);
boost::asio::connect(ssl_stream.next_layer(), endpoint);
// Выполнение TLS handshake
ssl_stream.handshake(boost::asio::ssl::stream_base::client);
// После этого соединение безопасно
boost::asio::write(ssl_stream, boost::asio::buffer("GET / HTTP/1.1rnHost: example.comrnrn"));

Ответ 18+ 🔞

А, слушай, про этот ваш TLS handshake, ну, ебать мои старые костыли, сейчас расскажу, как оно на самом деле. Представь, что твой браузер — это такой чувак, который приходит в закрытый клуб, а сервер — это швейцар с лицом кирпичом. И они не могут просто так начать шептаться про твои пароли. Им надо сначала по-тихому договориться, на каком языке они будут материться, и обменяться секретными бумажками. Это и есть рукопожатие, только цифровое и с криптографией, а не с поцелуями в щёку.

Короче, как оно идёт, по шагам:

  1. Клиент здоровается: Твой комп шлёт серверу записку: «Привет, я умею вот такие шифры, вот моя версия протокола и вот случайное число (client random), как билетик в театр». Это типа «ёпта, я свой».
  2. Сервер отвечает: Сервер такой: «Окей, чувак. Давай на вот этой версии и вот этом шифре договариваться. Держи моё случайное число (server random) и, главное, мой SSL-сертификат». В этом сертификате — его паспорт с публичным ключом. Это как будто он показывает удостоверение, но не просто бумажку, а такую, которую можно проверить по базе.
  3. Проверка паспорта: Твой браузер берёт этот сертификат и начинает его крутить-вертеть. «А не поддельный ли? Кто выдал? На имя этого сайта? Не просрочен ли?» Если что-то не так — сразу тревога, доверия ебать ноль, и соединение накрывается медным тазом. Если всё ок — идём дальше.
  4. Обмен секретиками (самое важное): Тут два пути. Либо клиент генерирует Pre-Master Secret, засовывает его в конверт, запечатывает публичным ключом сервера (который из сертификата) и шлёт. Только сервер своим личным ключом сможет его открыть. Либо они оба умные и используют Ephemeral Diffie-Hellman (DHE/ECDHE) — это когда они, не передавая сам секрет по сети, обмениваются какими-то числами и каждый у себя в уме вычисляет один и тот же общий ключ. Магия, блядь, математическая! Удивление пиздец, но работает.
  5. Генерация главного ключа: Теперь у обеих сторон есть три штуки: client random, server random и Pre-Master Secret. Они как в блендер их кидают и на выходе получают одинаковый Master Secret. А из него уже штампуют конкретные ключи для шифрования трафика и проверки целостности. Получается овердохуища безопасности из трёх ингредиентов.
  6. Финишная прямая: Они шлют друг другу зашифрованные уже этими новыми ключами сообщения «Готово». Если расшифровалось и сошлось — значит, ключи у обоих одинаковые, и можно начинать нормальную работу. Всё, хитрая жопа атакующего осталась не у дел.

А как это выглядит для нас, кодеров на C++? Да хуй с горы, всё уже придумано. Берёшь OpenSSL или Boost.Asio с SSL, настраиваешь контекст, подключаешься к сокету, а потом просто вызываешь handshake(). Вся эта ебушка-воробушка с сертификатами и шифрами происходит под капотом. После удачного рукопожатия пишешь в сокет что угодно — библиотека сама всё зашифрует перед отправкой. Читаешь — сама расшифрует. Красота.

// Псевдокод с использованием Boost.Asio + SSL
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_stream(io_context, ssl_context);
boost::asio::connect(ssl_stream.next_layer(), endpoint);
// Выполнение TLS handshake (вот тут вся магия и происходит)
ssl_stream.handshake(boost::asio::ssl::stream_base::client);
// После этого соединение безопасно, можно болтать
boost::asio::write(ssl_stream, boost::asio::buffer("GET / HTTP/1.1rnHost: example.comrnrn"));

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