Ключи, шифры, сообщения: как работает TLS

Автор: Александр Венедюхин

Это версия документа от 09/11/2016, устаревшая.

Актуальная, дополненная версия описания TLS доступна по основному адресу.

*

Оглавление

Введение

SSL и TLS представляют собой развитие одной и той же технологии. Аббревиатура TLS появилась в качестве замены обозначения SSL после того, как протокол окончательно стал интернет-стандартом. Такая замена вызвана юридическими аспектами, так как спецификация SSL изначально принадлежала компании Netscape. Во многих случаях названия SSL и TLS продолжают использовать в качестве синонимов, хотя каноническим именем сейчас является TLS. При этом TLS имеет отдельную нумерацию версий и является более современным протоколом.

TLS/SSL изначально разработали для защиты коммерческих транзакций, проводимых через Интернет. То есть основной целью являлось получение относительно безопасного канала для осуществления покупок или управления банковским счётом - хотя, ни первое, ни второе ещё не пользовались какой-то популярностью у рядовых пользователей во времена становления SSL. Зато в современном Интернете на TLS/SSL полагаются не только в коммерческой деятельности, но и при решении гораздо более общей задачи сохранения "приватности" и конфиденциальности важной информации. Одним из самых распространённых применений TLS является HTTPS. HTTPS стремительно вытесняет незащищённую версию (HTTP): доля зашифрованного веб-трафика растёт, скорее всего, в скором будущем (несколько лет, 2016) практически весь веб-трафик будет зашифрован. В новой версии HTTP/2 защита информации средствами TLS будет использоваться по умолчанию. Благодаря такому положению дел, SSL/TLS - один из самых изученных, исследованных протоколов современного Интернета. Вместе с тем, история этого протокола показывает, насколько мало бывает практического толка от исследований: например, в 2010 году корпорация Microsoft исправила дефект в реализации SSL своего веб-сервера IIS - CVE-2010-3332; а соответствующая уязвимость в реализациях SSL была продемонстрирована в 2003 году. То есть, семь лет результаты исследования считались чистой теорией, не заслуживающей практического исправления. Впрочем, начиная, примерно, с 2014 года, интерес к поиску уязвимостей в TLS сильно возрос, а обнаруженные уязвимости стали привлекать внимание "самых широких слоёв" интернет-пользователей. Благодаря этому, процесс исправления дефектов приобрёл некоторую оперативность.

Впервые SSL внедрили в качестве проприетарной технологии, реализованной в браузере Netscape Navigator, одном из первых веб-браузеров. Версия 1 протокола не была опубликована, а так и осталась внутренней разработкой Netscape, развивавшейся в 1994-95 годах. SSLv2 - следующую, вторую версию протокола - опубликовали, однако спецификация так и не вышла из состояния черновика (draft). Более того, хоть в SSLv2 и скорректировали отдельные дефекты и уязвимости v1, протокол оказался ненадёжным, содержащим серьёзные архитектурные огрехи. SSLv2 давно (более 15 лет назад) и без оговорок признан небезопасным, поэтому сейчас не должен использоваться. Хотя на просторах веба всё ещё можно встретить архаичные серверы, которые поддерживают SSLv2, более того, многие современные криптографические библиотеки всё ещё поддерживают SSLv2 по историческим причинам. Несмотря на рост популярности TLS, вытеснение дефектных технологий идёт медленно. Однако если ваш сервер или клиентское ПО поддерживает SSLv2, можно смело сказать, что у вас просто нет безопасного канала, вы не поддерживаете TLS/SSL вовсе.

SSLv3 - это развитие SSLv2, но с весьма существенными доработками. SSLv3 представлен в 1996 году. Этот протокол получил полноценную спецификацию RFC, правда, значительно позже своего появления, и в статусе исторического документа: RFC 6101. На время SSLv3 пришлось становление TLS как интернет-технологии. Именно на базе SSLv3 и появился протокол TLS.

Сейчас существует три версии TLS, все они описаны в RFC: TLS 1.0, TLS 1.1 и TLS 1.2. Разрабатывается версия TLS 1.3, ожидается, что в ней появятся значительные улучшения. TLS 1.0 во многом повторяет SSLv3, за исключением ряда деталей, которые, впрочем, являются весьма важными. (В криптографических протоколах детали важны как ни в каких других протоколах Интернета.) Ключевым отличием TLS от SSL является наличие поддержки целого ряда расширений протокола, позволяющих реализовать современные методы защиты информации. TLS 1.1 и 1.2 достаточно близки к 1.0, но в версии 1.2 появились заметные изменения, например, используется другая основа псеводслучайной функции и появилась поддержка шифров в режиме аутентифицированного шифрования. Разрабатывается (август 2016) версия 1.3, которая существенно отличается от 1.2. В том числе, в TLS 1.3 изменён алгоритм установления соединения, удалены из спецификации или переименованы многие сообщения (элементы протокола). Однако TLS 1.3 пока находится в статусе Draft, спецификация активно меняется. Данный текст относится к современным утверждённым согласно процессу IETF версиям - 1.0, 1.1, 1.2. Там, где это требуется, по тексту даны пояснения, касающиеся версии 1.3, однако используемые названия полей и схемы сообщений - относятся к версиям до 1.2 (включительно). Для детального понимания работы протоколов TLS возможные нововведения 1.3 некритичны.

Рекомендованными являются TLS 1.1 и TLS 1.2. Повсеместно поддерживается TLS 1.0 - данный протокол совместим даже с древним браузером IE 6. TLS 1.1 поддерживается практически всеми сколь-нибудь распространёнными программными сервисами веба, а также других интернет-приложений. Поддержка TLS 1.2 в 2016 году также является повсеместной: все современные браузеры поддерживают эту версию, а проблемы могут возникнуть только с аномально старыми сборками и, иногда, на стороне сервера, при использовании древних приложений.

TLS 1.0 можно назвать версией 3.1 SSL. Собственно, именно так и сделано в спецификациях при описании способа наименования версий: SSL 3.1 - это TLS 1.0; 3.2 - TLS 1.1; 3.3 - TLS 1.2.

С 2015 года SSLv3, следом за публикацией очередных уязвимостей, перешёл в статус нерекомендуемых. Этот статус вполне официальный, каким только официальным он может быть в рамках технологических традиций Интернета: в июне 2015 года выпущен документ RFC 7568, требующий исключить SSLv3 из разряда поддерживаемых клиентами и серверами протоколов. Теперь остались только версии TLS.

Предназначение

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

Предполагается, что TLS работает поверх существующего между узлами "потокового" соединения (с которым обычно связан "сокет" - откуда название). Сейчас, в подавляющем большинстве случаев, TLS будет работать поверх TCP. Именно TCP отвечает за установление соединения, разбивку данных на пакеты, гарантированную доставку этих пакетов (при наличии соединения, естественно) и за разные другие телекоммуникационные моменты. TLS считает, что между узлами установлено надёжное соединение, поэтому, например, протокол не охватывает повторную отправку потерянных пакетов данных. (Для работы в условиях негарантированной доставки и неустойчивой связи существует особый, родственный протокол - DTLS, который мы здесь не рассматриваем.) TLS использует установленный канал связи для передачи сообщений. Эти сообщения кардинальным образом отличаются от пакетов данных (IP-пакетов в TCP/IP) и представляют собой структуру более высокого уровня. TCP позволяет надёжно обмениваться данными, но содержимое пакетов (за исключением специальных случаев) может быть легко прочитано кем-то, кто имеет доступ к каналу связи. Хуже того, этот кто-то может заменить пакеты или изменить передаваемые в них данные. Именно для защиты от этих угроз и используется TLS.

Другими словами, модель угроз TLS предполагает, что атакующий может как угодно вмешиваться в канал связи, в том числе активно подменять пакеты и вообще - прерывать связь. Ключевые задачи TLS: 1) обеспечить конфиденциальность, то есть реализовать защиту от утечек передаваемой информации; 2) обеспечить обнаружение подмены, то есть реализовать сохранение целостности передаваемой информации; 3) обеспечить аутентификацию узлов, то есть дать механизм проверки подлинности источника сообщений. TLS как-то справляется с этими задачами. Но не следует полагать, что TLS решает эти задачи полностью. Такое мнение является большой ошибкой, к сожалению, весьма распространённой. Доказательством того, что TLS не решает данные задачи полностью, являются уязвимости, обнаруженные в самом протоколе (а не только в его реализациях). Протокол и его реализации пытаются решить описанные задачи на максимально доступном уровне надёжности. Однако TLS в целом не обладает доказанной стойкостью, как не обладают ей и многие важнейшие составляющие части протокола. Обычно в качестве экстремального примера данного наблюдения приводят такую рекомендацию: не следует доверять TLS и связанным технологиям свою жизнь или жизнь других людей.

TLS использует концепцию клиент-сервер, которая, в логике данного протокола, во многом совпадает с логикой клиент-сервер TCP: например, и там и там соединение инициирует клиент.

Логика TLS

TLS работает с записями (records). Записи находятся в фундаменте протокола. Это нижний транспортный уровень TLS. Если рассматривать сеанс TLS на уровне условного сокета (TCP), то каждая передаваемая запись представляет собой блок, состоящий из короткого заголовка и, собственно, самих данных. Сообщения TLS, относящиеся к верхним уровням, могут быть разбиты на несколько записей. Это достаточно важный момент: в некоторых случаях сообщения требуют сборки из нескольких записей, что существенно влияет на логику обработки состояний протокола.

Обратите внимание, что в TLS используется сетевой порядок байтов (старший байт идёт первым, слева направо - "тупоконечное" представление). Разберём заголовок записи. Заголовок имеет длину 5 байтов, и следующий формат:

00	Тип (1 байт)
01	Версия протокола (2 байта)
03	Длина данных	(2 байта)

Тип - это тип записи. Определено четыре типа: 20 (0x14) - сообщение Change Cipher Spec (CCS); 21 (0x15) - сообщение Alert (это не обязательно предупреждение, есть вполне "фатальные alert-ы"); 22 (0x16) - сообщение Handshake (установление соединения); 23 (0x17) - Application Data (запись содержит данные приложения - то есть, полезную нагрузку). При передаче информации, основные преобразования с шифрами и кодами аутентификации как раз происходят в блоке данных записи с типом 23 (0x17) Application Data. Прочие записи обычно передаются в открытом виде, но могут быть и зашифрованы, в зависимости от типа записи и текущего состояния TLS-соединения;

Версия протокола - это версия, записанная в двух байтах, как указано выше: 03 00 - это SSLv3, 03 01 - TLS 1.0; 03 02 - TLS 1.1; 03 03 - TLS 1.2;

Длина данных - количество байтов, содержащих данные (сами байты следуют после заголовка). Так как используется сетевой порядок байтов, для определения длины нужно значение первого байта (h) умножить на 2^8 и прибавить значение второго (младшего, l) байта, вот так: h*256+l. Пустой блок данных (длина нуль) - допускается протоколом, но иногда вызывает сбои в клиентах, написанных с ошибками.

Пример заголовка записи (значения байтов в шестнадцатеричной записи):

16 03 02 03 FE

Раскодируем: это сообщение Handshake (0x16); версия 3.2 (0x03,0x02)- то есть TLS 1.1; длина данных - 0x03FE байтов (1022 байта).

Заголовок всегда передаётся в открытом виде.

За заголовком должно следовать определённое число байтов (>=0), которые представляют собой содержимое данной записи. Максимальное значение - 18432 байта. 18432 = 16384 + 2048. То есть, 16 килобайт + два раза по килобайту. Такое значение может показаться странным. Действительно, "базовая" версия записи, описанная в спецификации, предполагает блок данных в 2^14 байтов, то есть 16 килобайт. Но такой размер относится только к записям, содержащим открытый текст (TLSPlaintext). Например, к сообщениям, управляющим установлением соединения (Handshake). Здесь, действительно, максимальная длина - 16К.

Для защищённых записей ситуация меняется. Во-первых, вводится сжатие данных. В TLS все данные защищённой записи должны сжиматься, так предписано спецификацией. Но на практике это означает не совсем то, о чём можно подумать (для TLS это нередкая ситуация): на практике сжатие сейчас не используется. Дело в том, что TLS среди алгоритмов сжатия содержит алгоритм null, означающий, что никакого сжатия не производится. Этот алгоритм служит вариантом по умолчанию (некоторое время можно было встретить алгоритм DEFLATE, но сейчас он относится к нерекомендованным из-за обнаруженных уязвимостей). Тем не менее, реально сжатые данные могут, в ряде случаев, оказаться длиннее, из-за особенностей алгоритма сжатия. TLS отводит на такое увеличение длины 1024 байта (на практике хорошо реализованные алгоритмы используют значительно меньше).

Во-вторых, для проверки целостности зашифрованных данных к ним приписывается код аутентификации сообщения (MAC) и дополнительная информация (вроде векторов инициализации шифров) - на это отводится ещё 1024 байта. Итого - 2048, хотя данный момент и вносит некоторую сумятицу в организацию протокола. Естественно, универсальные и, потому, фиксированные 16K в качестве максимального предела смотрелись бы лучше. В реальности, редкая запись TLS дотягивает и до 16K - обычно длина заметно меньше.

Код аутентификации - важнейший элемент защиты информации в TLS. Обычно используется HMAC - версия с той или иной хеш-функцией. Типичный выбор: SHA-1 или SHA-256. Код аутентификации приписывается к блоку данных. Один из архитектурных дефектов всех используемых сейчас версий TLS состоит в том, что спецификация предписывает сперва вычислять MAC, а потом шифровать сообщение. То есть, вычисленный код аутентификации присоединяется к открытому тексту сообщения, а потом все вместе шифруется. При этом принимающая сторона должна сперва расшифровать полученные данные, а потом - проверить MAC. Хорошо известно, что такой метод легко приводит к возникновению "криптографических оракулов", на использовании которых основано несколько эффективных атак против TLS. Современный подход: код аутентификации добавляется после шифрования открытого текста, то есть MAC вычисляется для уже зашифрованного соообщения и прикрепляется к нему ("сперва шифр, потом - MAC"). Для внедрения современного подхода, в спецификацию добавлено специальное расширение, позволяющее клиенту и серверу договориться о том, в какой последовательности они генерируют коды аутентификации - RFC 7366. Более того, современные шифры в TLS используются в режиме аутентифицированного шифрования (а именно - AEAD). Этот режим не нуждается в MAC, так как целостность данных гарантирует сам алгоритм шифрования (подробное описание дано ниже, в разделе, посвящённом шифрам). Пример: AES в режиме GCM, этот вариант является предпочтительным, поддерживается всем современным ПО, и в 2016 году получил большое распространение.

Используемые криптосистемы в TLS объединяются в типовые шифронаборы (Cipher Suites). Чтобы начать обмен информацией по защищённому каналу, клиент и сервер должны согласовать используемый шифронабор. Очевидно, что параметры шифров и обмена сопутствующей информацией должны быть совместимы между собой. Согласование проводится на этапе установления соединения (Handshake). В шифронабор входят:

Существуют стандартные обозначения для шифронаборов. Например, выбор типового шифронабора TLS_RSA_WITH_AES_128_CBC_SHA означает, что будет использована криптосистема RSA для передачи сеансового ключа, AES со 128-битным ключом в режиме CBC в качестве симметричного шифра (то есть, защищаемые данные приложения будут зашифрованы симметричным шифром AES), SHA-1 в качестве хеш-функции HMAC. Шифронаборы строго фиксированы, состав закреплён в RFC, каждому приписан свой индекс, а реестр ведёт IANA.

Шифронаборы имеют важнейшее значение для безопасности TLS. Выбор нестойкой комбинации означает, что достаточной защиты конкретная реализация TLS не обеспечивает. Применительно к вебу: браузеры используют встроенный комплект шифронаборов, обычно это 10-15 вариантов; на сервере поддерживаемые шифронаборы настраиваются администратором. Реестр IANA в настоящий момент содержит свыше 300 (трёх сотен, да) шифронаборов (в число которых входят и так называемые псевдошифры, которые используются в качестве сигналов). Среди шифронаборов встречается экзотика вроде TLS_KRB5_WITH_RC4_128_SHA и безнадёжно устаревшие варианты, например - TLS_RSA_EXPORT_WITH_RC4_40_MD5. Современные серверы и клиенты не должны использовать подобного. Наличие того или иного шифронабора в реестре IANA означает, что этому шифронабору присвоен индекс (номер) и обозначение, не более того: нужно понимать, что данный реестр не имеет никакого отношения к тому, какие шифры и криптосистемы рекомендованы или не рекомендованы для использования. В 2016 году добротным вариантом считается TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: то есть, генерация общего секрета проводится по протоколу Диффи-Хеллмана на эллиптических кривых, для аутентификации данных на этапе установления соединения используется криптосистема электронной подписи ECDSA (тоже работающая на эллиптических кривых), полезная нагрузка шифруется AES со 128-битным ключом в режиме GCM, а в качестве хеш-функции служит SHA-256 (используется при генерации сеансовых клюей).

Установление соединения (Handshake)

(TLS Handshake иногда в современных русскоязычных статьях называют "рукопожатием", что является прямым, и не очень удачным, переводом английского термина; этот вариант мы не станем использовать.)

Клиент и сервер должны договориться об используемых шифрах и методах аутентификации, согласовать ключи и другие параметры сеанса связи. Набор согласованных параметров называется контекстом. Согласование происходит при установлении соединения, путём обмена специальными сообщениями - handshake-сообщениями. Такой обмен в TLS осуществляется поверх обмена записями. Каждое handshake-сообщение содержит специальный заголовок, состоящий из четырёх байтов. Первый байт обозначает код типа сообщения, три следующих байта - длину сообщения.

Сообщения Handshake

Handshake-сообщения отправляются в виде записей, которые мы обсудили выше. Это означает, что заголовку сообщения может предшествовать заголовок записи, где тип сообщения определён кодом 22 (0x16) - см. выше. Несколько handshake-сообщений могут быть переданы в одной записи - это распространённый случай, например, так нередко поступает Miscrosoft IIS. А самое интересное, что одно сообщение может быть разбито на несколько записей, передаваемых последовательно - спецификация допускает и это. К счастью, последний вариант скорее теоретический, на практике встречается исчезающе редко. (А экстремальный метод "запутывания" состоит в передаче перед разрезанным на несколько записей сообщением пустых записей, то есть TLS-записей, состоящих из одного заголовка, где в качестве длины указан нуль; такой вариант также допустим спецификацией.) Все эти случаи должны корректно поддерживаться добротной реализацией TLS. Таким образом, находящийся ниже протокола установления соединения уровень TLS-записей нужно рассматривать как некий потоковый псевдосокет, куда сообщения записываются как есть, и где они могут быть разрезаны на блоки для передачи. Единственное, что запрещает спецификация TLS - это нарушение порядка при доставке записей (в отличие от IPv4).

Спецификация предполагает, что любой клиент и сервер TLS по умолчанию уже согласовали шифронабор null, без MAC и с "нулевым" сжатием. То есть клиент и сервер могут обмениваться сообщениями в виде открытого текста без аутентификации. Установление соединения всегда начинает клиент. В типичной конфигурации, TLS здесь оказывается схож с TCP, так как, например, при работе с сервером по HTTPS - TCP-соединение также инициирует клиент. Благодаря историческому совпадению принципов построения технологий, клиент HTTPS, TLS, TCP - один и тот же узел. Можно предложить теоретические конфигурации протоколов, когда это не так, но в наиболее распространённой ситуации веба - подобные конфигурации едва ли возможны.

  Client                                               Server

  ClientHello                  -------->
                                                  ServerHello
                                                 Certificate*
                                           ServerKeyExchange*
                                          CertificateRequest*
                               <--------      ServerHelloDone
  Certificate*
  ClientKeyExchange
  CertificateVerify*
  [ChangeCipherSpec]
  Finished                     -------->
                                           [ChangeCipherSpec]
                               <--------             Finished
  Application Data             <------->     Application Data

