Как устроено шифрование в HTTPS с точки зрения разработчика на C++?

«Как устроено шифрование в HTTPS с точки зрения разработчика на C++?» — вопрос из категории Сети, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Разработчик на C++, работающий с сетевыми приложениями, взаимодействует с HTTPS обычно через библиотеки, реализующие TLS/SSL (OpenSSL, LibreSSL, Boost.Beast + SSL). Механизм можно описать так:

1. Установление защищённого соединения (TLS Handshake):

  • ClientHello: Клиент (наше приложение) отправляет серверу список поддерживаемых шифр-наборов (cipher suites).
  • ServerHello: Сервер выбирает один шифр-набор и отправляет свой цифровой сертификат, содержащий его публичный ключ и подписанный доверенным центром сертификации (CA).
  • Проверка сертификата: Код на C++ с использованием OpenSSL проверяет цепочку доверия, срок действия и имя сервера в сертификате.
  • Обмен ключами: Клиент генерирует случайный сессионный ключ (симметричный, например, для AES), шифрует его публичным ключом сервера (используя RSA или ECDH) и отправляет.
  • Завершение рукопожатия: Сервер расшифровывает сессионный ключ своим приватным ключом. Обе стороны генерируют общие ключи для шифрования и аутентификации сообщений (HMAC).

2. Передача зашифрованных данных: Все дальнейшие HTTP-запросы и ответы шифруются с использованием быстрого симметричного шифрования (AES-GCM, ChaCha20-Poly1305) на том самом сессионном ключе.

Примерный код на C++ с использованием Boost.Beast и OpenSSL:

#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/asio/ssl.hpp>

namespace beast = boost::beast;
namespace ssl = boost::asio::ssl;
using tcp = boost::asio::ip::tcp;

// 1. Создание SSL-контекста и загрузка корневых сертификатов
ssl::context ctx(ssl::context::tls_client);
ctx.set_default_verify_paths(); // Загрузка системных CA

// 2. Создание SSL-потока
tcp::resolver resolver(ioc);
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);

// 3. Установка имени хоста для проверки сертификата (SNI)
SSL_set_tlsext_host_name(stream.native_handle(), "api.example.com");

// 4. Подключение и выполнение TLS handshake
auto const results = resolver.resolve("api.example.com", "443");
beast::get_lowest_layer(stream).connect(results);
stream.handshake(ssl::stream_base::client);

// 5. Отправка HTTPS-запроса
beast::http::request<beast::http::string_body> req{beast::http::verb::get, "/", 11};
req.set(beast::http::field::host, "api.example.com");
beast::http::write(stream, req);

Почему такая гибридная схема? Асимметричное шифрование (RSA) безопасно, но медленно. Симметричное (AES) — быстро, но требует безопасной передачи ключа. TLS объединяет оба подхода: асимметричное шифрование защищает обмен сессионным ключом, а на нём работает быстрое симметричное шифрование данных.