# TLS handshake _Сеть: L4 и выше · LinuxLab Knowledge Base_ **TL;DR:** TLS - слой шифрования поверх TCP. Перед передачей данных стороны делают handshake: обмениваются ключами, проверяют сертификат, выбирают cipher. ## Где живёт TLS TLS (Transport Layer Security, RFC 8446 для 1.3) - **слой между TCP и приложением**. То есть: ``` Application (HTTP, IMAP, SMTP, ...) │ TLS (encryption + authentication) │ TCP (reliability) │ IP / Ethernet ``` HTTPS = HTTP внутри TLS. Когда браузер открывает `https://...`: 1. DNS-резолв (см. [dns-resolution](/kb/dns-resolution.md)) 2. TCP [tcp-handshake](/kb/tcp-handshake.md) на порт 443 3. **TLS-handshake** (см. ниже) - это новые пакеты внутри TCP-соединения 4. После hadshake'а - обычный HTTP-обмен **зашифрованным** payload'ом ## TLS 1.3 handshake (упрощённо) TLS 1.3 - one-roundtrip версия handshake'а: ``` Client Server │ │ │ ── ClientHello ───────────────► │ │ • TLS-versions supported │ │ • cipher-suites supported │ │ • SNI: hostname │ │ • client key share (X25519) │ │ │ │ ◄── ServerHello │ │ • chosen cipher (TLS_AES_256...)│ │ • server key share │ │ ◄── Encrypted Extensions │ (after this everything encrypted) │ ◄── Certificate │ │ ◄── CertificateVerify │ │ ◄── Finished │ │ │ │ ── Finished ─────────────────► │ │ │ │ ◄═════════════ HTTP data ════════►│ ``` В TLS 1.2 было 3 roundtrip'а - медленнее. 1.3 - 1 roundtrip + 0-RTT для сессии-resumption. ## Что происходит в handshake 1. **Согласование версии и cipher'а** - какой алгоритм шифрования использовать (TLS_AES_256_GCM_SHA384, ChaCha20-Poly1305 и т.п.) 2. **Key exchange** - обмен ключевыми материалами через ECDH (X25519, secp256r1) 3. **Аутентификация сервера** - сервер показывает x509-сертификат, клиент проверяет цепочку до известного CA 4. **(Опционально) Аутентификация клиента** - mTLS, клиент тоже шлёт серт 5. **Derive keys** - обе стороны вычисляют общие ключи для шифрования данных 6. **Finished** - взаимное подтверждение что всё прошло без MITM ## SNI (Server Name Indication) Сертификаты сервера привязаны к доменному имени. Если на одном IP несколько сайтов (виртуальный хостинг), сервер не знает за какой домен отвечать пока HTTP-запрос не пришёл - но HTTP уже после handshake'а. Решение: **SNI** - клиент в **ClientHello открытым текстом** пишет имя сервера. Сервер выбирает правильный сертификат. Это значит что hostname виден сетевому наблюдателю даже в HTTPS - повод для появления Encrypted Client Hello (ECH) в современных версиях. ## Где увидеть в живую ```bash openssl s_client -connect example.com:443 -servername example.com < /dev/null ``` Покажет: согласованный cipher, цепочку сертификатов, verification result. В [[#cmd-tcpdump|tcpdump]] на порту 443 видны пакеты TLS-handshake - но payload зашифрован уже после ServerHello. ## Команды ```bash openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>&1 | head -30 ``` TLS-handshake вручную, видно цепочку сертификатов и согласованный cipher ```bash openssl x509 -in cert.pem -noout -subject -issuer -dates ``` Прочитать subject/issuer/validity сертификата из файла ```bash openssl s_client -connect host:443 -showcerts < /dev/null ``` Полная цепочка сертификатов в PEM-формате ```bash echo | openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates ``` Только expiry даты сертификата (для мониторинга) ```bash curl -vsI https://example.com 2>&1 | grep -E 'TLS|cipher' ``` TLS-данные через curl: версия, cipher, ALPN ## См. также - [TCP three-way handshake](/kb/tcp-handshake.md) - [HTTP/1.1, HTTP/2, HTTP/3](/kb/http-protocol.md) - [HTTP/2 internals - binary framing, HPACK, stream multiplexing](/kb/http2-internals.md) - [MQTT - publish/subscribe для IoT и mobile push](/kb/mqtt.md) - [WebSocket - bidirectional поверх HTTP](/kb/websocket.md) - [TLS-сертификаты - X.509, цепочка доверия, Let's Encrypt](/kb/tls-certificates.md)