Схема обмена Handshake-сообщениями
(ChangeCipherSpec - не является сообщением Handshake).
Источник: RFC 5246

Первым сообщением в протоколе установления TLS-соединения всегда является сообщение ClientHello (тип 01). Сообщение содержит следующие данные:

  1. версию протокола - максимальную версию, которую готов поддерживать клиент;
  2. 32 байта случайных значений - Client Random. Изначально, в спецификации рекомендовалось использовать первые 4 байта для передачи UNIX-таймстемпа, а оставшиеся 28 - заполнять результатом работы криптографического генератора псевдослучайных чисел. Однако сейчас многие браузеры и веб-серверы генерируют все 32-байта случайным образом. Забегая чуть вперёд: предполагалось, что наличие таймстемпа в handshake-сообщениях может помочь при обнаружении проблем с подменой времени на том или ином узле. Однако данный метод сейчас никак не используется, поэтому байты ClientRandom часто просто случайны;
  3. идентификатор TLS-сессии - SessionID: TLS позволяет возобновлять ранее установленные сессии, используя сокращённый вариант протокола установления соединения. Идентификатор сессии как раз содержит номер такой сессии, параметры которой (возможно) сохранены на сервере (подробнее мы рассмотрим этот вариант ниже);
  4. список шифронаборов, которые поддерживает клиент - Cipher Suites. Порядок шифронаборов в списке отражает их степень предпочтения клиентом (предпочтительные передаются первыми);
  5. список поддерживаемых методов сжатия - Compression Methods, порядок, опять же, соответствует степени предпочтения, но обычно в этом поле лишь одно значение - null, так как сжатие не рекомендуется использовать;
  6. данные нескольких расширений протокола.

Каждое из полей в сообщении ClientHello также имеет свой формат, а полю предшествуют данные о его длине и, в случае расширений, дополнительный заголовок. Заголовок обычно содержит тип расширения и длину его данных. Такая структура позволяет реализациям корректно разбирать сообщения. Например, список шифронаборов может быть представлен в таком виде (шестнадцатеричная запись):

[...] 00 06 c0 2b c0 2f с0 29

00 06 - шесть байтов занимают идентификаторы шифронаборов;

c0 2b - шифронабор 0xc02b - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;

с0 2f - шифронабор 0xc02f - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;

с0 29 - шифронабор 0xc029 - TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256.

Расширения ClientHello позволяют использовать различные нововведения, которые появились позже, чем структура данного сообщения. Так, например, в расширениях могут быть перечислены эллиптические кривые, которые готов использовать клиент. Самым распространённым расширением является SNI (Server Name Indication), позволяющее браузеру указать имя веб-сервера, с которым он пытается установить соединение. SNI необходимо для того, чтобы на одном IP-адресе могли корректно откликаться по HTTPS веб-серверы, размещённые под разными доменами.

Разберём ClientHello в деталях. В качестве примера возьмём специальный образец ClientHello, сгенерированный утилитой опроса TLS-серверов в исследовательских целях. В качестве сервера используется sbrf.ru.

Смещение	Байты 					Комментарий
(десятичное)	(шестнадцатеричное представление)

0000	16		; Первый байт заголовка TLS-записи - тип 22, сообщение Handshake.

0001	03 02 00 65	; Версия TLS 1.1 (03 02), длина пакета данных - 0065 (101 байт).

0005	01		; Первый байт сообщения TLS: 01 - тип ClientHello.

0006	00 00 61	; Длина блока данных - 000061 (97 байтов).

0009	03 02 		; Версия TLS 1.1 (03 02).

0011	53 53 4c 20 53  ; 32 байта случайных значений, ClientRandom

[...]

0043	00		; Длина поля SessionID - 0 байтов, так как SessionID в данном сообщении
			  не используется (в случае браузерного ClientHello будет указана длина
			  записи SessionID, за которой последует значение SessionID).

0044 	00 16		; Длина поля со списком шифронаборов (Cipher Suites) - 22 байта: в данном
			  ServerHello указано 11 шифронаборов, по два байта на каждый из них.

0046 	c0 2b		; Шифронаборы.

[...]

0068 	01 00		; Длина поля CompressionMethods (1 байт) и значение этого поля (00 - null,
			  без сжатия: клиент не поддерживает сжатие).

0070	00 22 		; Длина поля Extensions (0022 - 34 байта), поле, в нашем случае, содержит
			  расширения: SNI, Elliptic Curves, Elliptic Curves Point Formats.

0072	00 00 		; Расширение SNI (Server Name Indication, тип 0000). Данное расширение
			  позволяет указать имя сервера, с которым будет происходить соединение
			  по протоколу HTTPS (в данном случае - HTTPS, но SNI может использоваться и для
				других протоколов).

0073	00 0c 		; Длина данных в расширении SNI.

0075	00 0a 00 00 07	; Заголовок расширения SNI - длина списка имён (000a - 10 байтов), тип
			  имени (hostname - 00), длина строки с именем (7 байтов).

0080	73 62 72 66 2e 72 75	; Значение имени SNI: sbrf.ru

0086	00 0a 		; Тип расширения - Elliptic Curves (000a), данное расширение позволяет
			  клиенту указать список эллиптических кривых, с которыми этот клиент
			  умеет работать.

0088	00 08 		; Длина данных расширения (8 байтов).

0090	00 06 		; Длина списка кривых (6 байтов - три кривые, по два байта на
			  идентификатор).

0092	00 17 00 18 00 19	; Идентификаторы кривых.

0098	00 0b 00 02 01 00       ; Расширение Elliptic Curves Point Format, тип (000b), длина
				  (0002), длина списка (01), формат записи точек - полный
				  (то есть, полностью передаются две координаты x,y в аффинном
				  представлении; есть форматы со сжатием, где передаётся только
				  одна координата, х, а для второй указывается знак).

Согласно спецификации, после отправки ClientHello клиент ожидает ответа сервера. В ответ может прийти либо сообщение об ошибке, в виде Alert, либо сообщение ServerHello.

Alert - короткие сообщения, содержащие информацию об уровне ошибки и о её типе. Сообщение Alert, например, может сообщать о сбое установления соединения. Рассмотрим такой вариант подробнее:

Смещение	Байты 					Комментарий
(десятичное)	(шестнадцатеричное представление)

0000	15	; Тип сообщения - 0x15 - Alert.
0001	03 01	; Версия протокола - 0x0301 - TLS 1.0.
0003	00 02	; Длина сообщения (два байта).
0005	02	; Уровень ошибки - 0x02 - Fatal (фатальная, соединение невозможно).
0006	28	; Состав ошибки - 0x28 - Handshake Failure (сбой установления соединения).

Если же сервер смог успешно обработать ClientHello, то он отвечает сообщением ServerHello. Это сообщение так же имеет заголовок из четырёх байтов: тип сообщения (один байт) и длина (три байта). ServerHello содержит следующие поля:

1) версию протокола, которую будут использовать клиент и сервер;

2) 32 байта случайных значений - Server Random. С этой строкой ситуация такая же, как и с Client Random: первые четыре байта могут быть таймстемпом, а могут и не быть. Интересным свойством данной метки времени является то, что она может послужить некоторым справочным материалом при последующем анализе записанного трафика: если сервер отвечает достоверным значением времени, то, учитывая, что полный набор сообщений Handshake содержит сообщение, криптографически удостоверяющее целостность данных (см. ниже про сообщение Finished), получаем подписанную сервером квитанцию о времени соединения по его часам, с точностью до секунды - иногда эти данные помогают правильно составить запрос в адрес администрации сервера об извлечении записей из логов;

3) идентификатор сессии - SessionID, присвоенный новой сессии сервером;

4) выбранный сервером шифронабор - Cipher Suite, этот шифронабор будет использоваться в дальнейшем и клиентом, и сервером. Сервер выбирает шифронабор из предложенных клиентом в ClientHello;

5) выбранный сервером метод сжатия - скорее всего, это null;

6) некоторый набор расширений.

В современных реализациях TLS раздел "Расширения" (Extensions) имеет очень большое значение, так как в нём передаются параметры, определяющие схему работы и клиента, и сервера. Предназначение ServerHello - согласовать параметры соединения. TLS использует хорошо известную схему "запрос-ответ-подтверждение", а сообщения ClientHello и ServerHello запускают схему. Эти сообщения не содержат никакой секретной информации, соответственно - передаются в открытом виде. Именно поэтому данные сообщения, особенно - ClientHello, часто используются в системах DPI для обнаружения факта установления (или попытки установления) TLS-соединения.

В рамках каждой сессии TLS, клиент и сервер согласовывают следующие параметры (RFC 5246):

1) роли узлов: какой узел является клиентом, а какой - сервером. Распределение этих ролей происходит "естественным образом", при установлении соединения - клиент всегда начинает сессию;

2) алгоритм PRF (псевдослучайная функция) - используется для генерации сеансового ключа, на основе данных, переданных в ClientHello и ServerHello, поэтому клиент и сервер должны использовать согласованный алгоритм;

3) алгоритм шифрования - для успешного шифрования потока данных внутри сессии оба узла должны использовать один и тот же алгоритм, в согласованном режиме. Узлы согласуют тип шифра (потоковый, блочный), сам шифр, режим использования шифра, размер ключа, размер блока (для блочных шифров), инициализирующие векторы, а также дополнительные параметры (если они требуются, например, в случае с AEAD-режимами);

4) алгоритм аутентификационного кода сообщения (MAC) - аналогично шифрованию, в рамках сессии необходимо использовать согласованный алгоритм аутентификации. Напомню, что для AEAD-режима шифрования MAC не используется;

5) алгоритм сжатия - также должен быть общим, однако в современной реализации сжатие, фактически, не используется;

6) основной секрет (master secret) - массив из 48 секретных байтов, известный серверу и клиенту. Основное предназначение данного массива - генерация сеансового ключа, в тех шифронаборах, где применимо;

7) случайные данные клиента (ClientRandom) - 32 байта случайных значений, передаются в ClientHello;

8) случайные данные сервера (ServerRandom) - 32 байта случайных значений, передаются в ServerHello.

Эти параметры позволяют построить контекст для работы криптосистемы, которая будет обрабатывать защищённые TLS-записи на стороне сервера и клиента. Помимо ClientHello и ServerHello, установление соединения подразумевает обмен несколькими другими сообщениями.

За отправкой ServerHello, со стороны сервера следует ряд других сообщений, состав и содержание этих сообщений зависят от выбранного сервером режима работы.

Certificate. Ключевым аспектом для современных реализаций TLS является использование SSL-сертификатов, мы рассмотрим их подробнее ниже. Сертификаты передаются сервером в сообщении Certificate. Это сообщение присутствует практически всегда. Серверный сертификат содержит открытый ключ сервера. В теории, спецификация позволяет установить соединение без отправки серверных сертификатов. Это так называемый "анонимный" режим, однако он практически не используется, как и режимы TLS без шифрования. Так, примерно 100% веб-серверов, поддерживающих TLS, передают SSL-сертификаты. Для типичной конфигурации сообщение Certificate будет включать несколько SSL-сертификатов, среди которых один серверный сертификат и так называемые "промежуточные сертификаты", позволяющие клиенту выстроить цепочку валидации. Спецификация предписывает строгий порядок следования сертификатов в сообщении: первым должен идти серверный сертификат, а последующие - в порядке удостоверения предыдущего. Однако на практике серверы нередко возвращают сертификаты в произвольном порядке. На клиентской стороне браузеры также достаточно свободно относятся к порядку сертификатов, пытаясь выстроить из них корректную цепочку путём различных подстановок. Такое положение дел прямо нарушает спецификацию TLS версий 1.0, 1.1, 1.2, но такова реальность: несмотря на все строгости криптографии - реализации допускают вольности (последние нередко ведут к уязвимостям). Хотя, достаточно сложно найти веские причины для строго порядка следования сертификатов в TLS-сообщении. В новой версии протокола (TLS 1.3) данное требование предлагается смягчить, сделав допустимым произвольный порядок промежуточных сертификатов.

ServerKeyExchange. Это сообщение, содержащее серверную часть данных, необходимых для генерации общего сеансового ключа. Сообщение может отсутствовать. В зависимости от выбранного шифронабора, данных, содержащихся в SSL-сертификате, может быть недостаточно для выработки общего ключа. Недостающие данные как раз и передаются в ServerKeyExchange. Обычно это параметры протокола Диффи-Хеллмана (DH), однако исторический вариант предусматривает и передачу временного ключа RSA. Если используется классический вариант, то в сообщении ServerKeyExchange передаётся значение модуля и вычисленный сервером открытый ключ DH. В варианте на эллиптических кривых (ECDH) - идентификатор самой кривой и, аналогично DH, открытый ключ. Параметры подписываются сервером (за исключением экзотических вариантов обмена, вроде анонимного DH), клиент может проверить подпись, используя открытый ключ сервера из SSL-сертификата. В зависимости от используемой криптосистемы, подпись может быть DSA (сейчас практически не встречается), RSA или ECDSA. Подпись на параметрах DH очень важна, так как позволяет защитить соединение от атаки типа "Человек посередине". В разных версиях TLS подпись вычисляется для разных наборов данных. В TLS 1.0, 1.1 (при использовании RSA, что является основным вариантом) - от объединения значений двух хеш-функций - MD5 и SHA-1, взятых от параметров обмена DH. В TLS 1.3 - от значения хеш-функции, заданной в используемом шифронаборе, вычисленного для объединения ServerRandom, ClientRandom и параметров обмена DH.

CertificateRequest. В TLS возможна обоюдная (двухсторонняя) аутентификация узлов, использующая SSL-сертификаты. Сервер может запросить клиентский сертификат при помощи сообщения CertificateRequest. Сообщение содержит список поддерживаемых сервером типов сертификатов и типов криптосистем - здесь указываются криптосистемы, относящиеся к процедуре валидации клиентского сертификата: алгоритмы подписи, хеш-функции. Также в составе CertificateRequest могут быть переданы имена удостоверяющих центров, ключи которых сервер будет использовать для проверки клиентского сертификата.

ServerHelloDone - обозначает окончание пакета сообщений, возглавляемого ServerHello. Это сообщение имет нулевую длину (однако заголовок никто не отменял, поэтому в TLS-записи ServerHelloDone соответствует 4 байта) и служит простым флагом, обозначающим, что сервер передал свою часть начальных данных и теперь ожидает ответа от клиента.

Итак, сервер отвечает на ClientHello последовательностью сообщений TLS Handshake, максимум - пятью сообщениями. Типичный для современного состояния TLS случай: передача сервером четырёх сообщений - ServerHello, Certificate, ServerKeyExchange (с параметрами алгоритма Диффи-Хеллмана), ServerHelloDone. После передачи ServerHelloDone сервер ожидает ответа клиента. Клиент должен ответить своим набором сообщений:

Certificate - это сообщение содержит клиентский сертификат, если он был запрошен сервером. Если у клиента сертификата нет, а сервер его запрашивает, то клиент либо пропускает данное сообщение (SSLv3), либо отвечает пустым сообщением с типом Certificate (TLS). Клиентский сертификат требуется для двухстороннней аутентификации. Типичные сценарии использования: аутентификация в системах "телебанка", в платёжных системах, в корпоративных веб-шлюзах. Все современные браузеры, работающие на десктопе, поддерживают клиентские сертификаты. Клиентский сертификат, в ряде случаев, может заменить пару "логин/пароль" для аутентификации на том или ином онлайн-ресурсе.

ClientKeyExchange - клиентская часть обмена данными, позволяющими узлам получить общий сеансовый ключ. Содержание этого сообщения зависит от того, какой шифронабор выбран. Есть два основных типа - RSA и несколько вариантов протокола Диффи-Хеллмана. В случае использования RSA, клиент генерирует 48-байтовый случайный секрет (при этом первые два байта содержат версию используемого протокола), шифрует его открытым ключом сервера (этот ключ передаётся в составе серверного SSL-сертификата или в сообщении ServerKeyExchange) и передаёт зашифрованные данные на сервер. Сервер может расшифровать значение, используя соответствующий секретный ключ. Данная схема является исторической, так как обладает целым рядом недостатков. Например, если секретный серверный ключ станет известен третьей стороне, то она сможет расшифровать ранее записанный TLS-трафик. Тем не менее, обмен сеансовым ключом при помощи RSA продолжает использоваться в некоторых реализациях TLS. Современный метод - использование протокола Диффи-Хеллмана. В этом случае, ClientKeyExchange содержит открытый ключ DH. Этот ключ генерируется клиентом либо в соответствии с параметрами, переданными сервером в ServerKeyExchange, либо в соответствии с параметрами, указанными в серверном SSL-сертификате, если последний поддерживает DH. Случай с передачей параметров классического DH в составе сертификата - сейчас является экзотическим, таких сертификатов в "дикой природе" не встречается. Напомню, что серверные параметры DH (или ECDH) подписываются серверным ключом, клиент проверяет подпись, используя открытый ключ сервера.

CertificateVerify - если клиент передал в ответ на запрос сервера свой сертификат, то серверу требуется некоторый механизм, позволяющий проверить, что клиент действительно обладает секретным ключом, связанным с сертификатом. Для этого клиент подписывает массив переданных и принятых ранее сообщений Handshake. Такая подпись, если её значение удастся успешно проверить открытым ключом из сертификата, удостоверит факт наличия секретного ключа у клиента. Сообщение передаётся только если был передан клиентский сертификат в сообщении Certificate.

В современной конфигурации клиентские сертификаты используются редко. Поэтому в типичном случае клиент, на данном этапе, передаёт одно сообщение ClientKeyExchange. Это сообщение является, согласно спецификации, обязательным.

Следом за сообщением ClientKeyExchange, если оно было единственным, либо за CertificateVerify, клиент должен передать сообщение ChangeCipherSpec. Важный момент: это сообщение не является сообщением Handshake. Да, в спецификации TLS встречаются такие, не совсем прозрачные, моменты, когда вроде бы логичное течение протокола прерывается специальными "вставками". При разработке TLS 1.3 от этой сомнительной практики отказались. В частности, ChangeCipherSpec - предложено удалить.

ChangeCipherSpec - это специальное сообщение-сигнал, обозначающее, что с данного момента клиент переходит на выбранный шифр, а следующие TLS-записи будут зашифрованы. ChangeCipherSpec (CCS) имеет собственный тип и, соответственно, передаётся в отдельной TLS-записи. Таким образом, CCS имеет весьма важное значение в TLS (раньше версии 1.3), отделяя открытую часть сеанса связи от закрытой.

Со стороны клиента установление соединения звершается отправкой сообщения Finished.

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

Клиентское Finished содержит отпечаток - значение хеш-функции, - от всех предыдущих сообщений Handshake, отправленных, на данный момент, и клиентом, и сервером. Получив это сообщение в защищённой TLS-записи, сервер может вычислить хеш-сумму от известных ему предшествовавших сообщений и сравнить результат. Таким образом подтверждается подлинность сообщений и, соответственно, оказываются криптографически защищены выбранный шифронабор и другие параметры соединения. Впрочем, данная защита не лишена недостатков: например, так как сообщения ClientHello и ServerHello не содержат подписей, Finished никак не защищает сессию от атак, основанных на подмене параметров до отправки ChangeCipherSpec. Этот дефект протокола использован в нашумевшей в 2015 году атаке LogJam (об атаках на TLS/SSL мы подробно поговорим ниже).

В ответ на Finished со стороны клиента, сервер, в случае успешного установления соединения, отправляет свою пару ChangeCipherSpec и Finished. На стороне клиента полученное серверное сообщение Finished также используется для проверки, что сообщения Handshake не подверглись модификации активным атакующим, который перехватил канал. Состав Finished сервера отличается от клиентского варианта, так как включает в свой состав клиентское сообщение (и отличную от клиентской дополнительную строку).

