Кратко

  • CertificateVerify подписывает transcript закрытым ключом сертификата; Finished аутентифицирует следующую границу ключом из handshake traffic secret отправителя.
  • Успешный Finished сервера не доказывает Finished клиента, принятие 0-RTT, upstream прокси, прикладные полномочия или устойчивый результат операции.

Успех объявили на одно сообщение раньше

Цепочка, имя сервиса и CertificateVerify были допустимы. Затем verify_data не совпал с transcript и ключом, вычисленными клиентом. RFC 9846 требует фатального завершения. Это не частично успешная сессия, а неудавшееся рукопожатие.

CertificateVerify подтверждает контроль закрытого ключа credential и подписывает разделённую по роли конструкцию до Certificate. Проверка цепочки и сервисной идентичности остаётся отдельной.

Finished не является второй подписью сертификата. TLS 1.3 выводит finished_key из handshake traffic secret отправителя и считает HMAC по transcript до CertificateVerify, если оно было. Так подтверждаются вычисленные ключи и история именно этого соединения.

При PSK сообщения Certificate и CertificateVerify могут отсутствовать, а Finished остаётся обязательным. Эти доказательства нельзя подменять друг другом.

Transcript не равен последовательности пакетов

Хеш охватывает сообщения рукопожатия по порядку вместе с их типом и длиной, но не заголовки record, alerts и прикладные данные. Фрагментация не меняет логическую историю.

После HelloRetryRequest первый ClientHello представляется синтетическим message_hash. Остальная часть рукопожатия TLS 1.3 зашифрована. Поэтому точная реконструкция требует диагностических секретов конечной точки или равноценного свидетельства реализации, а не простого склеивания видимых байтов.

Хеш набора шифров используется и в transcript, и в HKDF. Длина verify_data в TLS 1.3 равна длине результата хеша, а не фиксированным 12 октетам TLS 1.2.

У каждого направления свой момент окончания

Клиент и сервер имеют разные secrets, создают разные Finished и проверяют партнёра в разное время. После отправки Finished сервер может перейти на application traffic key и передавать данные до получения Finished клиента. Но личности и активности клиента он ещё не знает: ClientHello мог быть воспроизведён.

Клиент проверяет сервер, при запросе отправляет свою аутентификацию, затем Finished. Один tls_complete скрывает, кто что отправил и проверил. Нужны отдельные finished_sent, peer_finished_verified и проверенное состояние реализации для каждой стороны.

0-RTT — явное исключение: оно использует прежний PSK и допускает повтор. Предложение, принятие или отказ early data должны храниться отдельно от обычного завершения.

У прокси две истории

Соединения клиент–прокси и прокси–origin не разделяют transcript, secrets или Finished. Перенос downstream-метки в upstream выдумывает непрерывность. Прикладная корреляция связывает запросы, но каждое плечо обязано доказать своё рукопожатие.

OpenSSL разделяет SSL_is_init_finished() и SSL_get_verify_result(), относящийся к сертификату. Key log полезен в лаборатории, но раскрывает secrets. BoringSSL указывает, что Finished-accessors возвращают ноль в TLS 1.3; GnuTLS предлагает hooks и отдельную ошибку. Сходное имя API не даёт переносимого доказательства.

Решающая проверка сохраняет сертификат и CertificateVerify правильными, меняет один байт Finished и требует фатального отказа. Затем удерживает Finished клиента, выполняет PSK без сертификата, вызывает HelloRetryRequest и повторяет через разные плечи прокси. Только поведение работающего кода доказывает границу.