Третьей стороне, активно перехватывающей канал связи, для того, чтобы успешно произвести подмену сообщений Handshake, потребуется вычислить сеансовый ключ и другие секретные параметры (если они использовались) для того, чтобы сфабриковать корректное сообщение Finished. Это вычислительно трудная задача, обычно неразрешимая на практике, если выбраны правильные настройки протокола и корректная реализация. Однако, в случае использования нестойких шифронаборов, атакующий может на лету вычислить секретный сеансовый ключ и - успешно подделать сообщение Finished.

В предлагаемом варинте TLS 1.3 порядок сообщений заметно меняется. Это касается и Finished. Серверное сообщение Finished отправляется сразу после сообщений, оносящихся к генерации общих ключей, то есть, раньше, чем клиент отправит своё Finished.

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

Посмотрим на ServerHello и последущие сообщения сервера в деталях, примером послужит ответ сервера на сообщение ClientHello, которое разбиралось выше. В этом примере несколько сообщений TLS передаются в одной TLS-записи.

Смещение	Байты					Комментарий
(десятичное)	(шестнадцатеричное представление)

0000	16 03 02		; Тип записи - Handshake (0x16) - версия TLS 1.1 (0x0302).

0003	07 02 			; Длина пакета данных (0x0702 = 1794 байта - в этой записи
				  содержатся все отправленные сервером сообщения -
				  ServerHello, Certificate, ServerKeyExchange,
				  ServerHelloDone - отсюда и такая длина).

ServerHello. Начало сообщения

0005	02 			; Тип сообщения: ServerHello (02).

0006	00 00 46 		; Длина данных: (0x000046 = 70 байтов - это данные
				  ServerHello).

0009	03 02   		; Версия протокола: 0x0302 - TLS 1.1.

0011	55 dc 	 		; 32 байта ServerRandom, в начале поля сервер указывает
				  timestamp.

[...]

0043	20 			; Длина SessionID - (0x20 = 32 байта).

0044	81 19 			; 32 байта, содержащие переданный сервером идентификатор
				  сессии (SessionID).

[...]

0076	c0 14 			; Выбранный сервером шифронабор - 0xc014 -
				  TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (протокол Диффи-Хеллмана
				  на эллиптических кривых для генерации сеансового ключа,
				  RSA - для подписи, AES с 256-битным ключом в режиме CBC
				  в качестве потокового шифра, SHA-1 для генерации MAC).

0078	00 			; Выбранный сервером метод сжатия - 00 - null.

Certificate. Начало сообщения

0079	0b  			; Сообщение Certificate (тип - 0x0b = 11).

0080	00 05 21 		; Длина сообщения (0x000521 = 1313 байтов).

0083	00 05 1e 		; Длина данных сертификатов (0x00051e = 1310 байтов).

0086	00 05 1b 		; Длина поля с сертификатом. Каждый из сертификатов, указанных
				  в сообщении Certificate, предваряется тремя байтами,
				  содержащими длину записи данного сертификата. Также передаётся
				  общая длина набора сертификатов - отсюда и эти "вложенные"
				  поля с числом байтов.

0089	30 82 05 17 30 		; Байты, составляющие сертификат (сам сертификат здесь не
				  приводится). Сертификат передаётся сервером в бинарном
				  представлении, с использованием определённого кодирования.
				  На практике это означает, что передаются сертификаты в
				  общепринятом формате DER, который поддерживается большинством
				  утилит, например из пакета OpenSSL и др.

[...]

ServerKeyExchange. Начало сообщения

1396	0c 00 01 8b 		; Сообщение ServerKeyExchange (тип - 0x0c = 12), длина данных
				  (0x00018b = 395 байтов). Так как выбран шифронабор с ECDHE,
				  то сообщение ServerKeyExchange содержит параметры именно для
				  алгоритма ECDHE. Каких-то специальных флагов, обозначающих
				  ServerKeyExchange как ECDHE, - не предусмотрено, всё
				  определяется шифронабором. Использование DH на эллиптических
				  кривых описано в RFC 4492 (https://tools.ietf.org/html/rfc4492).

1400	03 			; Тип кривой (0x03 - типовая именованная кривая).

1401	00 19 			; Идентификатор выбранной сервером кривой (0x0019 - secp521r1,
				  реестр кривых ведёт IANA, каждая из типовых кривых содержит
				  в своём определении необходимые для DH параметры, которые строго
				  зафиксированы: это генератор и разрядность группы точек).

1403	85 			; Длина поля публичного ключа, которое идёт следом
				  (0x85 = 133 байта).

1404	04 00 8f 96 86		; 133 байта, представляющие собой публичный ключ ECDHE
				  сервера. Публичный ключ - это точка на кривой, которую сервер вычислил,
				  используя параметры кривой. В соответствии с форматом записи
				  точек, указанным клиентом в расширении ClientHello, передаются
				  аффинные координаты точки (x,y) (запись предваряется заголовком).
				  Клиенту параметры кривой известны, потому что используется
				  типовая кривая.

[...]

1537 	01 00 			; Длина подписи, соответствующей параметрам DH
				  (0x0100 - 256 байтов). За значением открытого ключа DH следует
				  серверная подпись, выполненная по алгоритму и с ключом,
				  соответствующим указанным в серверном сертификате (в нашем
				  случае это RSA). В TLS 1.2 формат представления подписи несколько
				  другой: в составе ServerKeyExchange добавлены поля, обозначающие
				  используемый для генерации подписи на параметрах DH набор из
				  хеш-функции и криптосистемы электронной подписи (например, RSA+SHA-256).

1539 	25 23 f5 		; Значение подписи DH (полностью не приводится).

[...]

ServerHelloDone. Начало сообщения

1795	0e 00 00 00		; Сообщение ServerHelloDone (тип - 0x0e = 14). Это сообщение имеет
				  нулевую длину, поэтому следом за типом идут три байта с нулевыми
				  значениями (это обозначающие длину байты).

Сокращённый формат Handshake

Установление TLS-соединения - многоступенчатый процесс, достаточно сложный и требующий проведения заметного количества вычислительных операций, особенно жадными в плане процессорного времени оказываются операции проверки подписей и других действий с асимметричными криптосистемами. Установление соединения TLS - затратно, особенно если ваш онлайн-сервис обслуживает большое число клиентов. Для экономии ресурсов существует сокращённая версия Handshake.

В составе ServerHello сервер передаёт SessionID - идентификатор новой сессии. Этот идентификатор может быть использован клиентом позже, в составе ClientHello (см. выше). Предполагается, что на стороне сервера (и клиента) сохранены необходимые параметры, которые могут быть быстро восстановлены, возобновив тем самым сессию.

  Client                                                Server

  ClientHello                   -------->
                                                   ServerHello
                                            [ChangeCipherSpec]
                                <--------             Finished
  [ChangeCipherSpec]
  Finished                      -------->
  Application Data              <------->     Application Data

Установление соединения по сокращённой схеме
Источник: RFC 5246

В случае использования сокращённой схемы, сразу после получения ClientHello, содержащего валидный идентификатор сесcии SessionID, сервер отвечает сообщениями ServerHello, ChangeCipherSpec и Finished (этот вариант, кстати, напоминает предлагаемую в TLS 1.3 последовательность). После того как клиент пришлёт свои CCS и Finished, сессия возобновляется и узлы могут начать обмен данными в защищённом режиме.

Сокращённый сценарий содержит существенно меньше сообщений, позволяет не использовать вычислительно затратные операции проверки подписей и генерации общего секрета, кроме того, из-за меньшего количества сообщений заметно снижается задержка при установлении соединения (экономится время, требуемое на отправку пакетов от клиента к серверу и обратно). Современные браузеры широко используют сокращённый Handshake. Сессии могут сохраняться на серверах в течение нескольких минут и даже часов.

Сейчас используются и другие способы возобновления сессий. Например, RFC 5077 вводит понятие тикета сессии (TLS Session Ticket) - это расширение, в рамках которого сервер передаёт клиенту зашифрованное представление серверного контекста TLS. Клиент может восстановить сессию, передав в составе ClientHello сохранённый тикет.

После того как клиент и сервер установили соединение (по полной или сокращённой схеме), они обмениваются TLS-записями с типом Application Data, каждая такая запись содержит блок данных, зашифрованный симметричным ключом, а также код аутентификации сообщения. Код аутентификации защищает сообщение от изменения. Клиент и сервер самостоятельно ведут учёт полученных и переданных блоков.

TLS имеет ряд важных, и достаточно общих, криптографических особенностей: во-первых, этот протокол никак не скрывает факт установления соединения (как указано выше, системы DPI могут относительно уверенно детектировать начало сенса TLS при помощи простых правил-фильтров); во-вторых, в TLS возможны утечки мета-информации: число переданных блоков, различные предупреждения, передаваемые в открытом виде - всё это позволяет пассивному наблюдателю сделать некоторые выводы о характере передаваемой информации и, в случае веба, о типе совершаемых пользователем на веб-ресурсе действий. TLS лишь более или менее надёжно защищает от перехвата и подмены саму передаваемую информацию.

Повторное проведение Handshake

Во время работы в рамках TLS-сессии клиент и сервер могут столкнуться с необходимостью повторно провести Handshake. Такой случай предусмотрен протоколом. Хрестоматийная ситуация, когда это может потребоваться, выглядит следующим образом: после того как установлено TLS-соединение, клиент, использующий HTTP, запрашивает документ по некоторому URL, однако этот URL требует дополнительной авторизации с использованием клиентского сертификата. Клиентский сертификат может быть передан только в составе сообщения Handshake, поэтому сервер отправляет клиенту сообщение HelloRequest, требующее повторного обмена сообщениями Handshake (уже с клиентским сертификатом). Клиент может инициировать новую сессию в любой момент, передав сообщение ClientHello (в TLS 1.3 эту возможность планируется исключить: сервер должен будет закрывать соединение с фатальной ошибкой, получив ClientHello после установления сессии). Так как узлы уже согласовали TLS-соединение, обмен сообщениями повторного Handshake будет проводиться в защищённом виде. В примере с клиентским сертификатом это означает, что сертификат не будет виден прослушивающей канал стороне.

Предупреждения (Alert)

Спецификация TLS предусматривает обмен сообщениями с типом Alert. Это предупреждения и сообщения об ошибках. Например, в случае, если сервер не смог корректно разобрать сообщение ClientHello, он отвечает сообщением Alert, содержащим код ошибки Parse Error. Сообщения Alert иногда могут быть использованы в составе атак на TLS-узлы: так, при помощи отправки этих сообщений можно "подвешивать" сессию, когда перехватывающему устройству требуется время на, например, вычисление ключа.

После того, как узлы согласовали криптографический контекст и обменялись сообщениями ChangeCipherSpec - сообщения Alert передаются в зашифрованном виде. То есть, с этого момента содержание предупреждения не может быть прочитано прослушивающей трафик стороной, но сам тип записи - Alert - всё равно передаётся в открытом виде, что, соответственно, может приводить к утечке информации о состоянии узлов и соединения.

Сеансовые ключи

Асимметричные криптосистемы (с открытым ключом) не подходят для быстрой передачи потоков данных. Поэтому для шифрования данных внутри сессии TLS использует симметричные шифры. Обычно это блочные шифры, нередко используемые в режиме, сходном с потоковым (например, GCM). Есть несколько основных способов выработки общих данных для сеансового ключа. Сеансовый ключ, при этом, должен по понятным причинам оставаться секретным. О симметричных сеансовых ключах узлы договариваются в процессе установления соединения (Handshake). Основные методы генерации общего секрета рассмотрены выше, осталось обсудить некоторые подробности. Прежде всего: симметричных ключей используется пара - один для передаваемых сообщений, второй - для принимаемых. Серверному ключу для передаваемых сообщений (Write) соответствует клиентский ключ для принимаемых (Read), и наоборот.

Основа ключей - общий секрет Master Secret - генерируется из нескольких переменных составляющих: так называемый Premaster Secret, ClientRandom и ServerRandom. Premaster Secret - согласуется в рамках обмена ключами, это либо последовательность случайных байтов, зашифрованная открытым ключом сервера, либо значение, полученное в результате обмена по протоколу Диффи-Хеллмана. Master Secret - это массив из 48 байтов, получаемый в результате применения клиентом и сервером к этим составляющим псевдослучайной функции, определённой спецификацией. Псевдослучайная функция (PRF) TLS 1.2 построена на базе хеш-функции SHA-256 (либо может быть использована более "мощная" хеш-функция, указанная в составе шифронабора), предыдущие версии используют конструкцию на базе сочетания MD5 (которая давно не считается криптографически стойкой) и SHA-1 (которую перестали считать достаточно стойкой в 2015 году). При этом, способ использования MD5 при генерации сеансового ключа не приводит к возникновению уязвимостей, связанных с недостаточным качеством данной функции. То же самое можно сказать и про SHA-1. RFC 5246 определяет Master Secret для TLS 1.2 следующим образом:

master_secret = PRF(pre_master_secret, "master secret",
		    ClientHello.random + ServerHello.random)[0..47];

Master Secret - это ещё не сеансовый ключ. Дело в том, что разные шифры требуют разных ключей и дополнительных данных (например, инициализирующих векторов). Эти ключи и данные вычисляются на основе Master Secret, с использованием той же псевдослучайной функции. Делается это следующим способом (TLS 1.2):

  1. для выбранного шифронабора определяется количество байтов, необходимое для получения ключа и инициализирующей информации;
  2. нужное число байтов (Key Block) получается на оcнове Master Secret, ServerRandom, ClientRandom при помощи посдовательных вызовов всё той же псевдослучайной функции;
  3. полученный Key Block разбивается на подмножества, нужные для инициализации и работы шифра, а также для вычисления кода аутентификации сообщений (MAC). При этом могут различаться ключи и векторы инициализации сервера и клиента (но полный набор криптографических параметров известен обоим узлам).

Само шифрование осуществляется на уровне TLS-записей.

Большинство методов получения исходных данных сеансового ключа так или иначе оперируют публичным ключом сервера. На практике этот ключ использует почти 100% TLS-сессий. Публичный ключ передаётся сервером в сертификате. Помимо рассмотренных выше вариантов RSA и Диффи-Хеллмана, возможны экзотические схемы получения общего секрета:

1. PSK - pre-shared key. Схема основана на использовании общего секретного ключа и симметричной криптосистемы, при условии, что ключ был согласован заранее;

2. временный ключ RSA - исторический метод, предполагавший создание сеансового ключа RSA. Сейчас данный метод не используется, однако его поддержка послужила основой для атаки FREAK, опубликованной в 2014 году;

3. SRP - протокол SRP (Secure Remote Password), RFC 5054. Протокол, позволяющий сгенерировать общий симметричный секретный ключ достаточной стойкости на основе известного клиенту и серверу пароля, без раскрытия этого пароля через незащищённый канал;

4. Анонимный DH - анонимный вариант протокола Диффи-Хеллмана, в котором не используется подпись на серверных параметрах. Такая схема подвержена атаке типа "человек посередине", практически не встречается;

5. DH с сертификатом - вариант DH, в котором параметры (модуль, генератор и открытый ключ сервера) определены в серверном сертификате. Этот метод является историческим, требует специального сертификата и на практике не встречается. Основное его отличие от используемых сейчас вариантов (нередко называемых также "эфемерным Диффи-Хеллманом") состоит в том, что серверный открытый ключ протокола Диффи-Хеллмана оказывается зафиксирован: именно он входит в сертификат и подписывается удостоверяющим центром. Это означает, что схема не обладает прогрессивной секретностью. Сервер всякий раз использует один и тот же секретный ключ Диффи-Хеллмана, а раскрытие этого ключа позволяет расшифровать ранее записанный трафик TLS.

В разрабатываемой спецификации TLS 1.3 подход к генерации секретных ключей переработан. Так, ключи разделены на классы, соответствующие фазе протокола. Для защиты трафика используются ключи, получаемые после установления соединения. Также предлагается (и, скорее всего, войдёт в финальную версию спецификации) другой алгоритм генерации ключей на основе информации из контекста сессии. Изменения достаточно существенные, однако основная логика остаётся неизменной: сеансовые ключи генерируются из параметров, переданных в составе сообщений Handshake, общего секрета, имеющегося на момент генерации у узлов, и некоторых констант, которые определены протоколом.

Протокол Диффи-Хеллмана

Современный метод генерации общего сеансового секрета - это протокол Диффи-Хеллмана (DH). Использование DH, прежде всего, позволяет добиться так называемой прогрессивной секретности (Perfect Forward Secrecy, PFS), когда раскрытие серверного ключа асимметричной криптосистемы (RSA или ECDSA), не приводит автоматически к раскрытию ранее записанного трафика.

Протокол Диффи-Хеллмана - важнейший элемент современных реализаций TLS, также этот протокол имеет огромное значение для множества других защищённых протоколов Интернета, и для прикладной криптографии в целом. Протокол не является шифром или алгоритмом электронной подписи. На его основе можно построить и асимметричную криптосистему, и схему электронной подписи, но в TLS он используется с другой целью: этот протокол позволяет участникам обмена информацией договориться об общем секретном ключе через открытый канал связи. Задача Диффи-Хеллмана, лежащая в основе протокола, прямо связана с алгебраической задачей дискретного логарифмирования в конечной группе ― на вычислительной сложности этой задачи и построена защита протокола от перехвата секретного ключа.

Основная идея протокола может быть сформулирована достаточно просто. Две стороны, желающие получить общий секрет, очевидно, могут сгенерировать собственные, локальные, секреты, например, то или иное число. Предположим, что у нас есть некоторая однонаправленная функция - то есть, можно легко вычислить значение функции для заданного аргумента, но обратить вычисления и узнать аргумент по значению - сложно. Тогда можно было бы устроить алгоритм таким образом, чтобы договаривающиеся об общем секрете стороны передавали по открытому каналу друг другу значения функции от секретных аргументов, проводили над этими значениями и аргументами собственные вычисления и, в результате применения некоторых преобразований, могли бы каждая прийти к одному и тому же значению. Просматривающая коммуникации третья сторона же видела бы только значения однонаправленной функции, без возможности обратить её и узнать секрет. Успешная реализация такого протокола и есть схема Диффи-Хеллмана. Она может быть реализована на базе самых разных математических систем. Самое интересное, что именно эта идея используется и в самых современных криптосистемах, предлагаемых в качестве замены классическим в постквантовую эпоху. Отличие состоит только в математических объектах, лежащих в основе схемы: так, классический DH использует мультипликативную группу кольца вычетов, а сверхсовременные решения, например, разновидности схемы LWE, такой объект, как решётка.

Вернёмся к использованию DH в TLS. Сейчас (2016 год) в ходу два варианта протокола. В классическом случае, DH работает на “обычной” конечной группе, задаваемой простым числом P, которое называется модулем. Второй вариант - использует группу точек эллиптической кривой.

Рассмотрим подробнее первый вариант. Здесь группа, в которой будут проводиться операции вычисления общего секрета TLS, задаётся единственным числом – модулем. Это обязательно большое простое число. Большое - означает, что разрядность его записи превышает 1000 бит, а современная рекомендация для классического варианта - не менее 2048 бит. Сервер передаёт это число (значение модуля) в составе сообщения ServerKeyExchange, если используется классический алгоритм DH. Модуль полностью определяет группу, поэтому каких-то дополнительных параметров для группы передавать не нужно. На практике веб-серверы используют ту или иную типовую группу (заданную типовым модулем). Модуль не является секретным - он передаётся в открытом виде. Таким образом, известны группы, используемые большинством веб-серверов, поддерживающих DH. Вообще говоря, уникальные группы DH рекомендуется генерировать при начальной настройке TLS, это несложно сделать стандартными утилитами. Тем не менее, не так давно наиболее распространеная типовая группа имела разрядность в 1024-бита, что не слишком много. Вместе с задающим группу модулем, сервер передаёт другой открытый параметр - генератор (см. ниже), - а также свою часть обмена Диффи-Хеллмана - открытый ключ (принцип его вычисления также рассмотрен ниже).

Второй вариант протокола Диффи-Хеллмана - «эллиптический», он основан на эллиптических кривых. Математические операции протокола в обоих случаях эквивалентны, отличие кроется в используемых группах: «эллиптический» вариант работает на группе точек эллиптической кривой. Такая группа может быть построена при помощи заданной на кривой операции, называемой сложением точек. Следует понимать, что здесь важно не название, а свойства операции, с таким же успехом её можно было назвать умножением точек. Собственно, различают два эквивалентных подхода к обозначению групповых операций: мультипликативный (умножение) и аддитивный (сложение). Обычно, применительно к TLS (и прикладной криптографии), в случае классического протокола DH - используют мультипликативный вариант и говорят об умножении и возведении в степень (экспонента); в случае "эллиптического" - аддитивный, и говорят о сложении точек и нахождении кратного (умножение на скаляр). Это чисто терминологическое отличие, никак не связанное со стойкостью и другими особенностями протокола.

Итак, операция сложения точек ставит в соответствие двум точкам кривой - третью, называемую их суммой. На этой операции строится алгоритм удвоения точки и умножения на скаляр (целое положительное число, не являющееся, соответственно, точкой на кривой). Также вводится нейтральный элемент - в его роли выступает бесконечно удалённая точка. Эллиптическая кривая обладает непрерывностью, но в случае с протоколом Диффи-Хеллмана, она рассматривается только в "целых" точках. Прикладная особенность группы точек эллиптической кривой состоит в том, что решение задачи дискретного логарифмирования (обратной задачи для основы протокола DH) здесь сложнее, чем в "обычной" группе, это позволяет использовать ключи меньшей разрядности. Впрочем, для понимания математического принципа протокола Диффи-Хеллмана детали работы с эллиптическими кривыми не требуются.

Протокол DH на "обычной" группе работает следующим образом. Для того чтобы получить общий секретный ключ, стороны сначала выбирают общие параметры Диффи-Хеллмана ― это модуль, задающий группу (простое число), а также некоторый элемент этой группы, называемый генератором - соответственно: P и G. Параметры DH открыты и считаются известными третьей стороне. На следующем шаге каждая из сторон выбирает собственное секретное число a (и, соответственно, b) и вычисляет значение A = Ga mod P (соответственно, B = Gb mod P). То есть, все операции проводятся по модулю P, что, собственно, и отображает их результаты в элементы группы. Далее стороны обмениваются по открытому каналу значениями A, B и вычисляют Ab mod P и Ba mod P, соответственно. Полученные значения равны, так как, из свойства степеней, Ab = (Ga)b = Ba = (Gb)a = Gab. Таким образом, стороны получили общий секретный ключ Gab. Если вернуться к описанной выше основной идее протокола, то операция возведения в степень (по модулю) выступает в роли однонаправленной функции, значения которой передаются по открытому каналу. Свойство групповой операции (в данном случае, эквивалентное более привычному свойству степеней), позволяет сторонам, осуществляющим обмен значениями, прийти к одинаковому результату (Gab). В случае TLS, сервер передаёт в сторону клиента параметры Диффи-Хеллмана и свой открытый ключ (A), удостоверяя эти значения собственной электронной подписью (либо RSA, либо ECDSA). Подпись вычисляется от ключа, открытая часть которого указана в сертификате сервера (см. выше). Прослушивающая канал сторона знает значения P, G, A и B. Но для того, чтобы определить значение секретного ключа, необходимо вычислить a или b, решив относительно x уравнение вида A = Gx mod P. Стоящая за решением этого уравнения задача и называется задачей дискретного логарифмирования в конечной группе. Эта задача вычислительно сложна для групп большой разрядности (1024 бита и выше), поэтому возведение в степень оказывается однонаправленным. (Фундаментальная причина сложности данной операции сходна с общими проблемами деления в группах: грубо говоря, деление представляет собой операцию поиска среди элементов группы такого, который при умножении на известный делитель давал бы делимое. Ситуация эквивалентна привычному делению из курса начальной школы: чтобы разделить 15 на 3 нужно найти такое число, которое при умножении на 3 давало бы 15. Если известны некоторые свойства группы, позволяющие построить её арифметическую структуру, то операцию деления можно оптимизировать, используя тот или иной "быстрый" пошаговый алгоритм, отбрасывающий из перебора заведомо неподходящие элементы.)

В случае с эллиптической кривой - вместо модуля P задаётся сама кривая; вместо возведения в степень используется умножение на скаляр (то есть, вычисляется кратное для точки: если мы умножаем точку A на значение 3, это означает, что на кривой вычисляется точка 3A = A+A+A). В TLS используются именованные кривые, параметры которых заранее известны сторонам. Генератором, в эллиптическом случае, является точка кривой. Однако логика протокола от этих особенностей не зависит. Операции DH на эллиптической кривой просто будут записаны в аддитивной форме: Ab = (Ga)b = Ba = (Gb)a = Gab.

Итак, практическая полезность DH строится на сложности задачи дискретного логарифмирования в конечной группе. Известно, что при наличии больших, но вполне реальных, вычислительных мощностей, для 1024-битной "классической" (не "эллиптической") группы можно уже сейчас предвычислить её арифметические структуры, потратив пару лет работы суперкомпьютера и сохранив результаты в специальных таблицах. После этого вычислять дискретный логарифм в этой группе можно достаточно быстро (за часы, а возможно, просто на лету), особенно, если вы используете специальную многопроцессорную систему. Это означает, что можно расшифровать записанный ранее трафик TLS-сессий (а также других протоколов, использующих DH). Дело в том, что сеансовый ключ, если вы умеете отыскивать дискретный логарифм, элементарно вычисляется из ключа DH, который передаётся в открытом виде. Предвычислить нужную структуру можно только для известной группы, поэтому для атакующего важно, чтобы TLS-серверы использовали типовые параметры. При этом, для тех, у кого ресурсов мало (кто не является специализированным агентством, например), группа остаётся вполне стойкой.

Разновидность DH на группе точек эллиптической кривой называется ECDH. Это рекомендуемый сейчас вариант алгоритма, он получает всё большее распространение в современных реализациях TLS. Так как отыскание дискретного логарифма в группе точек эллиптической кривой вычислительно сложнее, можно использовать более короткие ключи. Высокую секретность, согласно современным представлениям, обеспечивает группа эллиптической кривой с разрядностью 192 и более бит. Также алгоритм позволяет использовать общую кривую, а не генерировать новую для каждого сервера. По крайней мере, современная ситуация такова, что рекомендовано использовать хорошо известные кривые из утверждённого списка (хотя никто не мешает договориться о собственной кривой, в TLS это предусмотрено - параметры будут передаваться в сообщениях Handshake). Распространённый случай – кривая secp256r1, предлагающая разрядность 256 бит. Если группу классического DH в TLS легко поменять – модуль и генератор передаются в сообщении сервера и могут быть любыми, – то для типовых эллиптических кривых всё сильно сложнее: параметры здесь фиксированы заранее, клиент и сервер могут договориться только о самой кривой, выбрав её из ограниченного списка.

Параметры DH всегда задаёт сервер. Эти параметры, как сказано выше, подписываются серверным ключом, что необходимо для того, чтобы параметры не могли быть заменены в момент передачи. Хотя сообщение Finished защищает весь обмен Handshake, активный атакующий мог бы подменить параметры DH на свои собственные, перехватив соединение и, в дальнейшем, сгенерировав для клиента и сервера разные, но корректные сообщения Finished - протокол DH сам по себе не защищён от атаки типа человек посередине (схема, сходная с только что описанной, применяется в атаке Logjam). Поэтому подпись на данных параметрах крайне важна. В зависимости от типа сертификата, в качестве криптосистемы подписи может использоваться либо ECDSA (современный вариант), либо RSA, а устаревший алгоритм DSA сейчас не встречается.

Широко используются аббревиатуры ECDHE и DHE, то есть, варианты записи, оканчивающиеся буквой E. Эта буква обозначает то, что протокол DH используется для выработки временного сеансового ключа, от английского ephemeral ("эфемерный"). То есть, E - подчёркивает то, что ключ не является постоянным (однако не следует использовать для обозначения этого свойства прямой русский перевод - "эфемерный": в русскоязычном варианте ключ называется "временным" или "сеансовым"). Почему потребовалось отдельно выделять "временные ключи"? Причина в том, что в TLS определены варианты соединений, использующих постоянный серверный открытый ключ DH, определённый в сертификате. Однако сейчас такие схемы на практике не встречаются, поэтому ключ, полученный в результате обмена сообщениями DH, всегда будет временным. Это, однако, не означает, что этот ключ каким-либо образом автоматически исчезает после закрытия соединения: во-первых, ключ может не удаляться из памяти сервера или направляться в долговременное хранилище, по тем или иным причинам; во-вторых, ключ можно восстановить из записи трафика, при условии, что третьей стороне известен (или она может вычислить) соответствующий секретный параметр Диффи-Хеллмана.

Пример серверного сообщения ServerKeyExchange, содержащего параметры DH (в "классическом" варианте) и подпись на этих параметрах. (Вариант на эллиптической кривой рассматривался в анализе дампа трафика выше.)

Смещение	Байты				Комментарий
(десятичное)	(шестнадцатеричное представление)
0000   	16 03 01		; Handshake, TLS 1.0.
0003	03 0d 			; Длина сообщения: 0x030d = 781 байт.
0005	0c 			; Сообщение ServerKeyExchange.
0006	00 03 09 		; Длина данных: 0x000309 = 777 байт.

0009	01 00			; Длина значения модуля P (из параметров DH): 0x0100 = 256 байт
				  (2048 бит).
0011	ff ff ff...		; Блок со значением модуля P (полностью не приводится).
[...]

0257	00 01 			; Длина блока со значением генератора G: 1 байт.
0259	02 			; Значение генератора: 2.
0260	01 00			; Длина блока серверной части обмена DH: 0x0100 = 256 байт.
0261	5c fb 83... 		; Блок со значением серверной части DH (полностью не приводится).
[...]

0517	01 00 			; Длина блока подписи (RSA) на параметрах DH - 256 байт,
				  соответствует серверному ключу из сертификата в 2048 бит.
0519	c4 bc 5d...		; Начало блока, содержащего значение подписи.

Асимметричные криптосистемы, электронная подпись

Для организации защищённого соединения в TLS также используются асимметричные криптосистемы, криптосистемы электронной подписи. На практике, сейчас это RSA и ECDSA. Как описано выше, RSA может применяться в исторической схеме для передачи сеансового секрета клиентом на сервер (такая схема всё ещё нередко встречается). ECDSA - служит только для получения подписи, то есть, аутентификации.

Криптосистема RSA

RSA - самая массовая криптосистема с открытым ключом (асимметричная криптосистема). Повсеместно используется для подписи в составе SSL-сертификатов. Долгое время RSA являлась, фактически, единственной криптосистемой SSL-сертификатов. Лишь недавно (в 2014 году) наряду с RSA стала заметна доля ECDSA в сертификатах. Логика построения RSA напоминает протокол Диффи-Хеллмана, однако математические объекты используются другие, также отличаются свойства однонаправленной функции. В RSA однонаправленная функция имеет "лазейку" или "чёрный ход", знание которого позволяет легко обратить функцию, вычислить аргумент по значению. RSA основана на возведении в степень по модулю, а стойкость этой криптосистемы связана с задачей разложения числа на простые множители.

Параметрами криптосистемы RSA являются модуль (это всегда составное число) и открытая (шифрующая) экспонента - то есть, показатель степени, в которую возводится сообщение. Модуль в RSA является составным числом: N = pq, где p и q - достаточно большие простые числа. (Строго говоря, успехи криптоанализа RSA накладывают на p и q немало дополнительных требований, которые мы не рассматриваем, так как для понимания прицнипа работы криптосистемы это не важно.) Разложение N держится в секрете. Знание этого разложения позволяет построить "лазейку", с помощью которой можно обратить однонаправленную функцию, лежащую в основе RSA.

Помимо модуля, для использования RSA с целью генерирования электронной подписи и расшифровки сообщений, необходимо сгенерировать секретный ключ. Секретным ключом является экспонента, обратная к шифрующей (расшифровывающая). В случае TLS, сервер публикует модуль N и шифрующую экспоненту e, а в секрете сохраняется соответствующая e расшифровывающая экспонента d = e-1 (также обычно в состав секретного ключа входит разложение модуля - p и q, необходимое не только для вычисления d, но и для ускорения вычислительных операций). Таким образом, открытый ключ состоит из N и e, а закрытый - из (N,p,q,d). Открытый ключ обычно публикуется в составе сертификата TLS (сертификаты подробнее рассмотрены в специальном разделе). Открытый ключ служит для проверки подписей, а для их генерации необходимо знать соответствующий секретный.

RSA позволяет зашифровать сообщение, выступая в роли асимметричного шифра. Учебный пример зашифрования состоит в возведении сообщения, представленного в виде числа, в шифрующую степень и вычислении остатка по модулю N (заданному в составе ключа). C = me mod N, где C - шифротекст, а m - открытый текст. Соответственно, зная расшифровывающую экспоненту, можно расшифровать C: m = Cd mod N. Это соотношение можно проиллюстрировать следующим нестрогим рассуждением, используя тот факт, что e = d-1, поэтому: Cd = (me)d = me*d = me*e-1 = m1 = m (mod N).

Из-за возможности проведения эффективной атаки, RSA нельзя использовать подобным прямым образом, непосредственно шифруя сообщение. Допустимым вариантом является, например, шифрование RSA случайного секрета, дополненного специальным образом до разрядности модуля, с последующим вычислением ключа симметричного шифра при помощи хеш-функции от переданного секрета - именно эта схема используется в TLS.

Генерация ключей RSA начинается с генерации пары простых чисел, которые составят модуль. Знание p и q позволяет легко определить обратный элемент к шифрующей экспоненте. Однако задача разложения числа на простые множители (факторизации) является вычислительно сложной. Фактически, для достаточно больших чисел (1024 бит и более), сейчас не известно алгоритмов, позволяющих находить разложение произвольных чисел за разумное время с использованием любых технологически мыслимых вычислительных мощностей. Тем не менее, существует, - пока сугубо теоретическая, но отлично обоснованная, - возможность быстрой факторизации на квантовом компьютере достаточной разрядности (под вопросом находится сама возможность создания подходящего компьютера). "Квантовая атака" касается не только RSA, но также и обоих описанных вариантов DH, и криптосистемы электронной подписи ECDSA.

При использовании для создания и проверки электронной подписи в TLS, RSA работает следующим образом. Сторона, генерирующая подпись, вычисляет значение хеш-функции от подписываемого сообщения (H = Hash(M)), приводит это значение к разрядности используемого модуля N и вычисляет значение подписи при помощи секретной (расшифровывающей) экспоненты: S = Hd mod N. Для проверки подписи требуется знать открытую часть ключа: модуль и шифрующую экспоненту e. Значение подписи возводится в степень e, а получившееся значение сравнивается с вычисленным значением хеш-функции сообщения: Se mod N = H, если значения совпали, то подпись верна. Для вычисления корректной подписи (подделки) третьей стороне необходимо знать секретную экспоненту d, что обеспечивает защиту от подделки.

Этот механизм электронной подписи используется для удостоверения параметров протокола Диффи-Хеллмана, переданных сервером в сообщении ServerKeyExchange. Подпись, передаваемая в составе сообщения TLS, является целым числом, имеющим разрядность, соответствующую разрядности модуля ключа. Модуль ключа содержится в сертификате сервера. Так, если модуль имеет разрядность 2048 бит, то для записи подписи потребуется 256 байтов (256*8 = 2048). Подпись, как отмечено выше, вычисляется от значения хеш-функции для объединения параметров (в них входит и серверная часть обмена DH), кроме того, в TLS 1.2 к параметрам добавляются значения ServerRandom и ClientRandom. В процессе вычисления значения подписи параметры преобразуются к требуемому формату.

В TLS, RSA может использоваться и для прямой передачи сеансового секрета на сервер, то есть, в качестве асимметричного шифра. Такой метод считается историческим, и не должен применяться на практике, что не мешает встречать его "в дикой природе". RSA здесь применяется самым хрестоматийным образом: открытый ключ извлекается из переданного сервером сертификата, сгенерированный секрет преобразуется к заданному формату и результат возводится в степень, заданную шифрующей экспонентой. Получившееся зашифрованное сообщение передаётся в сообщении ClientKeyExchange (как описано выше). Сервер, которому известно значение секретной экспоненты, расшифровывает полученное сообщение. Если в ход установления соединения вмешалась третья сторона, подменившая сервер, то этой стороне не удастся корректно расшифровать переданный клиентом секрет, соответственно, стороны не смогут прийти к общему набору сеансовых ключей.

Другое историческое применение RSA, как асимметричного шифра, в TLS возможно при выборе сервером варианта протокола установления соединения, использующего временные ключи RSA. В таком случае сервер передаёт модуль и значение открытой экспоненты в составе сообщения ServerKeyExchange. Клиент использует полученный открытый ключ RSA для того, чтобы зашифровать случайные байты ClientKeyExchange, которые послужат для генерации симметричных ключей. Эта экзотическая схема была актуальна для "экспортных" вариантов протокола, когда от реализации требовалось, чтобы для защиты сессии не использовались ключи RSA длиннее 512 бит: сервер использовал "длинный" ключ из сертификата для подписывания "короткого" временного ключа из ServerKeyExchange. Сейчас RSA в TLS не следует использовать в качестве асимметричного шифра, это небезопасно (тем более, с коротким ключом).

Таким образом, операции в криптосистеме RSA сводятся к возведению в степень, осуществляемому в кольце (по модулю N). Если атакующая криптосистему сторона умеет быстро извлекать в данном кольце корни произвольной степени из произвольных значений, либо быстро находить обратный элемент для данного, то RSA теряет стойкость. С этими направлениями атак связано большое число теоретико-числовых оптимизаций, успешно используемых в криптоанализе RSA.

Важно учитывать, что математическая эквивалентность операций получения электронной подписи и расшифрования в RSA является особенностью данной криптосистемы, не более того. Операции подписывания и шифрования/расшифрования - это разные операции, их совпадение в RSA не означает, что другие криптосистемы электронной подписи также могут прямо служить шифрами (хотя построить шифр на базе такой криптосистемы обычно можно).

Рассмотрим фрагмент трафика установления TLS-соединения, в котором узлы договорились использовать шифронабор с передачей сеансового секрета с помощью шифрования RSA (а именно: TLS_RSA_WITH_AES_128_CBC_SHA). Сертификат, представленный сервером, содержит открытый ключ длиной 2048 бит. Этот ключ используется клиентом для шифрования. О том, что клиент прислал именно зашифрованный RSA секрет - сервер знает потому, что RSA указана в шифронаборе, соответственно, рассматриваемое сообщение ClientKeyExchange не содержит никакого указания на то, что используется именно RSA, с тем или иным ключом.

Смещение	Байты				Комментарий
(десятичное)	(шестнадцатеричное представление)
0000	16 03 03		; Handshake, TLS 1.2.
0003	01 06 			; Длина: 0x0106 = 262 байта.
0005	10 			; Сообщение ClientKeyExchange (0x10).
0006	00 01 02 		; Длина: 0x000102 = 258 байт.

0009	01 00 			; Длина блока PreMasterSecret: 0x0100 = 256 байт. Длина
				  соответствует разрядности ключа из сертификата - 2048 бит.

0011	54 ...			; Далее идёт зашифрованный блок из 256 байтов.
				  Из них 48 байтов (46+2) - это PreMasterSecret, остальные байты
				  содержат дополнение, необходимое для корректного использования RSA.

Криптосистема ECDSA

ECDSA является "эллиптическим" вариантом криптосистемы электронной подписи DSA. Последняя (DSA) - сейчас практически не используется. Интересно, что она, в свою очередь, является вариантом схемы подписи Эль-Гамаля, которая представляет собой алгоритм, построенный на базе задачи дискретного логарифмирования, и, таким образом, также имеющий прямое родство с протоколом Диффи-Хеллмана.

Фундаментальную идею схемы ECDSA можно изложить следующим образом: предложим такую пару алгоритмов для генерации значения подписи и проверки этого значения, которые будут "сходиться" к одному значению только в том случае, если стороны использовали ключи из связанной однонаправленной функцией пары (это, впрочем, очевидная схема для ЭЦП). В качестве однонаправленной функции используется умножение точки эллиптической кривой на скаляр. При этом подписи "рандомизируются", для чего служит дополнительный параметр - ещё одна точка кривой. При успешной проверке, результат, полученный проверяющей стороной, совпадёт с этой дополнительной точкой - откуда и возникает, достаточно условное, представление о том, что схемы сходятся "в одной точке". ECDSA существенно отличается от RSA, как по составу и свойствам параметров, ключей, так и по алгоритмам генерации подписи и её проверки. ECDSA, вообще говоря, несколько сложнее. Ключевым является тот факт, что обе основные части криптосистемы ECDSA - алгоритм вычисления подписи и алгоритм проверки, - представляют собой две части единого базового соотношения.

Итак, прежде чем использовать ECDSA, стороны должны согласовать общие параметры: выбрать кривую E (она задаётся парой чисел); выбрать поле, над которым будут производиться операции на кривой (кривая используется в целочисленной форме); выбрать соответствующую общую точку на кривой - генератор G. Точка - это пара значений (x,y), которые далее называются координатами. Как и в случае с "эллиптическим" вариантом DH, на кривой задана операция сложения точек - это та же самая операция, что используется и в ECDH. На этой операции строится алгоритм удвоения точки и умножения на скаляр. Именно умножение на скаляр является основной криптосистемы (опять же, аналогично ECDH). Так как используется аддитивная запись, то речь идёт именно об умножении, однако в мультипликативном случае ему соответствовало бы возведение в степень, отсюда и возникает "логарифмирование". Если у нас есть точка N на кривой, то умножение на скаляр dN - тоже даёт точку (B) на кривой. Задача вычисления d по известным N, B и есть задача дискретного логарифмирования на эллиптической кривой, уже знакомая из описания протокола Диффи-Хеллмана.

Открытый ключ в ECDSA - это точка Q на кривой, полученная умножением выбранного генератора G на целое положительное число d: Q = dG. Соответственно, d - является секретным ключом. Схема здесь напоминает RSA, только в данном случае в состав открытого ключа не входит некий модуль. Генератор G является публичным значением, но, как было только что отмечено, для того, чтобы определить d, потребуется отыскать соответствующий логарифм, что нереализуемо на практике (при условии правильно выбранных параметров криптосистемы). Открытый ключ, таким образом, представляет собой пару чисел (xq,yq) - так как точке соответствует две координаты.

Для вычисления подписи в ECDSA также используется хеш-функция, позволяющая получить "сжатое" представление сообщения M: H = Hash(M). Значение H приводится к разрядности выбранных параметров. В описании ниже - n обозначает порядок генератора G, то есть nG = 0 (0 - нейтральный элемент кривой). Схема существенным образом базируется на "рандомизации" - каждая подпись использует случайный параметр k, который не должен повторяться. Уникальность k является фундаментальным требованием для обеспечения стойкости ECDSA, посколько повторное использование позволяет вычислить секретный ключ. Это, пожалуй, самая часто упоминаемая в литературе особенность ECDSA, но тем не менее, ошибки тут всё равно встречаются. Именно на некорректном, повторном использовании k основывался нашумевший взлом системы защиты ПО Sony Playstation в 2010 году.

Вычисление подписи (на входе: значение хеш-функции от сообщения - H; секретный ключ - d) выполняется следующим образом (RFC 6979):

Так как от выбора k зависит стойкость реализации ECDSA, для генерации этого параметра предложены различные безопасные схемы, в том числе, и детерминированные алгоритмы, работающие наподобие счётчика и, таким образом, сводящие к минимуму вероятность повтора k.

Для проверки подписи необходимо знать параметры криптосистемы, а также открытый ключ Q. Перед проверкой требуется убедиться, что переданные значения соответствуют криптосистеме (Q является точкой соответствующей кривой, и так далее). Проверка выполняется по следующему алгоритму (на входе - значение хеш-функции H от сообщения, пара (r,s), открытый ключ Q):

Согласно шагу 4 алгоритма вычисления значения подписи: s = (H + rd)k-1 mod n. Выполним подстановки (все сравнения по модулю n, вместо знака ≡ для простоты используем знак равенства):

ECDSA используется как в SSL-сертификатах, так и для удостоверения серверных параметров DH. При этом ECDSA нельзя использовать для шифрования, а только для получения и проверки электронной подписи.

Рассмотрим фрагмент трафика TLS-сессии - это сообщение ServerKeyExchange, в котором передаются параметры протокола Диффи-Хеллмана на эллиптических кривых (ECDH), подписанные ECDSA.

	Смещение	Байты				Комментарий
	(десятичное)	(шестнадцатеричное представление)
0000   	16 03 03 	; Handshake, TLS 1.2.
0003	00 95		; Длина: 0x0095 = 149 байтов.
0005	0c		; ServerKeyExchange.
0006	00 00 91 	; Длина: 0x000091 = 145 байт.
0009	03 00 17 	; Серверные параметры ECDH: 0x03 - префикс именованной кривой;
		  	  0x0017 - индекс кривой secp256r1.
0012	41 		; Длина блока серверной части DH: 0x41 = 65 байт.
0013	04 f3 52...	; Значение серверной части DH - это точка на кривой secp256r1.
[...]
0078	06 03		; Тип подписи и хеш-функции. 0x06 - хеш-функция SHA-512;
                	  0x03 - тип подписи ECDSA.
0080	00 48 		; Длина блока значения подписи: 0x0048 - 72 байта;

0082	30 		; Далее идёт значение подписи. Значение представляет собой
			  пару целых чисел (r,s). Числа имеют разрядность, соответствующую
  			  разрядности группы кривой (в данном случае: 256 бит, то есть, 32
			  байта на одно число). Пара кодируется в соответствии с ASN.1, в
			  двоичном представлении. Таким образом, для записи двух чисел
			  потребуется 32 * 2 = 64 байта. Остальные байты содержат служебные
			  значения кодирования ASN.1. Так, 0x30 - обозначает, что будет
			  передана последовательность (SEQUENCE). Следующий байт - длина
			  последовательности в октетах.

0083	46 		; Длина последовательности ASN.1: 0x46 = 70 байт (октетов).
0084	02 		; Префикс, обозначающий в ASN.1 тип INTEGER (целое).
0085	21		; Длина записи значения INTEGER: 0x21 = 33 байта (октета) (32+1).
0086	00 98 8b... 	; Далее идёт значение первого элемента подписи, 33 байта.
			  Так как само число 32-байтовое, первый байт содержит
			  значение 00.
[...]
0119	02 21 		; Аналогичные предыдущему числу поля ASN.1: INTEGER (0x02),
			  33 (0x21) октета.
0121	00 99 26... 	; Значение второго элемента подписи (33 байта, первый - 00).

Параметры криптосистем

Все описанные криптосистемы подразумевают, что стороны согласовали некоторые параметры, определяющие контекст применения:

Наибольшее число параметров требуют "эллиптические" криптосистемы. В TLS для них принято использовать типовые кривые, обозначенные зафиксированными именами, но при установлении соединения можно указать и любую другую кривую. Очевидно, и сервер, и клиент должны соответствующую кривую поддерживать. Каждому типовому набору соответствует сама кривая и поле, над которым проводятся операции. Реестр имён и индексов ведёт IANA, названия определены в RFC: 4492, 7027 и др. Конкретные параметры определены в сопутствующих документах NIST и ANSI. В 2016 году соответствующий реестр IANA переименован в "Реестр поддерживаемых групп" ("Supported Groups Registry"), что лучше соответствует математической действительности, потому что используемые в TLS криптосистемы работают в группах, просто, в ряде случаев, это группы точек эллиптической кривой.

С именованием кривых существует некоторая исторически сложившаяся путаница. Например, кривая, распространённая в сертификатах, используемых для HTTPS, называется prime256v1, она же - secp256r1, она же - NIST P-256; а параметры этой кривой определены, в частности, в рекомендации NIST 186-4.

Типичные параметры ECDSA, на примере NIST P-256 (источник: SafeCurves, там же представлены сводные данные по всем другим распространённым кривым):

-- Уравнение кривой:

y2 = x3-3x+41058363725152142129326129780047268409114441015993725554835256314039467401291

Кривые определяются параметрами a,b уравнения: y2 = x3 + ax + b

Соответственно:
a = -3
b = 41058363725152142129326129780047268409114441015993725554835256314039467401291

-- Поле; значение задающего модуля P (простое число):

P = 115792089210356248762697446949407573530086143415290314195533631308867097853951 =
0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff =
2256 - 2224 + 2192 + 296 - 1.

-- Генератор G. Точка на кривой, заданная парой координат (x,y):

(48439561293906451759052585252797914202762949526041747995844080717082404635286,
36134250956749795798585127919587881956611106672985015071877198253568414405109)
= (0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)

-- Порядок генератора G; n (простое число):

115792089210356248762697446949407573529996955224135760342422259061068512044369
= 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
= 2256 - 2224 + 2192 - 89188191075325690597107910205041859247

Сертификаты

SSL-сертификаты играют важную роль в современной инфраструктуре TLS. У SSL-сертификата одно предназначение: привязать открытый ключ к некоторому сетевому имени. Например, сопоставить ключ 0xA3VEF7...DA3107 имени example.com. SSL-сертификаты не содержат никакой секретной информации. Они не являются непосредственными носителями ключей шифрования передаваемых данных в TLS (за исключением открытого серверного ключа RSA, который может быть использован для шифрования сеансового секрета в устаревших режимах работы TLS).

Открытый ключ, который содержится в сертификате, принадлежит искомому серверу, с которым клиент планирует установить соединение. Однако открытый ключ может быть подменён третьей стороной на свой, имитирующий легитимный ключ сервера. Для того, чтобы можно было обнаружить подобную подмену, служит инфраструктура удостоверяющих центров, которая даёт клиенту механизм проверки принадлежности ключа. То есть, клиент соглашается верить некоторой третьей стороне (не злоумышленнику, а удостоверяющему центру - УЦ), что эта третья сторона проверила соответствие ключа и сетевого имени (в вебе это обычно домен). Результат такой проверки подтверждается подписью УЦ, которую он ставит на сертификате сервера. Для подписи сейчас используются криптосистемы RSA (повсеместно) и ECDSA (поддержка этой криптосистемы до сих пор ограничена - соответствующие сертификаты доступны не у всех УЦ).

Предполагается, что клиент TLS имеет в своём распоряжении некоторый набор сертификатов удостоверяющих центров (часть из этих сертификатов является корневыми) и может проверить подписи на предоставляемых сервером сертификатах, выстроив цепочку доверия, ведущую от серверного ключа к одному из доверенных корней. На практике, в браузеры встроены многие десятки корней, соответствующих различным УЦ. Для выстраивания цепочки доверия определяющее значение имеет именно корневой ключ, а не сам сертификат, который его содержит. Тем не менее, в сертификате содержатся имена, которые позволяют отличить один ключ от другого и сопоставить отдельные сертификаты друг другу.

В SSL/TLS используются сертификаты формата X.509, это очень старый формат, изначально разрабатывавшийся для телеграфа, но позже адаптированный для использования в Интернете, в частности, в вебе.

Сертификат представляет собой электронный документ, имеющий определённую спецификацией структуру. Так, каждый сертификат содержит поля Issuer и Subject. Поле Issuer определяет имя стороны, выпустившей данный сертификат (поставившей на нём подпись), а поле Subject - имя стороны, для которой сертификат выпущен. В случае веба, в Issuer обычно указано имя из сертификата УЦ (не обязательно корневого), а в Subject - доменное имя, адресующее сайт.

Клиент получает серверные сертификаты (в большинстве случаев - это цепочка, но серверный сертификат может быть и единственным) в сообщении Certificate (см. выше), выстраивает их в последовательность, где каждый сертификат удостоверяет следующий, проверяет подписи и соответствие имени серверного сертификата имени сервера, с которым планирует установить соединение. Проверка имени является важнейшим аспектом, так как злоумышленник может выпустить доверенный сертификат для своего имени сервера, но предъявлять его под именем другого сервера (перехватив сеанс тем или иным способом). В таком случае, полностью валидный сертификат злоумышленника будет отличаться от легитмного только указанным именем. Например, сертификат выпущен для example.com, а предъявляется для test.ru. (Такое несоответствие также является одной из самых распространённых ошибок настройки TLS-серверов.)

Сертификат выпускается на определённый срок, который указан в самом сертификате. Сертификат может быть отозван, по какой-то причине, раньше окончания срока действия. Браузеры и другие TLS-клиенты должны проверять статус отзыва сертификатов, для этого существует ряд механизмов, однако в повседневной реальности проверка отзыва сертификатов, фактически, не работает - этот момент нужно учитывать при анализе рисков.

Посмотрим на пример SSL-сертификата - это расшифровка полей сертификата, выпущенного для dxdt.ru УЦ WoSign, полученная утилитой openssl x509:

Version: 3 (0x2)
; Версия спецификации, обычно - 3

Serial Number: 58:04:6c:41:4b:a3:02:a0:a7:c1:13:a3:07:53:97:90
; Серийный номер сертификата. На генерацию серийных номеров наложены определённые
  требования. Так, номера не должны быть предсказуемыми, не должно быть двух
  различных сертификатов одного УЦ с одинаковым серийным номером.

Signature Algorithm: sha256WithRSAEncryption
; Алгоритм подписи: это RSA-сертификат, с RSA-подписью, использующей хеш-функцию
  SHA-256.

Issuer: C=CN, O=WoSign CA Limited, CN=WoSign CA Free SSL Certificate G2
; Имя стороны, выпустившей сертификат: WoSign. Это простое текстовое поле,
  определённого формата. В сертификате имеет значение каждый байт, это касается
  и имени в Issuer.

Validity
    Not Before: May  5 23:50:37 2015 GMT
    Not After : May  6 00:50:37 2018 GMT
; Срок действия сертификата. Не ранее - не позднее.

Subject: CN=dxdt.ru
; Имя, для которого выпущен сертификат - в нашем случае это dxdt.ru. Обратите
  внимание, что если не указано дополнительных имён DNS (см. ниже), сертификат
  будет валиден только для для dxdt.ru, для www.dxdt.ru такой сертификат не
  подойдёт. Возможен выпуск сертификатов для имён с маской подстановки: *.dxdt.ru.
  В этом случае сертификат будет валиден для любого имени третьего уровня в dxdt.ru,
  например для tls.dxdt.ru или static.dxdt.ru, однако для dxdt.ru - сертификат
  окажется невалидным.

Subject Public Key Info:
; Данные открытого ключа сервера.

Public Key Algorithm: rsaEncryption
; Алгоритм - используемая криптосистема (RSA).

Public-Key: (2048 bit)
; Сам ключ (разрядность 2048 бит). Открытый ключ RSA (как описано выше) состоит
  из модуля и шифрующей экспоненты, обычно она выбирается из типовых значений,
  удобных для оптимизации вычислительных операций.

Modulus:
                    00:c4:c5:f6:49:02:50:3b:d2:9d:b6:d6:cd:19:80:
                    0f:66:a0:c9:eb:6b:b5:05:c5:52:e3:d9:09:25:a0:
                    2e:86:d8:e5:20:0c:39:66:bb:03:57:3e:23:a5:f7:
                    98:2d:99:16:f6:70:d4:87:c6:76:86:79:78:6a:4f:
                    bd:61:6a:89:23:7d:8c:dd:fc:8b:8f:d6:91:a5:33:
                    ed:df:35:b0:ee:cb:e7:43:1e:5a:8c:7e:e9:49:c3:
                    82:2a:95:9f:f1:a6:86:61:22:97:81:df:2b:55:b8:
                    f4:ad:0e:c1:f6:d2:c6:66:31:d3:57:a0:51:1e:5f:
                    7e:ce:d3:ba:27:21:cd:16:66:e5:22:e5:64:45:46:
                    64:8f:6a:f1:6c:2d:f2:8e:28:c1:72:27:3b:bf:8a:
                    10:56:c0:16:94:ec:60:c1:70:c0:c2:3d:28:8b:5c:
                    4c:44:e2:ac:67:1a:e7:93:ec:ec:1f:9b:a1:30:f6:
                    71:95:77:8d:8d:d4:d4:43:4e:1f:5e:5b:13:a9:48:
                    1d:c2:a3:87:26:e6:65:8a:ff:75:fb:84:c9:67:e3:
                    88:51:28:b2:2c:45:3a:7f:37:68:dd:da:14:73:c7:
                    71:b7:1c:ea:ea:d3:08:b4:a8:6b:8d:df:f6:be:71:
                    07:f0:fe:5c:b4:4e:15:de:2e:d8:96:8b:15:96:83:
                    b4:c3
; Модуль (2048 бит).
Exponent: 65537 (0x10001)
; Экспонента.

; Далее идут расширения формата, играющие важную роль в современной
  инфраструктуре SSL-сертификатов. Ряд из этих расширений имеет прямое
  отношение к работе TLS.

X509v3 extensions:
    X509v3 Key Usage:
         Digital Signature, Key Encipherment
; Указание на допустимое использование ключа из сертификата: в данном
  случае ключ может быть использован для создания подписи и для шифрования
  других ключей. Первый вариант необходим для генерации сеансового ключа
  по алгоритму Диффи-Хеллмана; второй - для передачи клиентом зашифрованного
  секрета. Значения из этого поля проверяются клиентом, например, браузером.

X509v3 Extended Key Usage:
         TLS Web Client Authentication, TLS Web Server Authentication
; Дополнительные параметры использования ключа: допускается применение для
  аутентификации веб-клиента и веб-сервера (традиционное использование).

X509v3 Basic Constraints:
               CA:FALSE
; Ограничения на использование ключа и сертификата: данный сертификат не
  является сертификатом УЦ, то есть от него не должны выпускаться другие
  сертификаты. Технически, никаких проблем с выпуском ещё одного сертификата,
  подписанного ключом данного, нет. Однако при выстраивании цепочки доверия,
  клиент (например, браузер), должен сверять статусы сертификатов и доверять
  только подписям, полученным от сертификатов, для которых в этом поле указано
  CA:TRUE. Интересно, что браузер Microsoft IE длительное время, до 2003 года,
  некорректно проверял статус сертификата (из-за ошибки в коде), что позволяло
  выпускать валидные, с точки зрения IE, сертификаты для любого имени, приобретя
  обычный серверный сертификат у того или иного УЦ.

X509v3 Subject Key Identifier:
       37:93:60:97:E7:8A:3C:FF:C6:68:6A:DD:97:92:A5:32:59:87:8A:E4
X509v3 Authority Key Identifier:
       keyid:D2:A7:16:20:7C:AF:D9:95:9E:EB:43:0A:19:F2:E0:B9:74:0E:A8:C7
; Специальные идентификаторы ключей, позволяющие клиенту быстрее найти
  соответствующие ключи в сертификатах в своих хранилищах. Это особенно актуально
  для поиска подходящего ключа УЦ, так как ускоряет построение цепочки валидации.

Authority Information Access:
                OCSP - URI:http://ocsp6.wosign.com/ca6/server1/free
                CA Issuers - URI:http://aia6.wosign.com/ca6.server1.free.cer
; Адреса: ответчика OCSP (Online Certificate Status Protocol) - это протокол
  онлайн-проверки статуса отзыва сертификата; и сертификата УЦ, от которого
  выпущен данный сертификат.

X509v3 CRL Distribution Points:
                Full Name:
                  URI:http://crls6.wosign.com/ca6-server1-free.crl
; Существует исторический метод отзыва сертификатов при помощи выпуска списков
  отозванных сертификатов (CRL - Certificate Revocation List). Данное поле
  содержит адрес, по которому список доступен.

X509v3 Subject Alternative Name:
                DNS:dxdt.ru, DNS:www.dxdt.ru, DNS:tls.dxdt.ru
; Дополнительные имена. Это расширение очень полезно, так как позволяет указать
  дополнительные имена, для которых валиден данный сертификат. Например, здесь
  указано www.dxdt.ru и tls.dxdt.ru - последнее имя позволяет вам читать этот
  материал через безопасное соединение на сайте tls.dxdt.ru, который находится
  на другом, относительно dxdt.ru, сервере.

X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.36305.6.1.2.2.1
                  CPS: http://www.wosign.com/policy/
; Перечень политик, которым соответствует данный сертификат. Политики
  устанавливаются УЦ (в соответствии с некоторыми нормами, если УЦ желает, чтобы
  его корни были включены в распространённые браузеры), но являются чисто
  административным инструментом, напрямую не относящимся к криптографии, хотя
  и влияющим на используемые механизмы. Например, именно политика определяет, будет
  ли сертификат расцениваться как сертификат "Расширенной проверки" (EV), а такие
  сертификаты, в свою очередь, требуют использования некоторых дополнительных
  криптографических механизмов.

    Signature Algorithm: sha256WithRSAEncryption
         09:20:65:da:92:9f:93:d2:96:55:80:51:8e:06:3a:37:c1:26:
         33:af:da:2a:63:0e:da:c2:b2:c9:31:80:b3:de:9a:b2:48:ce:
         3b:11:ee:a7:81:fc:9e:48:5d:a0:59:6f:b6:d0:e7:da:c9:86:
         47:79:ef:ea:71:cd:72:2f:b1:7d:ac:84:88:84:c5:a8:19:47:
         4d:6b:1c:e0:8f:81:b0:c9:13:95:c9:f7:27:7a:93:2b:2c:08:
         ac:c8:69:2d:0d:4f:c6:4d:b7:18:96:4f:50:c0:0f:23:20:cf:
         22:28:5d:fd:e5:89:dd:c9:1d:9c:c6:b3:54:4b:65:e5:8d:2e:
         26:07:85:fb:d4:d8:23:ed:30:dc:60:33:da:9e:85:79:37:1d:
         ec:04:87:ad:c2:00:7b:87:06:7e:ee:26:27:25:ef:4a:5c:8f:
         64:0f:77:28:3f:69:61:37:36:f8:40:78:8d:7d:00:f3:2f:e7:
         fb:5b:c6:52:c4:5e:e5:19:54:06:ad:3f:7d:48:19:59:82:ef:
         46:96:3b:fc:71:3f:4f:99:94:d5:1d:c2:80:44:e6:af:3c:cf:
         01:c2:4c:e7:e8:53:8b:56:ad:06:c7:c9:23:90:7e:93:1d:f5:
         f6:2e:29:21:ae:13:23:da:7a:f9:04:ea:62:8c:e3:24:f1:05:
         db:0d:e5:1f
; Значение подписи сертификата. Подпись вычисляется от значения хеш-функции, аргументом
  которой является весь сертификат (за исключением, соответственно, подписи).
  Генерируется подпись при помощи секретного ключа УЦ, соответствующего сертификату
  с именем, указанным в Issuer.

Итак, SSL-сертификаты являются важным элементом TLS, позволяющим проводить аутентификацию. Ключи из сертификатов используются при генерации общего секрета. В теории, возможно установление анонимного TLS-соединения без использования сертификатов (это не запрещено спецификацией, а серверное сообщение Certificate не является строго обязательным), однако массово применяемые на практике методы генерации сеансовго ключа основываются на использовании серверного сертификата (а точнее - на использовании открытого ключа из сертификата).

Основные современные претензии к SSL-сертификатам касаются сложившейся административной структуры УЦ. Так, пока что никакая распространённая технология не мешает УЦ выпустить "подменный" сертификат, позволяющий прозрачно перехватывать TLS-соединения, используя атаку типа "человек посередине" (см. ниже в разделе про перехват HTTPS). Но такие технологии разрабатываются, к ним относится инициатива Certificate Transparency (поддерживаемая Google), Certificate Pinning или Public Key Pinning, когда в браузерах сохраняются (и обновляются) заведомо корректные для данного узла сертификаты или открытые ключи, а также технология DANE, использующая DNS в качестве дополнительной системы контроля.

Шифры в TLS

Как рассказано выше, TLS использует для защиты информации шифронаборы. Шифронабор (Cipher Suite) позволяет обеспечить не только сокрытие информации, но и её целостность. В состав шифронабора входит симметричный шифр. С его помощью осуществляется шифрование потока данных, передаваемых через TLS-сокет. Возможно использование TLS без шифрования, однако это не является распространённым случаем.

В терминологии TLS существуют шифрующие и расшифровывающие функции. Эти функции работают с криптографическим контекстом соединения, о котором договорились узлы. После того, как узлы обменялись сообщениями ChangeCipherSpec (см. выше), все TLS-сообщения шифруются (а в проектируемой версии TLS 1.3 шифрование включается даже несколько раньше, сообщения ChangeCipherSpec там не используется). Шифрующая функция преобразует открытый текст, подготовленный для отправки в составе TLS-записи (строго говоря, после сжатия, но в современной реальности TLS сжатие обычно не используется), в секретный текст (зашифрованный). Расшифровывающая функция выполняет обратную операцию. Для того, чтобы шифрующие и расшифровывающие функции успешно работали на обоих узлах требуется выработка общего криптографического контекста - это происходит в рамках обмена сообщениями Handshake. В TLS могут использоваться и классические потоковые, и блочные шифры, последние - в различных режимах. Обычно, шифрующие и расшифровывающие функции являются одним симметричным шифром и различаются только составом ключей.

Шифр — это некоторый набор обратимых преобразований данных, каждое из которых определяется параметром - ключом шифрования. Порядок применения этих преобразований к входным сообщениям принято называть режимом шифрования. Простейшая криптосистема может включать лишь один шифр, то есть, грубо говоря, состоять из одного алгоритма шифрования и (обратного к нему) алгоритма расшифрования. В симметричных системах, которые используются для шифрования потока данных в TLS, знание шифрующего ключа позволяет легко получить расшифровывающий ключ, то есть расшифровать секретное сообщение. В используемых сейчас массовых симметричных криптосистемах шифрующий и расшифровывающий ключи просто совпадают, поэтому обычно говорят об одном и том же ключе. Другими словами, симметричные системы подразумевают, что обе стороны, обменивающиеся информацией по закрытому каналу, знают некий общий секрет ― например, секретный симметричный криптографический ключ, который позволяет как шифровать, так и расшифровывать сообщения.

Симметричные криптосистемы - первые из придуманных человечеством. Историческими примерами симметричных криптосистем являются так называемые "наивные" шифры — например, шифры простой замены букв текста на естественном языке, где в качестве ключа может выступать та или иная перестановка алфавита. Современные шифры, вроде AES и 3DES, также являются симметричными. К симметричным относится и единственная криптосистема, обладающая абсолютной стойкостью - шифр Вернама, с равновероятным одноразовым ключом, равным по длине шифруемому сообщению (одноразовый шифр-блокнот). Шифр Вернама не очень удобен, поэтому нечасто используется на практике. Однако основная идея этого шифра послужила прообразом самых защищённых режимов шифрования в TLS.

В основе логики построения современных шифров лежит задача сокрытия статистических свойств исходного открытого текста. То есть, шифр стремятся спроектировать таким образом, чтобы его вывод был вычислительно неотличим от результата (псевдо)случайной функции. Дело в том, что попытка скрывать лишь сами символы открытого текста не даёт никакой секретности. Игрушечным, но весьма показательным примером является шифр простой замены, где буквы открытого текста заменяются на какие-то другие символы (например, на специально выдуманные "иероглифы"). Каждая буква неузнаваема, но прочитать такой шифротекст легко, если известно, на каком языке был записан открытый текст, так как статистические свойства открытого текста прямо наследуются: частотность "иероглифов" соответствует известной частотности букв в тексте на исходном языке. Эта классическая "дешифровка" многократно описана в художественной литературе. Соответственно, современные криптосистемы прежде всего направлены на сокрытие статистических свойств открытого текста, на "размытие" статистики "до уровня случайности". (Важно отметить, что при правильном выборе процедуры генерации ключей, шифр простой замены может быть сделан чрезвычайно стойким, вплоть до невозможности вскрытия персонажами художественного произведения.)

Криптосистемы строятся на базе некоторых "примитивов" - своего рода строительных блоков, которые выполняют базовые операции над данными. К базовым операциям относятся подстановки, перемешивание, сложение и умножение (выполняемые по модулю некоторого числа или ещё каким-то особенным способом). Эти операции проводятся над входными данными, рассматриваемыми как набор символов, обычно, символами являются биты и байты, но возможны и другие варианты: например, группы из четырёх битов или двухбайтовые символы. Для того, чтобы составить самое простое представление о современных симметричных шифрах, можно представить, что шифр - это некоторый "ящик", на входы которого (в режиме шифрования) поступают открытый текст и ключ, внутри символы открытого текста перемешиваются по схеме, определяемой ключом, и выводятся через выход в качестве шифротекста. Каждый бит открытого текста оказывает существенное влияние на весь шифротекст: благодаря "лавинному эффекту" изменение даже значения одного бита в открытом тексте (или в ключе зашифрования) приводит к массовым изменениям в соответствующем шифротексте. Для расшифрования используется "ящик" на входы которого поступают шифротекст и ключ, символы опять перемешиваются, в результате на выходе получается открытый текст: если ключи совпали, то этот открытый текст совпадёт с исходным открытым текстом. Далеко не всегда для шифрования и расшифрования используется один и тот же "ящик": алгоритмы могут различаться.

Ключом в массовых современных симметричных криптосистемах является целое число достаточно большой разрядности ― 128 бит и более. Если криптосистема добротная, то есть, наилучшим алгоритмом атаки (взлома) для неё является полный перебор всего множества ключей, то 128 бит - более чем достаточная степень безопасности. Предположим, что ключ можно угадать с вероятностью 1/2, перебрав половину пространства ключей. (Вероятность 1/2 - это огромный показатель в разрезе криптографической защиты информации. Криптосистема, которую можно взломать с такой вероятностью, никакой стойкостью не обладает.) Тогда, в случае 128-битного ключа, придётся выполнить (2128)/2 = 2127 операций. Это огромное число вариантов, перебрать их за обозримое время невозможно. Проблема состоит в том, что для шифров обычно известны оптимизации, которые позволяют уменьшить число перебираемых вариантов. Например, снизить его для 128-битного ключа до 256. Перебрать 256 вариантов для вычислительно незатратного шифра можно очень быстро, даже если использовать в качестве вычислителя смартфон. Наличие такой оптимизации является уязвимостью криптосистемы.

Шифры принято делить на блочные и потоковые. Блочные шифры отличаются от потоковых тем, что работают с блоками фиксированной длины и требуют разбиения потока байтов на такие блоки; в блочном шифре каждый байт шифруется в составе блока байтов (битов). Потоковые шифры, напротив, работают прямо с потоком битов (или байтов, потому что байт, вообще говоря, тоже можно считать блоком), без разбиения. Существуют режимы использования блочных шифров, которые позволяют рассматривать их, фактически, как потоковые. Классический потоковый шифр TLS - RC4, который сейчас считается устаревшим. Классические блочные шифры: DES и 3DES, ГОСТ 28147-89. Особенностью блочных шифров является то, что они требуют использования дополнения (padding): в случае, если открытый текст не кратен размеру блока, его нужно дополнить до соответствующего числа битов. С этим аспектом связано немало уязвимостей в реализациях TLS.

AES - является современным блочным шифром, повсеместно используемым в TLS. Строго говоря, AES - это название стандарта шифрования, сам шифр называется Rijndael (произносится: "рай'н-да'л"). Rijndael выиграл конкурс, проводившийся NIST, и стал стандартом (AES) в 2002 году (сам шифр опубликован в 1998). Шифр стандартизован для использования с 128-, 192-, 256-битным ключом и 128-битным блоком.

Шифр строится из "раундов" (rounds) - повторных выполнений некоторого набора операций по трансформации блока данных; каждое повторение использует свой ключ. Набор ключей генерируется (разворачивается) из исходного ключа, с использованием специального алгоритма. Так, в случае 128-битного ключа (AES-128), на выходе алгоритма разворачивания ключа - 176 байт или 11 128-битных ключей для повторных трансформаций, составляющих шифр. Разворачивание ключа в AES использует циклические сдвиги ключей, подстановки и операции умножения в конечном поле. Процедура разворачивания ключа является составной частью многих шифров, её надёжность и "необратимость" оказывают огромное влияние на стойкость шифра в целом.

Шифр AES оперирует матрицами (таблицами) размером 4х4 элемента. Такая матрица как раз соответствует 16 байтам 128-битного блока: один элемент - один байт. Матрица со значениями байтов называется состоянием шифра, одна трансформация (раунд) последовательно выполняет над матрицей и её элементами несколько преобразований, после чего к матрице применяется ключ данного раунда. Результат преобразований переходит в следующий раунд, где всё повторяется, но со следующим ключом.

Базовые преобразования в AES-128 (названия также определены в стандарте):

После того, как выполнены три преобразования матрицы состояний, к ней применяется ключ действующего раунда. Операция называется AddRoundKey. Значение ключа разбивается на четыре блока по четыре байта и каждый из них применяется к столбцам матрицы состояний при помощи операции XOR. Операция AddRoundKey также выполняется перед первым раундом.

Далее операции шифра повторяются, но в AddRoundKey суммирование проводится со следующим ключом. Последний раунд не включает MixColumns. Для разной длины основного ключа AES предусматривает разное число только что описанных трансформаций (раундов): для 128-битного ключа - 10 раундов; AES-192 - 12 раундов; AES-256 - 14.

Запись AES на псевдокоде выглядит следующим образом (из документа FIPS PUB 197):

Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
// Nb - число столбцов в матрице состояния;
// Nr - число раундов;
// in, out - входная и выходная матрицы состояний;
// w - поток ключей раундов.
begin

	byte state[4,Nb]
	state = in

	AddRoundKey(state, w[0, Nb-1])

	for round = 1 step 1 to Nr–1

		SubBytes(state)
		ShiftRows(state)
		MixColumns(state)
		AddRoundKey(state, w[round*Nb, (round+1)*Nb-1])

	end for

	SubBytes(state)
	ShiftRows(state)
	AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1])

	out = state

end

Преобразования шифра обратимы, соответственно, можно построить алгоритм расшифрования, который будет восстанавливать открытый текст, при условии наличия ключа. Наличие режима расшифрования, как ни странно, требуется далеко не всегда. Так, современные режимы использования блочных шифров основаны только на зашифровании.

Если внимательно посмотреть на принципы построения операций AES, то окажется, что большая часть преобразований может быть предвычислена для всевозможных значений отдельных байтов (0-0xFF) и сохранена в таблицах, из которых значения будут извлекаться при работе алгоритма. Это основной программный метод оптимизации AES. Он же помогает составить общее представление о логике построения данного шифра: фактически, AES сводится к извлечению некоторой последовательности элементов матрицы возможных значений и сложении их при помощи XOR (то есть, по модулю 2) с входным блоком данных, при этом сама последовательность определяется используемым ключом (который разворачивается в набор ключей раундов).

Итак, блочный шифр ставит в соответствие блоку байтов открытого текста фиксированной длины и ключу шифрования - блок зашифрованных данных, той же длины. AES является самым часто используемым сейчас в TLS блочным шифром, фактически, это единственный распространённый безопасный вариант. Другие блочные шифры, с которыми можно столкнуться в практике TLS: 3DES (развитие устаревшего и нестойкого шифра DES, стандарта, предшествовавшего AES); Camellia (шифр восточного происхождения, включённый в ряд стандартов).

Потоковый шифр, имевший достаточное распространение в реализациях TLS - RC4. Из-за обнаруженных уязвимостей данный шифр сейчас считается нестойким, поэтому не должен применяться на практике. При поддержке Google на смену RC4 идёт потоковый шифр ChaCha20, который, в основном, пока используется только в связке браузер Chrome и сервисы Google.

Режимы шифрования

Если длина открытого текста превышает длину блока, то открытый текст для шифрования блочным шифром потребуется разделить на отдельные фрагменты, соответствующие разрядности шифра. Наивный способ использования блочного шифра "в лоб" состоит в прямом шифровании блоков открытого текста, с тем, чтобы получить шифротекст: C = E(K,PT), где PT - блок открытого текста сообщения; K - ключ; E() - алгоритм блочного шифра; С - шифротекст. Способ использования шифра называют "режимом шифрования". Только что описанный наивный вариант называется режимом ECB - Electronic CodeBook.

На практике такой способ применять нельзя, так как он не обладает стойкостью. Дело в том, что идентичные сообщения будут порождать идентичные блоки шифротекста, что приводит к утечке информации об исходном потоке данных. В частности, ECB не скрывает статистические свойства потока. Особенно плохо дела обстоят, если в защищаемом потоке присутствуют структуры, кратные размеру блока. Поэтому на практике в TLS сейчас используются другие режимы.

Режим CBC. CBC обозначает Cipher Block Chaining - в данном режиме зашифрованный блок подаётся на вход следующего шага шифрования и суммируется со следующим блоком открытого текста при помощи XOR, результат операции XOR шифруется, и так далее. То есть, Cn = E(K,PTn⊕Cn-1). Блоки оказываются соединены в цепочку. В качестве первого значения используется вектор инициализации, заданный в параметрах криптосистемы. Схематично данный режим можно представить следующим образом (данные преобразуются сверху вниз, от открытого текста к шифротексту, IV - вектор инициализации; PTn - блоки открытого текста, представляющие шифруемый поток; X - операция XOR; E - шифр):

      PT1       PT2       PT3
       |         |         |
  IV-->X    /--->X    /--->X
       |   /     |   /     |
   K-->E--/  K-->E--/  K-->E ...
       |         |         |
       С1        С2        С3
Расшифрование использует обратный алгоритм шифра и обратную схему следования блоков. При этом, если для зашифрования следующего блока нужно знать результат шифрования предыдущего, что диктует последовательную работу функции шифрования, то для расшифрования произвольного блока достаточно знать значение шифротекста предыдущего: то есть, расшифрование может работать параллельно.

Режим CBC обладает важным свойством: одинаковые блоки потока открытого текста не порождают одинаковые блоки шифротекста. Схема позволяет преобразовать поток открытого текста большой длины в связанный поток зашифрованных блоков. Важное значение имеет вектор инициализации (это блок данных, соответствующий разрядности шифра): вектор может генерироваться случайно, либо в его роли может выступать некий счётчик. В общем случае, вектор не должен повторяться в паре с одним и тем же ключом и должен быть непредсказуем для третьей стороны. Скрывать вектор инициализации, вообще говоря, не требуется.

В TLS 1.0 режим CBC спроектирован так, что источником данных инициализирующего вектора для следующей TLS-записи является последний блок предыдущей. Это послужило основой для нашумевшей атаки BEAST. (Первый вектор инициализации генерируется в TLS 1.0 в рамках создания криптографического контекста и не передаётся между узлами, то есть, остаётся секретным, что, впрочем, не особенно увеличивает степень безопасности протокола.) В TLS 1.1 ситуация изменилась: исходные данные для вектора инициализации передаются вместе с зашифрованной записью и периодически изменяются (между записями), сам вектор для очередной итерации вычисляется по специальному алгоритму, который призван затруднить установление корреляции между значением вектора и другими параметрами, которыми могла бы манипулировать третья сторона. В TLS 1.2 вектор инициализации режима CBC передаётся в открытом виде, в самом начале зашифрованной TLS-записи.

Другим подходящим режимом работы блочного шифра является режим счётчика (CTR). В этом режиме функция шифрования применяется не к блоку открытого текста, а к значению некоторого счётчика. Блок открытого текста суммируется с полученным шифротекстом. То есть: Cn = E(K,L)⊕PT, где L - это блок счётчика, значение этого блока своё для каждой операции зашифрования, обычно, оно просто увеличивается на единицу для каждого следующего блока. Начальное значение задаётся вектором инициализации. Счётчик также не должен повторяться с одним и тем же ключом. Схема работы в режиме счётчика:

       L1       L2       L3
       |        |        |
   K-->E    K-->E    K-->E
       |        |        |
 PT1-->X  PT1-->X  PT1-->X ...
       |        |        |
       C1       C2       C3
Блочный шифр, таким образом, превращается в потоковый: функция шифрования, будучи применённой к счётчику, порождает ключевой поток (гамму), с которой суммируется открытый текст. То есть, шифр здесь используется в качестве источника псевдослучайной последовательности, которая задаётся начальным значением счётчика L и ключом шифрования. Если предположить, что ключевая последовательность случайна, то получаем схему, которая эквивалентна шифру Вернама. Добротный блочный шифр выдаёт результат шифрования, который по статистическим характеристикам должен быть вычислительно неотличим от действительно случайной последовательности.

Для расшифрования, в режиме счётчика применяется та же схема, только блоки шифротекста и открытого текста меняются местами. То есть, для шифрования вычисляется значение XOR от результата шифрования блока счётчика и блока открытого текста. Для расшифрования - XOR от результата шифрования блока счётчика и блока шифротекста. Обратите внимание: сам шифр используется только для шифрования, обратная операция не требуется. Кроме того, в общем случае, не требуется и никакого дополнения последнего блока - лишние байты просто отбрасываются. Режим счётчика - очень эффективен. В TLS, впрочем, сейчас применяется развитие данного режима - режим GCM, включающий также и процедуру аутентификации данных.

Коды аутентификации сообщений

В русскоязычной литературе MAC (Message authentication code - код аутентификации сообщения) называется также имитовставкой. Предназначение MAC - предоставить механизм проверки целостности сообщения, то есть, защитить его от подмены и изменения. В общем случае, код аутентификации - это некоторое значение, вычисляемое для заданного сообщения и (обычно) секретного ключа; значение различается для разных сообщений. Чтобы вычислить корректное значение MAC требуется знать секретный ключ. Таким образом, если сообщение было изменено третьей стороной, то проверка MAC позволит это выявить. Кроме того, корректное значение MAC позволяет утверждать, что данное сообщение было сгенерировано стороной, которой известен соответствующий ключ. Другими словами - предполагается, что подделка MAC вычислительно недостижима, без знания секретного ключа. В TLS - MAC, в той или иной форме, содержится во всех зашифрованных записях. Способ вычисления кода аутентификации зависит от используемого шифронабора и режима шифрования.

HMAC - MAC, основанный на криптографической хеш-функции; используется, например, с шифрами TLS в режиме CBC. HMAC вычисляется независимо от функции шифрования. Более того, как отмечено выше, в TLS, MAC изначально используется неверно: сперва для открытого текста вычисляется HMAC, а потом сообщение, с присоединённым кодом аутентификации, шифруется. Именно так работает режим CBC в TLS. Внутри записи защищённые данные размещаются вместе с кодом аутентификации, и то, и другое - зашифровано. Архитектурная проблема состоит в том, что MAC можно проверить только после расшифрования сообщения и, если значение оказалось некорректным, реализация TLS должна сообщить об ошибке, из-за этого активный атакующий, манипулируя шифрованным текстом, может использовать расшифровывающий узел в качестве криптографического оракула, который постепенно раскроет весь секретный текст. На этом основано несколько практических атак на TLS.

В TLS 1.0 (согласно RFC 2246) HMAC вычисляется на основе хеш-функции, согласованной в криптографическом контексте, например, SHA-1. Значение HMAC - это результат применения хеш-функции к сообщению, в которое "подмешано" значение ключа: H(K⊕Pad1+H(K⊕Pad2+text)), где H - хеш-функция, Padn - константы, K - секрет, text - исходное сообщение, + - конкатенация. Логика построения HMAC в TLS следующая:

   HMAC = Hmac(Key, N + Type + Ver + Len + PT);

   Здесь:
   Hmac(Key,Content) - хеш-функция, например SHA-1, используемая в режиме HMAC (см. выше);
   Key - секрет, согласованный сторонами для MAC в криптографическом контексте сессии;
   N - порядковый номер TLS-записи;
   Type и Ver - тип записи и версия протокола;
   Len - длина данных записи;
   PT - содержание записи;
   + - обозначает конкатенацию.
Таким образом, код аутентификации HMAC зависит от всех полей, определяющих совйства TLS-записи, а не только от полезной нагрузки. При этом ряд полей (N, Type, Ver) - передаются в открытом виде.

Под влиянием множества различных атак, концепция аутентификации защищаемых криптографическими методами данных эволюционировала. Современным подходом (применительно к блочным шифрам) считается использование шифра в режиме счётчика, объединяющем процесс шифрования и вычисления MAC. При этом в область действия MAC попадают и данные сообщения, передаваемые в открытом виде. Такие режимы аутентифицированного шифрования называются AEAD - Authenticated Encryption with Associated Data (аутентифицированное шифрование со связанными данными). Фактически, в TLS сейчас используется единственный такой режим: GCM - Galois/Counter Mode: режим счётчика с аутентификацией Галуа. Этот режим повсеместно встречается вместе с шифром AES.

Аутентифицированное шифрование - GCM

Режим GCM похож на обычный режим CTR тем, что порождает ключевой поток при помощи шифрования блоков со значением счётчика. Полученные шифротексты складываются с открытым текстом при помощи XOR. GCM строится на двух специальных операциях: incr и Mult. Первая операция вычисляет значение следующего блока счётчика по предыдущему. Вторая (Mult) - выполняет умножение в заданном конечном поле (конечное поле - поле Галуа, откуда название).

Блок шифротекста вычисляется так: C = E(K,L)⊕PT. Но GCM включает ещё и алгоритм аутентификации. Фактически, incr и Mult образуют два уровня GCM: первый уровень (incr) задаёт исходную гамму, ключевой поток, который позволяет зашифровать данные, второй уровень (Mult) - связывает получившиеся блоки шифротекста, действуя подобно хеш-функции, и позволяет построить код аутентификации (AuthTag в терминологии GCM). Схема работы следующая (IV - инициализирующий вектор; L - блоки счётчика; K - ключ шифрования; PT - блоки открытого текста; С - блоки шифротекста; A - связанные данные, передаются в открытом виде; E - шифр; incr, Mult_H - функции режима GCM, Mult_H - использует секретный ключ хеш-функции H; (Bits_num) - конкатенация записей полных длин PT и A в битах; X - операция XOR):

(IV-->) L0--->incr--->L1--->incr--->L2--->incr--->L3          -->Ln
        |             |             |             |              |  -\
    K-->E         K-->E         K-->E         K-->E     .    K-->E    |
        |             |             |             |     .        |     = GCTR
        |       PT1-->X       PT2-->X       PT3-->X     .  PTn-->X    |
        |             |             |             |     .        |  -/
        |             C1            C2            C3    .        Cn
        |             |             |             |              |
        | A->Mult_H-->X      /----->X      /----->X      ... --->X  -\
        |             |     /       |     /       |              |    |
        |         Mult_H---/    Mult_H---/    Mult_H--       Mult_H   |
        |                                                        |     = GHASH_H
        |                                            (Bits_num)->X    |
        |                                                        |    |
        |                                                    Mult_H -/
        |                                                        |
        \------------------------------------------------------->X
                                                                 |
                                                              AuthTag
Блоки открытого текста суммируются (XOR) с ключевым потоком, что даёт блоки шифротекста. Эти блоки формируют входные данные функции Mult, которая, в итоге, замыкает всю конструкцию, включая открытую часть сообщения (связанные данные A), и вычисляет код аутентификации (AuthTag). Обратите внимание, что в алгоритме вычисления AuthTag также учитывается длина сообщения. Для работы Mult нужен секретный ключ H, который вычисляется при помощи шифрования нулевого блока (блока, состоящего из байтов со значением 0).

На схеме (справа) показаны два условных уровня, соответствующих базовым функциям GCM: GCTR - это, фактически, режим счётчика; GHASH - это специальная хеш-функция с ключом, предназначенная для вычисления кода аутентификации. Как указано выше, основа GCTR - операция incr (или incs в стандарте NIST). Данная операция устроена достаточно просто: она увеличивает на единицу заданное количество (s) младших битов блока счётчика, рассматривая данные биты как двоичное представление целого числа (>=0), старшие биты остаются нетронутыми. GHASH основана на умножении значений (битовых блоков) в конечном поле (разрядность которого соответствует разрядности GCM - 128 бит).

Для работы GCM требуется инициализирующий вектор IV, этот вектор представляет собой число, используемое с данным ключом только один раз (то есть, это nonce) - строго говоря, стандарт требует, чтобы вероятность повторного использования на разных входных данных не превышала 232. Таким образом, на входе - вектор инициализации IV, секретный ключ K, открытый текст PT, подлежащий шифрованию, связанные данные A. На выходе - шифротекст C и код аутентификации AuthTag.

Перед расшифрованием должен быть проверен код аутентификации. Проверка возможна, так как алгоритм вычисления кода использует только открытую часть защищённого сообщения: шифротекст и связанные данные. Для проверки необходимо знать секретный ключ. Если проверка прошла успешно, то расшифрование данных производится аналогично режиму счётчика - генерируется ключевой поток и, при помощи XOR, вычисляется открытый текст. При этом код аутентификации проверяется до попытки расшифрования. Результатом работы функции расшифрования может быть либо открытый текст, либо символ FAIL (⊥), обозначающий невозможность расшифрования.

Режим GCM может быть использован с любым блочным шифром подходящей разрядности блока (128 бит, в действующем стандарте). Однако в современном Интернете этот режим в большинстве случаев встречается вместе с шифром AES, в составе шифронаборов AES_GCM. Необходимо ещё раз отметить, что в AES возможны разные длины ключей (128,192,256 бит), но размер блока всё равно остаётся равным 128 битам.

Длины ключей и сочетание криптосистем

С шифронаборами в TLS связано несколько криптографических примитивов, к которым применимо понятие разрядности. Возьмём в качестве примера шифронабор TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - здесь есть разрядность ключа RSA (рекомендуемое значение - 2048 бит или более); разрядность симметричного ключа AES (указано 128 бит) и, наименее важная, разрядность хеш-функции (256 бит). Есть ряд рекомендаций по выбору разрядности ключей для криптосистем в TLS. Так, максимальная степень защиты должна применяться к сеансовым данным, потому что ради их защиты TLS и используется. Эти данные защищает симметричный шифр. 128 бит для AES считаются достаточными, 256 бит - обеспечат защиту уже на грани разумного. Большие длины ключей для добротного симметричного алгоритма не имеют никакого смысла. В качестве симметричного шифра может служить не только AES, но и CAMELLIA, и даже корейский шифр ARIA. В теории, возможно и использование шифров ГОСТ для TLS, однако, к сожалению, с ними есть немалые проблемы в реализации программного обеспечения, кроме того, они не входят в типовой список IANA.

Криптосистемы с открытым ключом служат в TLS для аутентификации. Заметьте, что задача выработки общего секрета - эквивалентна аутентификации узла, с которым устанавливается соединение. Действительно, при использовании слабого ключа аутентификации, атакующая сторона может перехватить сессию, выдав себя за сервер (например). Однако на практике это означает проведение сложной активной атаки, которая представляет собой менее вероятную угрозу, чем простое прослушивание канала, от которого защищает симметричный шифр. Естественно, перехвативший сессию атакующий уже не будет испытывать проблем со взломом симметричного шифра. Тем не менее, защита процедуры аутентификации нередко перемещается на второе по важности место, после защиты потока данных.

Хорошим примером, почему это происходит, является следующее наблюдение (подсказанное Z.T. в комментариях на dxdt.ru): если вы испльзуете нестойкий алгоритм для аутентификации узла, то, в обозримой перспективе, например, через десять лет, этот алгоритм может быть взломан; взломавшая алгоритм сторона получает возможность подделывать сессии, но как атаковать сессию десятилетней давности? Никак. А вот если взломан шифр, защищающий передаваемый трафик, то становится возможным прочитать трафик, записанный десять лет назад. В этом может быть смысл, так как он, возможно, содержит до сих пор актуальные сведения.

Весьма полезной практикой является разумное выравнивание стойкости используемых криптосистем. Например, в подавляющем большинстве случаев не имеет смысла использовать RSA-ключ длиной в 4096-бит, если ваш TLS-сервер всё ещё поддерживает SSLv3, а в качестве симметричного шифра применяет DES с 56-битным ключом.

Основные направления атак на TLS

TLS имеет дефекты на уровне протокола, но гораздо больше дефектов обнаруживается в его реализациях. Например, до сих пор можно нередко встретить поддержку заведомо нестойких, устаревших шифронаборов, использующих ключи длиной менее 128 бит. Иногда встречается и поддержка SSLv2, который давно не является хоть сколь-нибудь защищённым на практике. Использование устаревших протоколов и шифронаборов означает, что, в теории, атакующий может провести понижение уровня защиты до шифра, который может взломать на лету, после чего подделает сообщения Finished, став, тем самым, посредником в сессии. (Посредник может прослушивать весь трафик.) Однако современные браузеры не позволяют использовать заведомо нестойкие сочетания криптосистем и не поддерживают устаревшие версии SSL/TLS, что сводит поверхность для атаки к минимуму. Тем не менее, неверно было бы считать, что проблем здесь нет: существует большое число устаревших клиентских устройств, которые всё ещё поддерживают старые протоколы, по историческим причинам среди них немало важных узлов, например таких, как домашние WiFi-роутеры.

Мы уже упоминали необходимость дополнения данных при использовании блочных шифров, которая приводит к появлению нежелательных криптографических оракулов (такой оракул помогает "угадывать" секретные тексты). Есть целый класс оракулов, которые в англоязычной литературе называются Padding oracle, породивший несколько нашумевших атак. Одно из первых использований оракула, связанного с дополнением данных, относится к 2003 году. Атака, сконструированная Сержем Воденэ (Serge Vaudenay), основывалась на том факте, что реализация протокола SSL возвращала разные сообщения об ошибках в зависимости от того, удалось ли после расшифровки записи обнаружить корректное дополнение, но не совпал код аутентификации (MAC), или корректного дополнения данных обнаружить не удалось. Именно последствия этой атаки были устранены только спустя семь лет в веб-сервере IIS. Видимое "снаружи" различное поведение программной реализации TLS в зависимости от того, были в TLS-записи обнаружены корректные дополнение и MAC или некорректные, - традиционный фундамент для атак, актуальный до сих пор: на нём пострены атаки BEAST, CRIME и POODLE. Эти атаки привели к тому, что сейчас не рекомендуется поддержка SSLv3 (и, естественно, более ранних версий). Шифры, работающие в режиме GCM - а именно, AES, - не используют уязвимого дополнения блоков, соответственно, ликвидируют основу для большинства оракулов.

Отдельный большой класс атак составляют атаки, использующие побочные каналы утечки. Однако они в большей степени связаны с аппаратурой, поэтому мы рассмотрим их позже и за пределами данного текста.

В 2015 году появилась атака Logjam, связанная с использованием устаревших шифронаборов, а именно с протоколом Диффи-Хеллмана в так называемом "экспортном" варианте ("экспортные" шифры относятся к первому периоду криптовойн, к 90-м годам 20 века, когда был запрещён экспорт стойкой криптографии за пределы США, а вместо стойкой экспортировались заведомо нестойкие криптосистемы). Logjam не использует оракулов, но позволяет провести понижение уровня секретности, навязав клиенту и серверу нестойкий вариант DH, на группе малой разрядности (512 бит). Это возможно из-за архитектурного недостатка протокола: предложенные клиентом шифронаборы никак не удостоверяются, при этом клиент принимает любые параметры DH, переданные сервером, если только может их обработать. (В распространённых браузерах проблема была исправлена летом 2015 года.)

Для большого класса добротных, стойких криптосистем, широко используемых сейчас в TLS на практике, возможно восстановление сеансового ключа из записанного трафика, при условии, что атакующая сторона имеет в своём распоряжении секретный серверный ключ (ключ из пары, открытая часть которой указывается в сертификате сервера). Это означает, что получив секретный ключ сервера (не сеансовый!), кто-то может расшифровать накопленные ранее записи TLS-сеансов между клиентом и сервером. Упомянутый ключ может быть раскрыт разными способами: например, его можно скопировать с сервера, если есть доступ, или он может просто оказаться нестойким (такое случается не так редко, как можно подумать). Сгенерированный по протоколу Диффи-Хеллмана сеансовый ключ также может стать известен злоумышленнику, либо в результате активной атаки на уровне канала (как Logjam), либо в результате получения доступа к серверу, где сеансовый ключ может сохраняться достаточно долго, и не обязательно в защищённом хранилище. Очевидно, что если атакующая сторона получила соответствующий сеансовый ключ, то она может раскрыть TLS-трафик. Секретный серверный ключ или SSL-сертификат для этого не требуются.

Возможность выпустить SSL-сертификат для атакуемого домена сама по себе никак не помогает расшифровать TLS-трафик, идущий в этот домен, даже если трафик записывается. Процедура выпуска сертификата не требует передачи секретного ключа в удостоверяющий центр (передаётся только открытый ключ), поэтому у удостоверяющего центра секретного серверного ключа тоже нет, за исключением распространённого сейчас случая, когда заказчик поручает генерацию такого ключа УЦ (что, впрочем, является административной, а не технической, проблемой).

Однако валидный SSL-сертификат, выпущенный для атакуемого домена, позволяет провести незаметную для пользователя атаку типа “человек посередине”. Для этого требуется, чтобы между атакуемым пользователем и сервером существовал управляемый атакующим узел, активно перехватывающий трафик. Этот узел выдаёт себя пользователю за легитимный сервер, предъявляя тот самый валидный сертификат. Пассивное прослушивание канала не позволяет раскрыть TLS-трафик подобным образом – наличие сертификата или секретных ключей УЦ никак тут не помогает.

Перехват TLS-соединения может быть автоматизирован – существуют специальные узлы-прокси (SSL-прокси), которые выполняют такой перехват на лету, в том числе, генерируя нужные сертификаты. В такой прокси должен быть загружен сертификат и секретный ключ, позволяющие подписывать другие сертификаты (например, годится промежуточный сертификат УЦ, выпущенный для этих целей и соответствующий ему секретный ключ). Такой “человек посередине” не работает при наличии некоторых дополнительных мер: например, установление TLS-соединения требует взаимной аутентификации, и перехватывающему узлу недоступен клиентский секретный ключ (либо ключи УЦ, удостоверяющего клиентский ключ); или – пользовательский браузер ведёт реестр отпечатков открытых ключей сервера, которым он доверяет; или – пользователь применяет дополнительные источники сведений о разрешённых ключах и сертификатах, которые недоступны для подмены на перехватывающем узле (таким источником может служить DNS, либо другая база данных).

На практике, для массовых сервисов, возникают другие технические направления для атак, напрямую не связанные с какими-то особенностями TLS.

Так, массовые онлайн-сервисы используют SSL termination: то есть, пользовательский TLS-трафик в зашифрованном виде доходит только до пограничного прокси, где благополучно транслируется в открытый протокол, обычно это HTTP, сответствующий HTTPS, который дальше ходит по внутренним (в логическом, а не техническом смысле) сетям сервиса в открытом виде. Тотальный HTTPS, с ростом числа клиентов, быстро превращается в неподъёмную, плохо масштабируемую технологию, поэтому SSL termination является весьма распространённым решением. Если система инспекции трафика (DPI) находится внутри сетей сервиса, за таким пограничным SSL-прокси, то никакой TLS ей не помешает. Именно так получают доступ к данным платёжных систем и веб-почты специальные службы, располагающие, на законных основаниях, собственным оборудованием DPI за пограничными прокси.

Сеансовые ключи могут экспортироваться сервером наружу, в другие системы, которые, например, осуществляют балансировку нагрузки или "очистку" трафика. В некоторых случаях вообще используются услуги третьей стороны для организации TLS-соединения. Кроме того, внутренний трафик распределённых сервисов с легкостью ходит между узлами и дата-центрами по арендованным у крупных операторов каналам связи в открытом виде, такой трафик может прослушиваться, хотя для пользователя он выглядит как TLS-соединение.

Иными словами: TLS не предоставляет абсолютной защиты, но в подавляющем большинстве сценариев использования Интернета этот протокол делает обмен информацией хорошо защищённым; для повышения скрытности и безопасности, если такое требуется, TLS должен использоваться совместно с другими решениями.

Приложение 1. TLS 1.3 - новая версия протокола

TLS 1.3. Активная работа в рамках IETF над новой версией TLS началась в 2014 году. Одной из причин, повлекших начало разработки нового стандарта, стал резко возросший интерес научно-технического сообщества к архитектуре TLS; также сыграло роль обнаружение нескольких существенных дефектов в самом протоколе, что гораздо опаснее наличия дефектов в конкретных реализациях. С момента публикации RFC TLS 1.2 прошло около шести лет - за это время информационный ландшафт в Интернете успел сильно измениться. RFC TLS 1.3 пока (ноябрь 2016) находится в состоянии черновика (draft). Впрочем, протокол в его экспериментальной версии достаточно подробно проработан и уже поддерживается в некоторых библиотеках, а также в экспериментальных сборках браузеров и на некоторых крупных веб-сервисах. Краткое описание основных особенностей TLS 1.3 я привожу в приложении, так как, несмотря на уверенное продвижение спецификации к финальному документу, говорить о массовой поддержке ещё рано.

Первоначальные ожидания от новой версии TLS включали в себя надежду на то, что протокол станет проще и его приведут к более обозримому виду, сократив перечень логических веток технологии. Дело в том, что подавляющее большинство проблем с TLS связано не с дефектами в протоколе, а с проблемами внедрения: это один из самых сложных протоколов Интернета, при этом он ещё и один из самых "разветвлённых". TLS 1.3 не стал более сложным, но, к сожалению, и каких-то явно заметных сдвигов в сторону упрощения не произошло.

Версия 1.3 - радикально отличается от TLS 1.2, соответственно, от всех предыдущих версий тоже. Эти протоколы не только несовместимы, но и построены на разной инженерной идеологии. Различие тут обусловлено переосмыслением модели угроз, в рамках которой проектируется новый протокол. В версии 1.3, TLS попытались сделать и более быстрым (по крайней мере, потенциально), и более защищённым, и более скрытным. Что касается последнего аспекта, то речь тут идёт о совершенно новом для TLS направлении: сведении к минимуму утечек так называемой метаинформации, то есть, сведений о том, какие сообщения передаются внутри сессии, в каком состоянии сессия находится в данный момент времени. Конечно, TLS, как и ранее, не пытается скрыть сам факт установления соединения, но существенные усилия теперь направлены на то, чтобы даже хорошо оснащённая третья сторона не могла узнать ничего более, кроме как о том, что два узла установили TLS-соединение.

Установление соединения (Handshake)

Схему установления соединения между узлами в TLS 1.3 переработали полностью. Конечно, базовые принципы унаследованы от предыдущих версий: сохранились роли узлов (соединение инициирует клиент), не изменилась последовательность ClientHello - ServerHello, присутствует сообщение-сигнал Finished. Однако на этом сходство заканчивается. В TLS 1.3 число сообщений, передаваемых в открытом виде, сокращено: узлы практически сразу переходят на зашифрованный обмен. Поэтому из схемы установления соединения удален сигнал ChangeCipherSpec - он больше не требуется.

Схема полного Handshake TLS 1.3 выглядит следующим образом (из draft-ietf-tls-tls13-18, октябрь 2016):

       Client                                               Server

ClientHello
+ key_share*
+ pre_shared_key_modes*
+ pre_shared_key*		-------->
                                                            ServerHello
                                                            + key_share*
                                                       + pre_shared_key*
                                                   {EncryptedExtensions}
                                                   {CertificateRequest*}
                                                          {Certificate*}
                                                    {CertificateVerify*}
                                                              {Finished}
                                 <--------           [Application Data*]
{Certificate*}
{CertificateVerify*}
{Finished}                       -------->
[Application Data]               <------->            [Application Data]

Здесь символом '*' отмечены необязательные сообщения (передаются в зависимости от контекста), а в фигурных скобках даны сообщения Handshake, которые передаются в зашифрованном виде. Квадратные скобки обозначают данные полезной нагрузки, которые также передаются в зашифрованном виде, но с использованием другого набора ключей шифрования. Ещё раз обратите внимание: уже первый ответ сервера - содержит зашифрованные данные. В TLS предыдущих версий практически все существенные Handshake-сообщения передаются в открытом виде.

В протоколе установления соединения выделяются три фазы: выработка общего криптографического секрета (ключей), определение параметров соединения, аутентификация (сервера и клиента).

К первой фазе относятся группы ClientHello и ServerHello (а так же их расширения, обозначенные на схеме символом '+'). Результатом обмена сообщениями в первой фазе является получение первого секретного ключа (полученного из handshake_traffic_secret), который будет использован для шифрования последующих сообщений Handshake. Здесь хорошо заметно радикальное отличие от схемы установления соединения предыдущих версий протокола: так, серверный сертификат передаётся в зашифрованном виде. Это означает, что сторона, прослушивающая канал связи, но не имеющая ключей, не сможет определить, что за сертификат был предоставлен сервером (и был ли он предоставлен вообще - истинная длина сообщений может быть скрыта при помощи дополнения их нулевыми байтами, это также определено спецификацией 1.3).

Вторая фаза включает серверные сообщения EncryptedExtensions и, при необходимости аутентификации клиента, CertificateRequest. Новое сообщение - EncryptedExtensions - добавлено в TLS 1.3. Данное сообщение является фактическим аналогом сообщения Extensions предыдущих версий TLS, но передаётся в зашифрованном виде.

В третьей фазе, представленной группами сообщений обоих узлов Certificate, CertificateVerify и Finished, происходит аутентификация сервера и клиента. Как и в предыдущих версиях TLS, аутентификация может быть полностью исключена (анонимный режим), однако типичный сценарий использования подразумевает аутентификацию, по крайней мере, сервера клиентом.

Сравним новую схему с TLS 1.2. Кроме уже упомянутого выше отсутствия сигнала ChangeCipherSpec, отсутствуют сообщения ClientKeyExchange и ServerKeyExchange, а также сообщение ServerHelloDone. При этом параметры, необходимые для построения криптографического контекста, из ClientKeyExchange и ServerKeyExchange перекочевали в расширения ClientHello и ServerHello: это key_share, pre_shared_key_modes и pre_shared_key. Сообщение-сигнал ServerHelloDone (оно в предыдущих версиях имело нулевую длину и обозначало окончание первой партии серверных сообщений Handshake) заменено на серверное Finished. В 1.2 предполагается, что клиент и сервер изначально согласовали открытый криптографический контекст. При первоначальном установлении соединения большая часть сообщений Handshake передаётся в открытом виде, соответственно третья сторона видит существенное количество метаинформации - например, состав серверных ключей, значения подписей. В версии 1.3 стороны сразу же пытаются прийти к защищённому контексту и скрыть подробности установления соединения.

Даже полный формат Handshake (описан выше) в 1.3 оказывается короче на одну итерацию: если взглянуть на предыдущую версию, то после получения ответа сервера (заканчивающегося ServerHelloDone) клиент должен был отправить свою порцию сообщений, завершающуюся Finished, и дождаться ответного серверного Finished, только после этого переходить к передаче полезной нагрузки. В новой версии серверное сообщение Finished приходит в первом же ответе сервера, соответственно, клиент может сразу переходить к отправке полезной нагрузки (после своего Finished, конечно), не дожидаясь ещё одного пакета от сервера. Это экономит время, необходимое для доставки пакетов от клиента к серверу (и, вообще говоря, обратно). Такая же схема была предложена (и реализована) раньше под названием TLS False Start (RFC 7918), в версии 1.3 она вошла в спецификацию самого протокола TLS. Для сервисов, устанавливающих тысячи TLS-соединений в секунду, уменьшение задержки в каждом сеансе на 30-100 мс, соответствующих времени передачи данных, весьма существенно. На стороне клиента такое небольшое ускорение также полезно.

ClientHello 1.3

Среди нововведений версии 1.3 - запрет "пересогласования" соединения (renegotiation). Клиент больше не может отправить сообщение ClientHello внутри открытой TLS-сессии - это приведёт к прекращению сессии, так как является нарушением спецификации. Соответственно, отправка ClientHello возможна только при инициировании новой сессии. Зато на стороне сервера появилось сообщение HelloRetryRequest, которое может быть отправлено в сторону клиента, и означает предложение о повторном согласовании открытия сессии. При помощи HelloRetryRequest сервер может "мягко" сбрасывать несоответствующие требованиям запросы клиентов, ожидая, что в ответ клиент передаст скорректированное сообщение ClientHello.

Это новая для TLS концепция. В частности, HelloRetryRequest позволяет бороться с некоторыми видами активных атак при помощи сookie-сигнала, так как отправка cookie позволяет проверить, что клиент действительно отвечает по адресу, указанному в качестве адреса-источника запроса. Cookie в TLS 1.3 передаются в составе расширения HelloRetryRequest и имеют логическое сходство с известными куки-файлами в HTTP (или с менее известными cookie-сигналами из расширений TCP, где они используются для борьбы с SYN-флудом и другими видами флуда). Cookie представляет собой некоторый идентификатор (тикет), который сервер передаёт клиенту для того, чтобы клиент в следующем сообщении вернул данный тикет (либо вычисленное на его основе значение). Спецификация 1.3 предписывает использование конкретного cookie только в рамках одного TLS-соединения.

Изменения в составе ClientHello коснулись механизма передачи информации о поддерживаемых шифронаборах. В версиях до 1.3, шифронаборы, в большинстве случаев, включают как сами шифры, так и хеш-функции и криптосистемы электронной подписи. В 1.3 симметричные шифры и криптосистемы подписи окончательно разделены, в смысле логики формирования криптографического контекста сессии. При этом, в связке с шифрами осталась хеш-функция. То есть, передаваемый в составе ClientHello список шифронаборов включает только указания на шифры и хеш-функции. Более того, на данном этапе спецификация разрешает использование шифров только в режиме аутентифицированного шифрования. Текущий список шифронаборов невелик: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256. Здесь есть режим GCM, который широко используется с AES сейчас. Есть режим CCM (Counter with CBC-MAC) - это режим счётчика (как и GCM), в котором аутентификация построена на коде CBC-MAC (разновидность кода аутентификации, по схеме построения совпадающая с режимом шифрования CBC). А также потоковый шифр Chacha20, со схемой аутентификации Poly1305 (этот комплект криптопримитивов достаточно широко используется Google, например, он поддерживается браузером Chrome). Все упомянутые криптопримитивы уже используются в TLS на практике, однако схема согласования криптографического контекста в 1.3 делает конкретные шифронаборы несовместимыми с предыдущими версиями TLS.

Аутентифицированное шифрование не требует использования дополнительной хеш-функции для вычисления кода аутентификации сообщения. Однако в TLS согласованная сторонами криптографическая хеш-функция необходима для вычисления ключей симметричного шифрования. Поэтому тип хеш-функции указан в шифронаборе: как видно из приведённого в предыдущем абзаце списка, это SHA-256 и SHA-384 (разновидности SHA-2).

Таким образом, если устанавливается TLS-соединение без использования криптосистем подписи в целях аутентификации, то указание на такие криптосистемы вовсе не передаётся узлами. Соответственно, оказывается невозможным использование сертификатов для аутентификации узлов (в первую очередь, сервера). Если сертификаты используются, то необходимо указание совместимой с ними криптосистемы.

Перечень поддерживаемых криптосистем электронной подписи передаётся в обособленном расширении ClientHello - SignatureScheme. Аналогичное по назначению расширение уже было введено в спецификации TLS 1.2, под названием SignatureAlgorithm. Однако в TLS 1.2 объявляемые в составе ClientHello шифронаборы тоже содержат указание на криптосистему электронной подписи, то есть, присутствует некоторое дублирование. SignatureScheme является развитием данного расширения версии 1.2, и заменяет его.

Спецификация указывает несколько криптосистем: два варианта подписей, базирующихся на задаче RSA, широко распространённую ECDSA и новую криптосистему EdDSA, которая тоже работает на эллиптической кривой. RFC для последней пока что находится в состоянии черновика. EdDSA - довольно прогрессивная криптосистема, построенная на задаче дискретного логарифмирования (схема подписи Шнорра) и работающая в группах точек эллиптических кривых в форме Эдвардса (откуда название). Кривые Эдвардса, при некоторых дополнительных условиях, оказываются очень эффективны при использовании в составе криптосистем электронной подписи. (Интересно, что кривые Эдвардса уже рекомендованы к использованию в российском стандарте электронной подписи ГОСТ Р 34.10-2012.)

ServerHello 1.3

ServerHello - ответное сообщение сервера. На ClientHello сервер может ответить либо ServerHello, либо HelloRetryRequest. По решаемым задачам ServerHello в TLS 1.3 практически полностью совпадает с версией 1.2. При помощи этого сообщения сервер выбирает шифронабор из предложенных клиентом, передаёт данные, необходимые для построения общего криптографического контекста. Однако, так как в новой версии шифрование включается практически сразу, ServerHello обязательно содержит (в составе передаваемых расширений) параметры, позволяющие узлам незамедлительно получить общий секрет, который послужит прообразом симметричного ключа шифрования. Это ключевое отличие от предыдущих версий.

Серверное CertificateVerify

Спецификация TLS 1.3 предписывает серверу, который аутентифицирует себя при помощи сертификата, передавать сообщение CertificateVerify. CertificateVerify подтверждает (и позволяет проверить), что сервер действительно обладает секретным ключом из пары, открытая часть которой указана в серверном сертификате. В предыдущих версиях TLS это сообщение передавал только клиент, в случае своей аутентификации (аутентификация клиентов - до сих пор относительно редко используется в TLS): в зависимости от используемой схемы генерации общего секрета, предполагалось, что только сервер, располагающий секретным ключом, сможет расшифровать переданные клиентом данные, либо о наличии секретного ключа можно судить по подписи на сообщении ServerKeyExchange. В 1.3 сообщения ServerKeyExchange нет. А подтверждение наличия секретного ключа следует сразу за сертификатом. При этом для генерации подписи сервер должен использовать тот или иной алгоритм, указанный в списке SignatureScheme, переданной клиентом.

Шифрование и ключи

Как отмечено выше, в TLS 1.3 сервер практически сразу же переходит на передачу зашифрованных данных. Так, в зашифрованном виде передаются сообщения, следующие за ServerHello. Для того, чтобы операции зашифрования и расшифрования прошли успешно, узлы должны располагать общим симметричным секретом (симметричными ключами). Одним из способов получения такого секрета на ранней стадии установления соединения в TLS 1.3 является расширение key_share сообщения ClientHello, позволяющее узлам воспользоваться протоколом Диффи-Хеллмана (в том или ином варианте). Клиент передаёт в key_share свою часть обмена Диффи-Хеллмана. Сервер отвечает в составе ServerHello (key_share) - что и позволяет узлам получить симметричный секрет. (Для непосредственной генерации набора ключей используется хеш-функция, указанная в составе шифронабора, а также ряд фиксированных параметров, вместе с результатом обмена Диффи-Хеллмана.)

Другой вариант, предусмотренный спецификацией, - использование заранее заданного общего секретного ключа (PSK - Pre-Shared Key). Для его передачи служит расширение ClienHello/ServerHello pre_shared_secret. Клиент передаёт идентификаторы секретов (секретных ключей), из которых сервер выбирает подходящий (вообще говоря, известный серверу) и возвращает выбранный идентификатор клиенту. Узлы могут договориться об общих секретах разными способами: например, соответствующие ключи могут быть распределены независимо от канала TLS; либо клиент получает секрет от сервера в рамках предыдущей сессии TLS. Второй вариант сходен с механизмом сохранения и возобновления сессии из предыдущих версий протокола и служит фундаментом для последующего установления TLS-соединения по сокращённой схеме.

Используется несколько типов симметричных ключей, которые можно распределить по уровням, относительно начала сессии: ключи более глубоких уровней генерируются с использованием ключей более ранних уровней. На первом, самом раннем, уровне находятся ключи, предназначенные для шифрования первого запроса клиента при использовании сохранённого общего секрета (см. ниже пример с сокращённым Handshake). Второй уровень ключей используется в полной схеме установления соединения для шифрования Handshake-сообщений и базируется на секрете, полученном в рамках обмена Диффи-Хеллмана. Следующий уровень, третий, - это собственно сессионные симметричные ключи, которые защищают основной полезный трафик (они порождаются на основе общих секретов - server_traffic_secret, client_traffic_secret). При этом ключи третьего уровня могут периодически обновляться. В предыдущих версиях TLS предусмотрен единственный уровень симметричных ключей, соответствующий уровню защиты полезной нагрузки.

Сокращённый вариант установления соединения (Handshake)

При проектировании TLS 1.3 большое внимание уделялось снижению потерь времени в работе протокола. Основной вклад в задержку по времени вносит установление соединения. Поэтому предусмотрена схема сокращённого установления соединения, которая оказывается даже быстрее сокращённой схемы предыдущих версий. Новая схема носит условное название 0-RTT (Zero Round-Trip Time - нулевая задержка приёма-передачи).

Для использования данной схемы требуется, чтобы стороны заранее согласовали общий секрет (как описано выше). Если такой секрет известен, то клиент может начать соединение отправкой ClientHello, с указанием общего секрета, за которым сразу же следуют данные полезной нагрузки. В случае с HTTPS, такими данными будет, например, запрос HTTP GET - так как это самый распространённый сценарий в работе веб-сервисов. Другими словами, клиент сразу же, не дожидаясь ответа сервера, приступает к отправке запросов уровня приложения. В случае, если сервер успешно принял соединение, он отвечает сообщением ServerHello и другими сообщениями (Certificate и т.д.), заключает фазу открытия сессии сообщением Finished, и сразу же отправляет полезные данные, являющиеся ответом на запрос приложения клиента. В случае HTTPS, это будет HTTP-ответ на клиентский GET-запрос. Несложно заметить, что схема, по задержкам приёма-передачи, полностью эквивалентна обычной схеме HTTP: клиент отправляет запрос и получает ответ на него. При этом часть, относящуюся к TLS, можно считать некоторой дополнительной "обёрткой" над HTTP-схемой, не требующей выделенных сеансов приёма-передачи.

Клиентский запрос в схеме 0-RTT не обладает прогрессивной секретностью: он шифруется не выделенным сеансовым ключом, а общим секретным ключом, полученным в другом сеансе. Соответственно, если ключ будет скомпрометирован, получившая его сторона сможет расшифровать клиентскую часть Handshake из записанного ранее трафика. Этот аспект относится только к клиентскому запросу, так как дальнейший трафик приложения шифруется с использованием сессионных ключей, которые уже могут быть сгенерированы с обеспечением прогрессивной секретности. По этой и раду других причин, схема 0-RTT в целом считается менее безопасной (это отмечено в спецификации TLS).

Строгое определение криптографических параметров

Следующим радикальным изменением в TLS 1.3 является фиксирование основных криптографических параметров криптосистем, используемых для получения сеансового секрета. Cпецификация прямо запрещает устаревшую схему передачи сеансового секрета при помощи шифрования RSA (так называемая статическая RSA-схема). Допускаются только варианты протокола Диффи-Хеллмана. При этом для данных протоколов спецификация определяет базовые параметры: модули (для классического варианта) и эллиптические кривые (они были зафиксированы и ранее). Стороны могут использовать сеансовые открытые ключи Диффи-Хеллмана, но должны выбирать группы из перечня, указанного в спецификации - передать собственный модуль, как это было возможно в предыдущих версиях TLS, нельзя.