Процедуры, диагностики и безопасность в Интернет

Протокол TLS

Разбить на страницы
Показывать лекцию целиком

Введение

Первоначальной целью протокола TLS (Transport Layer Security, RFC-2246) является обеспечение конфиденциальности и целостности данных при коммуникации двух приложений. Протокол имеет два уровня: протокол записей TLS и протокол диалога TLS. На нижнем уровне, работающем поверх надежного транспортного протокола (напр., TCP [TCP]), размещается протокол записей TLS. Этот протокол обеспечивает безопасность соединений, которые имеют два основных свойства.

  • Соединение является конфиденциальным. Для шифрования данных используется симметричная криптография (напр., DES [DES], RC4 [RC4], и т.д.). Ключи для шифрования генерируются независимо для каждого соединения и базируются на секретном коде, получаемом с помощью другого протокола (такого, как протокол диалога TLS ). Протокол записей может использоваться и без шифрования.
  • Соединение является надежным. Процедура передачи сообщения включает в себя проверку целостности с помощью вычисления MAC. Для расчета MAC используются хэш-функции (напр., SHA, MD5 и т.д.). Протокол записей может работать и без MAC, но в этом режиме он применяется только в случае, когда другой протокол использует протокол записей в качестве транспортного при выяснении параметров безопасности.
  • Протокол записей TLS используется для инкапсуляции различных протоколов высокого уровня. Один из таких инкапсулируемых объектов, протокол диалога TLS, позволяет серверу и клиенту аутентифицировать друг друга и согласовать алгоритм шифрования и крипто-ключи, до того как приложение передаст или примет первый байт информации. Протокол диалога TLS обеспечивает безопасное соединение, которое имеет три базовых свойства.

  • Идентичность партнеров может быть выяснена с использованием асимметричной криптографии (например, RSA [RSA], DSS [DSS] и т.д.). Эта аутентификация может быть сделана опционной, но она необходима, по крайней мере, для одного из партнеров.
  • Выявление общего секретного кода является безопасным: этот секретный код недоступен злоумышленнику, даже если он ухитрится подключиться к соединению.
  • Диалог надежен: атакующий не может модифицировать соединение без того, чтобы быть обнаруженным партнерами обмена.
  • Одним из преимуществ TLS является то, что он не зависит от протокола приложения. Протоколы верхнего уровня могут размещаться поверх протокола TLS прозрачным образом. Стандарт TLS, однако, не специфицирует то, как протоколы увеличивают безопасность с помощью TLS ; решение о том, как инициализировать TLS -диалог и как интерпретировать сертификаты аутентификации, оставляется на усмотрение разработчиков протоколов и программ, которые работают поверх TLS.

    Цели

    Целями протокола TLS в порядке приоритетности являются:

  • Криптографическая безопасность. TLS должен применяться для установления безопасного соединения между двумя партнерами.
  • Совместимость. Независимые программисты должны быть способны разрабатывать приложения, использующие TLS, которые смогут успешно обмениваться криптографическими параметрами без знания особенностей программ друг друга.
  • Расширяемость. TLS ищет способ, как при необходимости встроить в систему новые ключи и методы шифрования. Здесь имеются две побочные цели: исключить необходимость создания нового протокола (что может быть сопряжено с введением новых слабых мест) и сделать ненужным внедрение новой библиотеки, обеспечивающей безопасность.
  • Относительная эффективность. Криптографические операции требуют больших мощностей ЦПУ, особенно этим славятся операции с открытыми ключами. По этой причине протокол TLS имеет опционную схему кэширования сессии, что позволяет уменьшить число соединений, устанавливаемых с использованием новых временных буферов.
  • Язык представления

    Представление данных в этом документе напоминает синтаксис языка Си и XDR [XDR], но эти параллели достаточно приблизительны и не имеют никакого отношения к самому протоколу TLS. Эти представления применены лишь для целей упрощения восприятия материала.

    Базовым блоком данных считается один байт (т.e. 8 бит). Многобайтовые информационные элементы представляют собой объединение последовательности байтов слева направо и сверху вниз. Многобайтовые элементы извлекаются из байтового потока (используя нотацию Си) следующим образом:

    value = (байт[0] << 8*(n-1)) | (байт[1] << 8*(n-2)) | ... | байт[n-1];

    Этот порядок байтов для многобайтовых последовательностей является стандартным для сетей (big endian).

    Комментарии начинаются с "/*" и завершаются "*/". Опционные компоненты выделяются с помощью помещения их в двойные квадратные скобки "[[ ]]". Однобайтовые объекты, содержащие не интерпретируемые данные, имеют непрозрачный тип (opaque).

    Вектор (одномерный массив) является потоком однородных информационных элементов. Размер вектора может быть специфицирован во время документирования или оставаться не специфицированным вплоть до начала работы. В любом случае длина определяет число байтов, а не число элементов в векторе. Синтаксис спецификации нового типа T', который является вектором фиксированной длины типа T, имеет вид TT'[n] ;

    Здесь T' занимает в информационном потоке n байт, где n кратно размеру T. Длина вектора не включается в кодированный поток.

    В следующем примере Datum определен как три последовательные байта, которые не интерпретируются протоколом, в то время как Data представляет собой три вектора Datum, состоящие из девяти байт.

    opaque Datum[3];    /* три не интерпретируемые байта */
    Datum Data[9];      /* 3 последовательных 3-байтовых вектора */

    Векторы переменной длины определяются путем спецификации субдиапазона легальных длин, используя нотацию <floor..ceiling>. При кодировании реальная длина предшествует потоку байтов, образующих вектор. Длина имеет форму числа, занимающего столько байт, сколько нужно, чтобы специфицировать максимально возможную длину вектора ( ceiling ). Вектор переменной длины с действительным полем длины равным нулю является пустым вектором.

    T T' <floor..ceiling>;

    В следующем примере, обязательным является вектор, который должен содержать от 300 до 400 байт непрозрачного типа. Он не должен быть пустым. Поле действительной длины занимает два байта, uint16, достаточных, чтобы представить значение 400. С другой стороны, longer может представить до 800 байт данных, или 400 uint16 элементов, и может быть пустым. Его кодовое представление будет включать два байта поля реальной длины, за которым будет следовать вектор. Длина закодированного вектора должна быть четной, кратной длине одиночного элемента (например: 17-байтовый вектор uint16 будет нелегальным ).

    opaque mandatory<300..400>;  
        /* поле длины имеет 2 байта, не может быть пустым */
    uint16 longer<0..800>;       
       /* 0 - 400 16-битовое целое число без знака */

    Базовый числовой тип данных представляет собой байт без знака ( uint8 ). Все более длинные типы цифровых данных образуются из фиксированной последовательности байт без знака, объединенных вместе. Следующие числовые типы являются предопределенными.

    uint8 uint16[2];
    uint8 uint24[3];
    uint8 uint32[4];
    uint8 uint64[8];

    Все значения здесь и в дальнейшем записываются в сетевом порядке (big-endian); uint32 представленное шестнадцатеричными байтами 01 02 03 04 эквивалентно десятичному значению 16909060.

    Еще одним типом данных является enum ( enumerated ). Поле типа enum предполагает, что величина декларирована при определении. Каждое определение дает новый тип. Только нумерованные элементы того же типа могут присваиваться и сравниваться. Каждому нумерованному элементу должно быть присвоено значение, как это показано в следующем примере. Так как нумерованные элементы неупорядочены, им может быть присвоено любое уникальное значение в любом порядке.

    enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;

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

    enum { red(3), blue(5), white(7) } Color;

    Можно опционно специфицировать значение без ассоциированной с ним метки, чтобы задать ширину без определения избыточного элемента. В следующем примере Taste в потоке данных занимает два байта, но может предполагать значения 1, 2 или 4.

    enum { sweet(1), sour(2), bitter(4), (32000) } Taste;

    Имена элементов нумерации собраны в пределах определенного типа. В первом примере полная ссылка на второй элемент будет выглядеть как Color.blue. Такое описание не требуется, если объект присвоения ( target ) хорошо специфицирован.

    Color color = Color.blue;  
       /* чрезмерная спецификация, допустимо */
    Color color = blue;         
       /* правильно, тип задан неявно */

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

    enum { low, medium, high } Amount;

    Типы структуры могут быть сформированы для удобства из примитивных типов. Каждая спецификация декларирует новый, уникальный тип. Синтаксис определения весьма похож на используемый в Си.

    struct {
      T1 f1;
      T2 f2;
      ...
      Tn fn;} [[T]];

    Поля в структуре могут быть квалифицированы, используя имена типов, которые синтаксис подобный нумерованным элементам. Например, T.f2 относится ко второму полю предыдущей декларации. Структурные определения допускают вложения.

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

    struct {
      T1 f1;
      T2 f2;
      ....
      Tn fn;
      select (E) {
      case e1: Te1;
      case e2: Te2;
      ....
      case en: Ten;
      } [[fv]];} [[Tv]];

    Например:

    enum { apple, orange } VariantTag;
    struct {
        uint16 number;
        opaque string<0..10>; /* переменная длина */
        } V1;
    struct {
        uint32 number;
        opaque string[10]; /* фиксированная длина */
        } V2;
    struct {
        select (VariantTag) { /* value of selector is implicit */
        case apple: V1; /* VariantBody, tag = apple */
        case orange: V2; /* VariantBody, tag = orange */
        } variant_body; /* optional label on variant */
        } VariantRecord;

    Структуры варианта могут быть подготовлены (сужены) путем спецификации значения селектора до спецификации типа. Например: orange VariantRecord является суженным типом для VariantRecord, содержащего variant_body типа V2.

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

    Алгоритм цифровой подписи включает в себя однопроходные хэшфункции, служащие для преобразования подписываемого текста. Элемент с цифровой подписью кодируется как непрозрачный вектор <0..216-1>, где длина специфицируется алгоритмом подписи и ключом.

    В подписи RSA, 36-байтовая структура двух хэшей (один SHA и один MD5 ) кодируется с помощью секретного ключа. Описание кодировки смотри в [PKCS1].

    В DSS, 20 байтов хэша SHA передаются непосредственно алгоритму цифровой подписи DSA (Digital Signing Algorithm) без дополнительного хэширования. В результате получаются числа r и s. Подпись DSS представляет собой непрозрачный вектор, содержимое которого представляет собой результат DER-кодирования:

    DssSigValue ::= SEQUENCE { r INTEGER,
                 s INTEGER}

    При поточном шифровании исходный текст сначала объединяется с псевдослучайным кодом идентичной длины (формируется специальным генератором) с помощью операции исключающее ИЛИ.

    При использовании блочного шифра каждый блок исходного текста преобразуется в зашифрованный кодовый блок той же длины. Все блочные шифрования выполняются в режиме CBC (Cipher Block Chaining), и все зашифрованные блочные элементы будут иметь размер, кратный длине шифрового блока.

    При шифровании с использованием общедоступного ключа алгоритм открытого ключа используется для шифрования данных так, чтобы их можно было дешифровать только с помощью секретного ключа, который образует пару с открытым ключом. Элемент, зашифрованный с помощью открытого ключа, выглядит как непрозрачный вектор <0..216-1>, где длина определяется алгоритмом подписи и ключом.

    В следующем примере:

    stream-ciphered struct {
           uint8 field1;
           uint8 field2;
           digitally-signed opaque hash[20];} UserType;

    содержимое хэша передается алгоритму подписи, затем вся структура шифруется с привлечением поточного шифра. Длина этой структуры в байтах будет равна 2 байтам для поля field1 и field2, плюс два байта для длины подписи, плюс длина выходных данных алгоритма подписи. Это определено благодаря тому факту, что алгоритм и ключ для подписи известны до кодирования или декодирования этой структуры.

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

    Например,

    struct { uint8 f1; uint8 f2;} Example1;
    Example1 ex1 = {1, 4};     /* assigns f1 = 1, f2 = 4 */

    HMAC и псевдослучайные функции

    Ряд операций на уровне записей и диалога требуют ключевого MAC; это дайджест определенных данных, защищенных секретным кодом. Фальсификация MAC невозможна без знания секретного кода. Конструкция, которая используется для этой операции, имеет название HMAC и описана в [HMAC].

    HMAC может использоваться с разными хэш-алгоритмами. TLS использует ее при диалоге с другими алгоритмами: MD5 и SHA-1, обозначая их как HMAC_MD5(secret, data) и HMAC_SHA(secret, data). Для других шифровых наборов и защищенных данных могут быть определены дополнительные хэш-алгоритмы, но в данной версии протокола для целей диалога жестко заданы MD5 и SHA-1.

    Кроме того, необходима схема расширения применения секретных кодов ( secret ) на блоки данных с целью генерации ключей и валидации. Такая псевдослучайная функция ( PRF ) применяет в качестве входной информации секретный код, порождающий код ( seed ) и идентификационную метку ( label ). При этом формируется выходной массив произвольной длины.

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

    Сначала, определена функция разложения данных, P_hash(secret, data), которая задействует одну хэш функцию для распространения секретного кода на произвольное число выходов:

    P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                     HMAC_hash(secret, A(2) + seed) +
                     HMAC_hash(secret, A(3) + seed) + ...

    где + обозначает объединение.

    A() определено как:

    A(0) = seed
    A(i) = HMAC_hash(secret, A(i-1))

    Для требуемого качества данных P_hash может итерироваться столько раз, сколько нужно. Например: если P_SHA-1 использовался для формирования 64 байт данных, его следует итерировать четыре раза (до A(4) ), создавая 80 байт выходных данных; последние 16 байт последней итерации будут отброшены, оставляя 64 байта.

    PRF TLS создана путем расщепления секретного кода на две части и использования одной половины для генерации данных с помощью P_MD5, а другой половины — для формирования данных посредством P_SHA-1, выходные данных этих двух процедур объединяются затем с помощью операции исключающего ИЛИ.

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

    L_S = длина секретного кода в байтах;
    L_S1 = L_S2 = ceil(L_S / 2);

    PRF определяется как результат смешения двух псевдослучайных потоков с помощью операции исключающее ИЛИ.

    PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);

    Метка представляет собой ASCII-строку. Она должна быть включена в исходном виде без байта длины или завершающего нуля. Например: метка "slithy toves" будет представлена в виде:

    73 6C 69 74 68 79 20 74 6F 76 65 73

    Заметим, что, так как MD5 выдает на выход 16 байт, а SHA-1 — 20 байт, границы их внутренних итераций не будут выровнены; чтобы сформировать на выходе 80 байт, P_MD5 осуществит итерации до A(5), в то время как P_SHA-1 - до A(4).

    Протокол записей TLS

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

    Четыре протокола описаны в данном документе: протокол диалога, протокол уведомления, протокол спецификации изменения шифра, и прикладной информационный протокол. Чтобы позволить расширение протокола TLS, разрешена поддержка протоколом дополнительных типов записей. Любые новые типы записей должны размещать значения типа немедленно за величинами ContentType четырех типов, описанных здесь. Если реализация TLS получает рекорд нераспознаваемого типа, она должна его игнорировать. Любой протокол, предназначенный для использования поверх TLS, должен быть тщательно сконфигурирован, чтобы противостоять любым атакам. Заметим, что из-за того, что тип и длина записи не защищены шифрованием, следует принимать меры, чтобы минимизировать трафик анализа этих величин.

    Состояния соединений

    Состояние соединения TLS является операционной средой протокола записей TLS. Оно специфицирует алгоритмы сжатия, шифрования и MAC. Кроме того, известны параметры этих алгоритмов: секретный код MAC, а также ключи шифрования и IV (вектор инициализации) соединения для направлений чтения и записи. Логически существует четыре состояния соединения: текущие состояния чтения и записи и отложенные состояния чтения и записи. Все записи обрабатываются в текущих состояниях чтения или записи. Параметры безопасности для отложенных состояний могут быть установлены протоколом диалога TLS. Протокол диалога может селективно переводить любое отложенное состояние в текущее, при этом соответствующее текущее состояние становится отложенным. Не допускается формировать состояние, которое не инициализировано с учетом параметров безопасности текущего состояния. Исходное текущее состояние всегда специфицировано без компрессии, шифрования или MAC. Параметры безопасности для состояния чтения и записи соединения TLS задаются путем определения следующих величин (таблица 16.1).

    Эти параметры определены в языке представления в виде:

    enum { server, client } ConnectionEnd;
    enum { null, rc4, rc2, des, 3des, des40 } BulkCipherAlgorithm;
    enum { stream, block } CipherType;
    enum { true, false } IsExportable;
    enum { null, md5, sha } MACAlgorithm;
    enum { null(0), (255) } CompressionMethod;
    /* Алгоритмы, специфицированные в CompressionMethod,
    BulkCipherAlgorithm и MACAlgorithm могут быть добавлены. */
    struct { ConnectionEnd       entity;
             BulkCipherAlgorithm bulk_cipher_algorithm;
             CipherType          cipher_type;
             uint8               key_size;
             uint8               key_material_length;
             IsExportable        is_exportable;
             MACAlgorithm        mac_algorithm;
             uint8               hash_size;
             CompressionMethod   compression_algorithm;
             Opaque              master_secret[48];
             Opaque              client_random[32];
             Opaque              server_random[32];} SecurityParameters;
    Конец соединения Клиент или сервер участник соединения.
    Алгоритм массового шифрования Алгоритм, используемый для массового шифрования. Эта спецификация включает размер ключа алгоритма, степень секретности ключа, является ли этот шифр блочным или поточным, размер блока и является ли шифр экспортным.
    Алгоритм MAC Алгоритм аутентификации сообщений. Эта спецификация включает размер хэша, который возвращается алгоритмом MAC.
    Алгоритм сжатия Алгоритм сжатия данных. Эта спецификация должна включать всю информацию, необходимую для выполнения компрессии.
    Секретный код сервера (master secret) 48-байтовый секретный код, общий для обоих партеров соединения.
    Случайный код клиента 32-байтный код, предоставляемый клиентом.
    Случайный код сервера 32-байтный код, предоставляемый сервером.

    Уровень записей будет использовать параметры безопасности для формирования следующих шести объектов:

  • Секретный код MAC записи клиента
  • Секретный код MAC записи сервера
  • Ключ записи клиента
  • Ключ записи сервера
  • IV записи клиента (только для блочного шифра)
  • IV записи сервера (только для блочного шифра)
  • Параметры записи клиента используются сервером при получении и обработке записей и наоборот. Раз параметры безопасности определены и ключи сформированы, состояния соединения могут быть в любой момент реализованы путем перевода их в текущее состояние. Эти текущие состояния должны актуализоваться после обработки записи. Каждое состояние соединения включает в себя следующие элементы:

    Состояние сжатия Текущее состояние алгоритма сжатия.
    Состояние шифра Текущее состояние алгоритма шифрования. Оно состоит из текущего ключа для данного соединения. Кроме того, для блочного шифра, работающего в режиме CBC (единственный режим, специфицированный в TLS ), оно в исходный момент содержит IV для данного состояния соединения и должно актуализоваться, чтобы содержать текст последнего шифрованного или дешифрованного блока. Для поточных шифров оно содержит всю необходимую информацию для продолжения шифрования или дешифрования данных.
    Секретный код MAC Секретный код MAC для данного соединения.
    Номер по порядку Каждое состояние соединения содержит номер по порядку, который поддерживается независимо для состояний чтения и записи. Номер по порядку должен быть установлен равным нулю, как только соединение переведено в активное состояние. Номера по порядку имеют тип uint64 и не могут превышать 264-1. Номер по порядку инкрементируется после прихода каждой записи: в частности, первая запись, передаваемая через некоторое соединение, имеет порядковый номер 0.

    Уровень записей

    Уровень записей TLS получает неинтерпретированные данные от верхних уровней в непустых блоках произвольного размера.

    Уровень записей фрагментирует информационные блоки и превращают их в записи TLSPlaintext, несущие данные в виде последовательностей длиной 214 байтов или меньше. Границы сообщения клиента на уровне записей не сохраняются (т.e., несколько сообщений клиента одного и того же ContentType могут быть объединены в одну запись TLSPlaintext, или одно сообщение может быть фрагментировано).

    struct { uint8 major, minor;} ProtocolVersion;
    enum         { change_cipher_spec(20), alert(21), handshake(22),
                 application_data(23), (255)
                 } ContentType;
    struct { ContentType type;
                 ProtocolVersion version;
                 uint16 length;
                 opaque fragment[TLSPlaintext.length];
                 } TLSPlaintext;
    type Протокол верхнего уровня, использованный для обработки вложенного фрагмента.
    version Версия примененного протокола.
    length Длина (в байтах) следующего TLSPlaintext.fragment. Длина не должна превосходить 214.
    Fragment Прикладные данные. Эти данные прозрачны и обрабатываются как независимые блоки, с которыми должен работать протокол верхнего уровня, специфицированный полем тип.

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

    Все записи сжаты с использованием алгоритма сжатия, определенным состоянием текущей сессии. Всегда имеется активный алгоритм сжатия; однако в исходный момент он определен как CompressionMethod. null. Алгоритм сжатия преобразует структуру TLSPlaintext в структуру TLSCompressed. Функции сжатия инициализируются информацией по умолчанию при переходе соединения в активное состояние.

    Должно использоваться сжатие без потерь, а длина содержимого не может стать больше чем 1024 байт. Если функция восстановления встречает фрагмент TLSCompressed.fragment, длина которого окажется больше 214 байт, она должна выдать уведомление о фатальной ошибке преобразования.

    Struct { ContentType type;       /* то же самое, что и TLSPlaintext.type */
           ProtocolVersion version;  /* то же самое, что и TLSPlaintext.version */
           uint16 length;
           opaque fragment
           [TLSCompressed.length];
           } TLSCompressed;
    length Длина (в байтах) следующего TLSCompressed.fragment. Длина не должна превосходить 214 + 1024.
    Fragment Сжатая форма TLSPlaintext.fragment.

    Операция CompressionMethod.null является идентификационной; ни одно из полей здесь не меняется.

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

    Функции шифрования и MAC преобразуют структуру TLSCompressed в TLSCiphertext. Функции дешифрования выполняют обратную процедуру. MAC записи включает также номер по порядку, чтобы было можно детектировать лишние или повторные сообщения.

    Struct { ContentType type;
           ProtocolVersion version;
           uint16 length;
           select (CipherSpec.cipher_type) {
           case stream: GenericStreamCipher;
           case block: GenericBlockCipher; } fragment;
           } TLSCiphertext;
    type Поле тип идентично TLSCompressed.type.
    Version Поле версия идентично TLSCompressed.version.
    length Длина (в байтах) последующего TLSCiphertext.fragment. Длина не может превосходить 214 + 2048.
    Fragment Зашифрованная форма TLSCompressed.fragment, с MAC.

    Нуль или стандартный поточный шифр

    Поточные шифры (включая BulkCipherAlgorithm.null) преобразуют структуры TLSCompressed.fragment в (или из) структуры TLSCiphertext. fragment.

    stream-ciphered struct { opaque content[TLSCompressed.length];
    opaque MAC[CipherSpec.hash_size];} GenericStreamCipher;

    MAC генерируется как:

    HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
    TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));

    где "+" означает объединение (слияние).

    seq_num Номер по порядку для данной записи.
    hash Алгоритм хэширования, специфицированный в SecurityParameters.mac_algorithm.

    Заметим, что MAC вычисляется до шифрования. Поточный шифр преобразует весь блок, включая MAC. Для поточных шифров, которые не используют вектор синхронизации (такой, как RC4), состояние шифра записи используется в последующих пакетах. Если CipherSuite равен TLS_NULL_WITH_NULL_NULL, шифрование представляет собой операцию идентичного преобразования (т.e., данные не шифруются, а размер MAC равен нулю, что говорит о том, что MAC не применяется). TLSCiphertext.length равна TLSCompressed.length плюс CipherSpec.hash_size.

    Блочный шифр CBC

    Для блочных шифров (таких, как RC2 или DES), функции шифрования и MAC преобразуют структуры TLSCompressed.fragment в блоки структур TLSCiphertext.fragment или обратно.

    block-ciphered struct {
                           opaque content[TLSCompressed.length];
                           opaque MAC[CipherSpec.hash_size];
                           uint8 padding[GenericBlockCipher.padding_length];
                           uint8 padding_length;
                           } GenericBlockCipher;
    Padding Заполнитель, который добавляется, чтобы сделать длину исходного текста кратной длине блочного шифра. Заполнитель может иметь длину вплоть до 255 байт. Длины больше необходимой могут оказаться желательными, для того чтобы блокировать атаки на протокол, базирующийся на анализе длин сообщений. Каждый uint8 в векторе заполняющих данных должен быть заполнен значением длины.
    padding_length Длина заполнения должна быть такой, чтобы общий размер структуры GenericBlockCipher являлся кратным длине блока шифра. Диапазон легальных значений лежит в диапазоне 0-255, включительно. Эта длина специфицирует длину поля заполнителя, исключая само поле padding_length.

    Длина шифрованных данных ( TLSCiphertext.length ) на единицу больше, чем сумма TLSCompressed.length, CipherSpec.hash_size и padding_length.

    Если длина блока равна 8 байт, длина содержимого ( TLSCompressed.length ) равна 61 байту, а длина MAC равна 20 байтам, длина до заполнения составляет 82 байта. Таким образом, длина заполнения по модулю 8 должна быть равна 6, для того чтобы сделать полную длину четной, кратной 8 байтам (длина блока). Длина заполнения может быть 6, 14, 22 и т.д. до 254. Если бы длина заполнения была минимально необходимой (6), заполнитель имел бы 6 байтов, каждый из которых содержал число 6. Таким образом, последние 8 октетов GenericBlockCipher до блочного шифрования были бы xx 06 06 06 06 06 06 06, где xx последний октет MAC.

    Для блочного шифра в режиме CBC (Cipher Block Chaining) вектор инициализации (IV) для первой записи генерируется с другими ключами и секретными кодами, когда параметры безопасности заданы. IV для последующих записей равен последнему блоку шифрованного текста предыдущей записи.

    Протокол записей требует алгоритма для генерации ключей, IV и секретных кодов MAC из параметров безопасности, поставляемых протоколом диалога.

    Мастерный секретный код (master secret) хэшируется в последовательность байтов, которая присваивается секретным кодам MAC, ключам и IV, требуемых текущим состоянием соединения. CipherSpecs требует чтобы клиент и сервер записали секретный код MAC, ключ и IV, которые сформированы из мастерного секретного кода в указанном порядке. Неиспользованные значения остаются пустыми.

    Для генерации ключей вычисляется:

    key_block = PRF(SecurityParameters.master_secret, "key expansion",
    SecurityParameters.server_random + SecurityParameters.client_random);

    до тех пор, пока не будет сформирован выходной код. Затем key_block позиционируется следующим образом:

    client_write_MAC_secret[SecurityParameters.hash_size]
    server_write_MAC_secret[SecurityParameters.hash_size]
    client_write_key[SecurityParameters.key_material_length]
    >server_write_key[SecurityParameters.key_material_length]
    client_write_IV[SecurityParameters.IV_size]
    server_write_IV[SecurityParameters.IV_size]

    Значения client_write_IV и server_write_IV генерируются только для не экспортных блочных шифров. Для экспортируемых блочных шифров векторы инициализации генерируются позже, как это описано ниже. Любой лишний материал key_block отбрасывается.

    Спецификация шифра требует 2 x 24 байтовых ключей, 2 x 20 байтовых секретных кодов MAC, и 2 x 8 байтов IV, для 104 байтов материала ключей.

    Алгоритмы экспортируемого шифрования (для которого CipherSpec.is_exportable равно 'истинно') требуют дополнительной обработки для получения ключей записи, как это показано ниже:

    final_client_write_key =
    PRF(SecurityParameters.client_write_key,
                 "client write key",
                 SecurityParameters.client_random +
                 SecurityParameters.server_random);
        final_server_write_key =
    PRF(SecurityParameters.server_write_key,
                 "server write key",
                 SecurityParameters.client_random +
                 SecurityParameters.server_random);

    Алгоритмы экспортируемого шифрования получают свои IV исключительно из случайных кодов сообщений hello:

    iv_block = PRF("", "IV block", 
       SecurityParameters.client_random + SecurityParameters.server_random);

    Блок iv_block делится на два инициализационных вектора, как это делалось выше для key_block:

    client_write_IV[SecurityParameters.IV_size]
    server_write_IV[SecurityParameters.IV_size]

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

    TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 требует пяти случайных байт для каждого из двух ключей шифрования и 16 байт для каждого ключа MAC, что составляет 42 байта ключевого материала. Выход PRF запоминается в key_block. Блок key_block делится, а ключи записи запоминаются, так как это алгоритм экспортного шифрования.

    key_block                = PRF(master_secret,
                             "key expansion",
                             server_random +
                             client_random)[0..41]
    client_write_MAC_secret  = key_block[0..15]
    server_write_MAC_secret  = key_block[16..31]
    client_write_key         = key_block[32..36]
    server_write_key         = key_block[37..41]
    final_client_write_key   = PRF(client_write_key,
                             "client write key",
                             client_random +
                             server_random)[0..15]
    final_server_write_key   = PRF(server_write_key,
                             "server write key",
                             client_random +
                             server_random)[0..15]
    iv_block                 = PRF("", "IV block", client_random +
                             server_random)[0..15]
    client_write_IV          = iv_block[0..7]
    server_write_IV          = iv_block[8..15]

    Протокол диалога TLS

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

    Протокол диалога ответственен за согласования характеристик сессии, куда входят следующие объекты:

    идентификатор сессии Произвольная последовательность байтов, выбранная сервером для идентификации состояния сессии (активная/ возобновляемая).
    сертификат партнера X509v3 [X509] сертификат партнера. Этот элемент состояния может быть равен нулю.
    метод сжатия Алгоритм, используемый для сжатия информации перед шифрованием.
    спецификация шифра Специфицирует алгоритм массового шифрования (такой как нуль, DES, и т.д.) и алгоритм MAC (такой как MD5 или SHA). Она определяет также криптографические атрибуты, такие, как hash_size.
    мастерный секретный код 48-байтовый секретный код, общий для сервера и клиента.
    'is resumable' Флаг, указывающий, может ли сессия использоваться для инициализации нового соединения.

    Эти объекты применяются затем для определения параметров безопасности уровня записей при защите прикладных данных. Многие соединения могут реализоваться в рамках той же сессии с помощью процедуры возобновления протокола диалога.

    Протокол изменения спецификации шифра

    Протокол изменения спецификации шифра предназначен для оповещения об изменении стратегии шифрования. Протокол использует одно сообщение, которое зашифровано и архивировано в рамках текущего состояния соединения. Сообщение состоит из одного байта со значением 1.

    struct { enum { change_cipher_spec(1), (255) } type;} ChangeCipherSpec;

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

    Протокол оповещения

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

    enum { warning(1), fatal(2), (255) } AlertLevel;
    enum { close_notify(0),
            unexpected_message(10),
            bad_record_mac(20),
            decryption_failed(21),
            record_overflow(22),
            decompression_failure(30),
            handshake_failure(40),
            bad_certificate(42),
            unsupported_certificate(43),
            certificate_revoked(44),
            certificate_expired(45),
            certificate_unknown(46),
            illegal_parameter(47),
            unknown_ca(48),
            access_denied(49),
            decode_error(50),
            decrypt_error(51),
            export_restriction(60),
            protocol_version(70),
            insufficient_security(71),
            internal_error(80),
            user_canceled(90),
            no_renegotiation(100),
    (255)} AlertDescription;
    struct { AlertLevel level; AlertDescription description;} Alert;

    Оповещения закрытия

    Клиент и сервер должны оба знать, что соединение завершается, для того чтобы избежать атаки усечения ( truncation ). Оба партнера могут запустить обмен сообщениями закрытия.

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

    Оба партнера могут инициализировать закрытие, послав уведомление close_notify. Любые данные, полученные после оповещения о закрытии, игнорируются.

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

    Предполагается, что закрытие соединения надежно доставляет все данные, ждущие передачи, прежде чем транспортная система будет блокирована.

    Оповещения об ошибках

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

    unexpected_message Получено непредусмотренное сообщение. Это оповещение является всегда фатальным и не должно встречаться при обменах между корректными реализациями.
    bad_record_mac Это оповещение присылается, если получена запись с неверным MAC. Это сообщение всегда вызывает фатальную ошибку.
    decryption_failed TLSCiphertext дешифрован неверно: либо текст не имел длину, четную и кратную размеру блока, либо их значения (заполнители) при проверке оказались некорректными. Это сообщение всегда вызывает фатальную ошибку.
    record_overflow Получена запись TLSCiphertext, которая имеет длину больше 214+2048 байт, запись дешифрована TLSCompressed в запись с более чем 214+1024 байтов. Это сообщение всегда вызывает фатальную ошибку.
    decompression_failure Функция декомпрессии получила неприемлемые данные (напр., данные, которые после восстановления будут иметь слишком большой объем). Это сообщение вызывает фатальную ошибку.
    handshake_failure Получение сообщения оповещения handshake_failure указывает, что отправитель не мог согласовать приемлемый набор параметров безопасности из числа предлагаемых опций. Это фатальная ошибка.
    bad_certificate Сертификат был поврежден, содержал подписи, которые не прошли проверку, и т.д..
    unsupported_certificate Сертификат имел не поддерживаемый тип.
    certificate_revoked Сертификат был отозван его подписантом.
    certificate_expired Сертификат имеет истекший срок годности или не пригоден по другой причине.
    certificate_unknown Некоторая другая, не специфицированная причина при обработке сертификата, делающая его неприемлемым.
    illegal_parameter Поле при диалоге оказалось вне диапазона допустимых значений или не согласуется с другими полями. Это фатальная ошибка.
    unknown_ca Получена корректная сертификатная последовательность или ее часть, но сертификат не был воспринят из-за того, что CA-сертификата не может быть обнаружен или не согласуется с известным проверенным CA. Это сообщение всегда вызывает фатальную ошибку.
    access_denied Получен правильный сертификат, но при проверке доступа отправитель решил не продолжать согласование. Это сообщение всегда вызывает фатальную ошибку.
    decode_error Сообщение не может быть дешифровано из-за того, что некоторое поле выходит за пределы допустимого или сообщение имеет неверный размер. Это сообщение всегда вызывает фатальную ошибку.
    decrypt_error Диалог криптографической операции не прошел, это может включать неудачу проверки подписи, обмена ключами или контроль завершающего сообщения.
    export_restriction Согласование параметров вошло в противоречие с экспортными регламентациями. Например: попытка передать 1024 битов краткосрочного RSA -ключа для метода диалога RSA_EXPORT. Это сообщение всегда вызывает фатальную ошибку.
    protocol_version Протокольная версия клиента распознана, но не поддерживается. Например, старые версии протокола могут отвергаться по соображениям безопасности. Это сообщение всегда вызывает фатальную ошибку.
    insufficient_security Возвращается вместо handshake_failure, когда согласование не прошло, в частности, из-за того, что сервер требует более секретного шифра, чем может поддержать клиент. Это сообщение всегда вызывает фатальную ошибку.
    internal_error Внутренняя ошибка, не связанная с партнером, или требования протокола не допускают продолжения процедуры (например, ошибка при выделении памяти). Это сообщение всегда вызывает фатальную ошибку.
    user_canceled Этот диалог аннулирован по какой-то причине, не связанной с протокольной ошибкой. Если пользователь аннулирует операцию после завершения диалога, закрытие соединения путем посылки close_notify является более приемлемым.
    no_renegotiation Посылается клиентом в ответ на запрос hello или сервером — в ответ на hello клиента после стартового диалога. Любое из этих сообщений должно, в норме, вызывать повторное согласование параметров. Когда это не приемлемо, получатель должен реагировать посылкой этого уведомления ( alert ). В этой точке отправитель исходного запроса может решить, следует ли сохранять соединение. Случаем, когда это приемлемо, может оказаться ситуация, когда сервер запускает процесс в ответ на запрос. Процесс может получить параметры безопасности (длину ключа, аутентификацию и т.д.) при запуске, и может быть трудно сообщить об изменении этих параметров в этой точке процесса. Это сообщение всегда является предупреждением.

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

    Обзор протокола диалога

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

  • Обмен сообщениями hello, чтобы согласовать алгоритмы, обмен случайными кодами и проверка перезапуска сессии.
  • Обмен необходимыми криптографическими параметрами, чтобы позволить клиенту и серверу согласовать предмастерные секретные коды.
  • Обмен сертификатами и криптографической информацией, чтобы позволить клиенту и серверу аутентифицировать друг друга.
  • Генерация мастерного секретного кода из предмастерного и обмен случайными кодами.
  • Предоставление параметров безопасности уровню записей.
  • Разрешение клиенту и серверу проверить, что их партнер вычислил те же самые параметры безопасности и что диалог прошел без вмешательства хакера.
  • Заметим, что верхние слои не должны слишком полагаться на TLS, всегда согласуя самые безопасные из возможных соединений между партнерами:

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

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

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

    Протокол TLS является безопасным, и любой шифровой набор предлагает свой уровень безопасности. Если вы согласуете использование 3DES с 1024-битовым RSA -ключом при связи с ЭВМ, чей сертификат вы проверили, вы можете быть уверены в безопасности. Однако вы никогда не должны посылать данные по каналу, где применяется 40-битовая шифрование, если только вы не уверены, что данные не стоят того, чтобы кто-то тратил силы на их дешифрование.

    Эти цели достигаются протоколом диалога, который может быть суммирован следующим образом. Клиент посылает сообщение hello, на которое сервер должен также откликнуться сообщением hello, в противном случае возникает ситуация фатальной ошибки и соединение разрывается. Сообщения client hello и server hello используются для установления более безопасного взаимодействия клиента и сервера. Сообщения client hello и server hello устанавливают следующие атрибуты: версия протокола, ID-сессии, шифровой набор и метод сжатия. Кроме того, партнеры генерируют и пересылают друг другу два случайных числа: ClientHello.random и ServerHello.random.

    Реальный обмен ключами подразумевает до четырех сообщений: сертификат сервера, ключевой обмен сервера, сертификат клиента и ключевой обмен клиента. Новые методы ключевого обмена могут быть созданы с помощью спецификации формата для этих сообщений, чтобы позволить клиенту и серверу согласовать использование общего секретного кода. Этот секретный код должен быть достаточно длинным. Современные методы ключевого обмена пересылают коды длиной от 48 до 128 байт.

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

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

    (рис 16.1) Обмен сообщениями в процессе диалога

    * отмечает опционные или зависящие от ситуации сообщения, которые посылаются не всегда.

    Когда клиент и сервер решают возобновить предыдущую сессию или задублировать существующую сессию (вместо согласования новых параметров безопасности), следует обмен следующими сообщениями.

    Клиент посылает .) Если соответствия с ID-сессии не найдено, сервер генерирует новый ID сессии, а клиент TLS и сервер осуществляют полный диалог.

    (рис 16.2) Обмен сообщениями для упрощенного диалога

    Протокол диалога

    Протокол диалога TLS представляет собой один из фиксированных клиентских протоколов высокого уровня (записей) TLS. Этот протокол используется для согласования атрибутов безопасности сессии. Сообщения диалога передаются уровню записей TLS, где они инкапсулируются в одну или более структур TLSPlaintext, которые обрабатываются и передаются так, как это специфицировано текущим состоянием активной сессии.

    enum { hello_request(0), client_hello(1), server_hello(2),
    certificate(11), server_key_exchange (12),
    certificate_request(13), server_hello_done(14),
    certificate_verify(15), client_key_exchange(16),
    finished(20), (255)
    } HandshakeType;
    struct { HandshakeType msg_type; /* тип диалога */
             uint24 length;          /* байтов в сообщении */
             select (HandshakeType) {
             case hello_request: HelloRequest;
             case client_hello: ClientHello;
             case server_hello: ServerHello;
             case certificate: Certificate;
             case server_key_exchange: ServerKeyExchange;
             case certificate_request: CertificateRequest;
             case server_hello_done: ServerHelloDone;
             case certificate_verify: CertificateVerify;
             case client_key_exchange: ClientKeyExchange;
             case finished: Finished; } body;
             } Handshake;

    Сообщения протокола диалога представлены ниже в порядке, в котором они должны быть посланы. Посылка сообщений диалога в неправильном порядке приведет к фатальной ошибке. Ненужные сообщения диалога могут быть опущены. Обратите внимание на одно исключение: сообщение сертификата используется в диалоге дважды (от клиента к серверу, а затем от сервера к клиенту), но оно описано лишь для первого случая его использования. Одно сообщение не привязано к этим правилам порядка обмена, — это сообщение запроса Hello, которое может быть послано в любое время, но которое должно игнорироваться клиентом, если приходит в середине диалога.

    Сообщения Hello

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

    Запрос Hello

    Сообщение-запрос hello может быть послано сервером в любое время. Запрос Hello является простым уведомлением о том, что клиент должен начать согласование параметров путем посылки сообщения client hello. Это сообщение будет проигнорировано клиентом, если он участвует в сессии согласования или если он не хочет заново согласовывать параметры сессии, или клиент может, если хочет, реагировать уведомлением no_renegotiation. Так как сообщения диалога предназначены для осуществления определенных действий над прикладными данными, ожидается, что согласование начнется до того, как будут получены новые записи от клиента. Если сервер посылает запрос hello, но не получает отклика client hello, он может разорвать соединение с фатальным уведомлением.

    После посылки запроса hello серверы не должны повторять запрос до тех пор, пока диалог согласования не завершится.

    Структура этого сообщения: struct { } HelloRequest;

    Это сообщение не должно никогда включаться в хэши сообщений и использоваться в завершающих сообщениях ( finished ), а также в сообщении верификации сертификатов.

    Hello клиента

    Когда клиент инициализирует соединение с сервером, первым должно быть послано сообщение client hello. Клиент может также послать client hello в качестве отклика на запрос hello или по своей собственной инициативе, для того чтобы заново согласовать параметры безопасности существующего соединения.

    Сообщение hello клиента включает в себя случайную структуру, которая позднее используется протоколом.

    struct { uint32 gmt_unix_time; opaque random_bytes[28];} Random;
    gmt_unix_time Текущее время и дата в соответствии со стандартом UNIX в 32-битовом формате (число секунд с момента полуночи 1-го января, 1970, GMT) согласно показаниям внутренних часов отправителя. Часы могут и не быть точно выверены на уровне протокола TLS. Прикладные протоколы могут накладывать дополнительные ограничения.
    random_bytes 28 байт сформированных безопасным генератором случайных чисел.

    Сообщение client hello включает в себя идентификатор сессии переменной длины. Если это поле не пусто, его значение идентифицирует сессию, чьи параметры безопасности клиент желает использовать повторно. Идентификатор сессии может соответствовать какому-то предшествующему соединению, текущему соединению, или другому активному соединению. Вторая опция полезна, если клиент хочет изменить случайные структуры и получить текущие значения параметров соединения, в то время как третья опция делает возможным установить несколько независимых безопасных соединений без повторения всей процедуры протоколы диалога. Эти независимые соединения могут существовать последовательно или одновременно; SessionID становится действенным, когда диалог согласования завершается обменом сообщениями Finished, и сохраняется до тех пор, пока не состарится или пока не произойдет фатальная ошибка. Действительное содержимое SessionID определяется сервером.

    opaque SessionID<0..32>;

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

    Список CipherSuite, передаваемый от клиента серверу в сообщении client hello, содержит комбинации криптографических алгоритмов, поддерживаемых клиентом в порядке их предпочтения (предпочтительный вариант — первый). Каждый CipherSuite определяет алгоритм пересылки ключей, алгоритм массового шифрования (включая длину секретного ключа) и алгоритм MAC. Сервер выберет шифровой набор или, если приемлемого варианта нет, пришлет уведомление об ошибке и прервет соединение.

    uint8 CipherSuite[2]; /* Селектор криптографического набора */

    Hello клиента включает в себя список алгоритмов сжатия, поддерживаемых клиентом, в порядке их предпочтения.

    enum { null(0), (255) } CompressionMethod;
    struct { ProtocolVersion client_version;
           Random random;
           SessionID session_id;
           CipherSuite cipher_suites<2..216-1>;
           CompressionMethod compression_methods<1..28-1>;
           } ClientHello;
    client_version Версия протокола TLS, которой хочет воспользоваться клиент во время сессии. Это должна быть последняя версия (с наибольшим кодом), поддерживаемая клиентом. Для данной спецификации значение версии равно 3.1.
    Random Псевдослучайная структура, генерируемая клиентом.
    session_id ID-сессия, которую клиент хочет использовать для данного соединения. Это поле должно быть пустым, если нет ни одного session_id или клиент хочет выработать новые параметры безопасности.
    cipher_suites Список криптографических опций, поддерживаемых клиентом в порядке предпочтения. Если поле session_id не пусто (запрос восстановления сессии), этот вектор должен включать, по крайней мере, cipher_suite данной сессии.
    compression_methods Список методов сжатия, поддерживаемых клиентом в порядке их предпочтения. Если поле session_id не пусто (запрос восстановления сессии) он должен включать compression_method данной сессии. Этот вектор должен содержать, а все реализации должны поддерживать CompressionMethod.null. Таким образом, клиент и сервер всегда могут согласовать метод сжатия информации.

    После посылки сообщения client hello клиент ждет сообщения server hello. Любое другое сообщение диалога, присланное сервером, рассматривается как фатальная ошибка.

    В интересах прямой совместимости, клиенту разрешено включать в сообщение client hello после методов сжатия дополнительные данные. Эти данные должны быть включены в хэши диалога, в противном случае они игнорируются. Hello — единственное сообщение диалога, для которого это допускается; для всех остальных сообщений объем данных должен точно соответствовать описанию сообщения.

    Hello сервера

    Сервер посылает это сообщение в ответ на сообщение client hello, когда он может найти приемлемый набор алгоритмов. Если он не может сделать приемлемый выбор, он реагирует уведомлением об ошибке диалога.

    Структура этого сообщения:

    Struct { ProtocolVersion server_version;
           Random random;
           SessionID session_id;
           CipherSuite cipher_suite;
           CompressionMethod compression_method;
           } ServerHello;
    server_version Это поле будет содержать самое низкое значение, которое предлагается клиентом в client hello, и наибольшее значение версии, поддерживаемое сервером. Значение версии данной спецификации равно 3.1.
    Random Эта структура генерируется сервером и должна быть отличной от ClientHello.random.
    session_id Идентифицирует сессию, соответствующую данному соединению. Если ClientHello.session_id не пусто, сервер будет искать соответствие с содержимым своего кэша сессий. Если соответствие найдено и сервер хочет установить новое соединение, используя специфицированное состояние сессии, он откликнется тем же значением ID, что было прислано клиентом. Это индицирует возобновляемую сессию. Партнеры должны продолжить обмен сообщениями finished. В противном случае, это поле будет содержать другое значение, идентифицирующее новую сессию. Сервер может вернуть пустое поле session_id, чтобы индицировать, что сессия не кэшируется и, следовательно, не может быть возобновлена. Если сессия возобновлена, она должна использовать тот же шифровой набор, который был согласован ранее.
    cipher_suites Шифровой набор, выбранный сервером из списка в ClientHello.cipher_suites. Для возобновленных сессий это поле несет в себе значение, взятое из состояния возобновляемой сессии.
    Compression_method Алгоритм сжатия, выбранный сервером из списка в ClientHello.compression_methods. Для возобновляемых сессий это поле содержит значение из состояния возобновляемой сессии.

    Сертификат сервера

    Сервер должен посылать сертификат всякий раз, когда согласованный метод обмена ключами не является анонимным. За этим сообщением всегда непосредственно следует сообщение server hello.

    Тип сертификата должен соответствовать выбранному алгоритму обмена ключами шифров, обычно это сертификат X.509v3. Он должен содержать ключ, который соответствует методу обмена ключами. Если не специфицировано обратного, алгоритм подписи для сертификата должен быть тем же, что и алгоритм для ключа сертификата. Если не специфицировано обратного, общедоступный ключ может иметь любую длину.

    Алгоритм обмена ключами Тип сертификата ключа
    RSA Общедоступный ключ RSA ; сертификат должен допускать использование ключа для шифрования.
    RSA_EXPORT Общедоступный ключ RSA с длиной больше чем 512 бит, который может быть использован для подписи, или ключ длиной 512 бит или короче, который может быть использован для шифрования или подписи.
    DHE_DSS Общедоступный ключ DSS.
    DHE_DSS_EXPORT Общедоступный ключ DSS.
    DHE_RSA Общедоступный ключ RSA, который может использоваться для подписи.
    DHE_RSA_EXPORT Общедоступный ключ RSA, который может использоваться для подписи.
    DH_DSS Ключ Diffie-Hellman. Алгоритмом, используемым для подписи сертификата, должен быть DSS.
    DH_RSA Ключ Diffie-Hellman. Алгоритмом, используемым для подписи сертификата, должен быть RSA.

    Все сертификатные профайлы, ключи и криптографические форматы определены рабочей группой IETF PKIX [PKIX]. Когда присутствует расширение применения ключа, бит digitalSignature должен быть установлен для ключа, выбранного для подписи, как это описано выше, а бит keyEncipherment должен присутствовать, чтобы разрешить шифрование, как это описано выше. Бит keyAgreement должен быть установлен для сертификатов Diffie-Hellman.

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

    Структура этого сообщения имеет вид:

    opaque ASN.1Cert<1..224-1>;
    struct { ASN.1Cert certificate_list<0..224-1>; } Certificate;
    certificate_list Это последовательность (цепочка) сертификатов X.509v3. Сертификат отправителя должен быть записан в списке первым. Каждый следующий сертификат должен непосредственно сертифицировать предшествующий сертификат. Так как верификация сертификата требует, чтобы корневые ключи распределялись независимо, самоподписывающий сертификат, который специфицирует корневой источник сертификата, может быть опционно удален из цепочки, в предположении, что партнер должен уже иметь его, чтобы проверить его в любом случае.

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

    Сообщение ключевого обмена сервера

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

  • RSA_EXPORT (если открытый ключ в сертификате длиннее, чем 512 бит)
  • DHE_DSS
  • DHE_DSS_EXPORT
  • DHE_RSA
  • DHE_RSA_EXPORT
  • DH_anon
  • Некорректно посылать сообщение ключевого обмена сервера для следующих методов пересылки ключей:

  • RSA
  • RSA_EXPORT (когда открытый ключ в сертификате сервера короче или равен 512 бит)
  • DH_DSS
  • DH_RSA
  • Это сообщение передает криптографическую информацию, чтобы позволить клиенту оперировать с предмастерным секретным кодом: либо общедоступный ключ RSA, чтобы зашифровать предмастерный секретный код, либо общедоступный ключ Diffie-Hellman, с помощью которого клиент может завершить обмен ключами.

    В качестве дополнительных определены наборы CipherSuites, которые включают в себя новые алгоритмы обмена ключами. Сервер пошлет сообщение обмена ключами тогда и только тогда, когда тип сертификата, ассоциированный с алгоритмов обмена ключами, не предоставил достаточно информации клиенту, чтобы осуществить пересылку предмастерного секретного кода.

    Согласно экспортному закону США, модули RSA больше 512 бит не могут применяться для ключевого обмена в программах, экспортируемых из США. Более длинные ключи RSA, зашифрованные в сертификатах, могут быть использованы для подписи более коротких ключей RSA в случае метода ключевого обмена RSA_EXPORT.

    Структура этого сообщения:

    enum { rsa, diffie_hellman } KeyExchangeAlgorithm;
    struct { opaque rsa_modulus<1..2^16-1>; opaque rsa_exponent
       <1..2^16-1>;} ServerRSAParams;
    rsa_modulus Модуль временного RSA -ключа сервера.
    rsa_exponent Общедоступный показатель временного RSA -ключа сервера.
    struct { opaque dh_p<1..2^16-1>;
    opaque dh_g<1..2^16 1>;
    opaque dh_Ys<1..2^16 1>;} ServerDHParams; /* Временные DH параметры */
    dh_p Простой модуль, используемый для операции Diffie-Hellman.
    dh_g Генератор, используемый для операции Diffie-Hellman.
    dh_Ys Общедоступное значение (gX mod p) метода Diffie-Hellman для сервера.
    struct { select (KeyExchangeAlgorithm) {
              case diffie_hellman:
              ServerDHParams params;
              Signature signed_params;
              case rsa:
              ServerRSAParams params;
              Signature signed_params; };
              } ServerKeyExchange;
    Params Параметры ключевого обмена сервера.
    signed_params Для неанонимных ключевых обменов. Хэш соответствующих значений параметров с подписью, согласованной с примененным хэшем.
    md5_hash MD5(ClientHello.random + ServerHello.random + ServerParams);
    sha_hash SHA(ClientHello.random + ServerHello.random + ServerParams);
    enum { anonymous, rsa, dsa } SignatureAlgorithm;
    select (SignatureAlgorithm)
             { case anonymous: struct { };
             case rsa:
             digitally-signed struct
             {
             opaque md5_hash[16];
             opaque sha_hash[20];
             };
             case dsa:
             digitally-signed struct {
             opaque sha_hash[20];
             };
             } Signature;

    Запрос сертификата

    Не анонимный сервер может опционно запросить сертификат от клиента, если это возможно для выбранного шифрового набора. За этим сообщением, если оно послано, непосредственно следует сообщение ключевого обмена сервера ( Server Key Exchange ) (в противном случае, сообщение сертификата сервера).

    Структура этого сообщения:

    enum { rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), 
       dss_fixed_dh(4), (255)} ClientCertificateType;
    opaque DistinguishedName<1..216-1>;
    struct { ClientCertificateType certificate_types<1..28-1>;
    DistinguishedName certificate_authorities<3..216-1>;
    } CertificateRequest;
    certificate_types Это поле представляет собой список типов запрошенных сертификатов, расположенных в порядке предпочтения сервера.
    certificate_authorities Список имен приемлемых провайдеров сертификатов. Эти имена могут специфицировать уникальное имя корневого или подчиненного CA. Таким образом, это сообщение может быть использовано как для описания известных корней и желательного пространства авторизации.

    DistinguishedName получается из [X509]. Считается фатальной ошибкой (оповещение handshake_failure), если анонимный сервер запрашивает идентификацию клиента.

    Hello done сервера

    Сообщение сервера hello done посылается сервером, чтобы индицировать завершение операций с hello сервера и связанных с ним сообщений. После отправки этого сообщения сервер ждет отклика клиента.

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

    После получения сообщения сервера hello done клиент должен проверить, что сервер предоставил корректный сертификат, если это требуется, и что параметры, присланные сервером, приемлемы.

    Структура этого сообщения: struct { } ServerHelloDone;

    Сертификат клиента

    Это первое сообщение, которое может послать клиент после получения сообщения от сервера hello done. Это сообщение посылается только в случае запроса присылки сертификата со стороны сервера. Если приемлемого сертификата нет, клиент должен послать пустое сообщение сертификата. Если серверу для продолжения диалога требуется аутентификация клиента, он может откликнуться, послав уведомление о фатальной ошибке.

    Сообщение обмена ключами клиента

    Это сообщение непосредственно следует за сообщением сертификата клиента (если оно посылается). В противном случае оно будет первым сообщением, посылаемым клиентом после получения сообщения сервера hello done.

    Предмастерный секретный код устанавливается с помощью этого сообщения, либо путем прямой его передачи, зашифровав с применением RSA, либо с помощью передачи параметров Diffie-Hellman, которые позволят каждой из сторон согласовать применение одного и того же предмастерного секретного кода. Когда в качестве метода передачи ключей использован DH_RSA или DH_DSS, запрашивается сертификация клиента, и клиент может откликаться посылкой сертификата, который содержит общедоступный ключ Diffie-Hellman, чьи параметры (группа и генератор) соответствуют, специфицированным в сертификате сервера. Это сообщение не содержит никаких других данных.

    Структура этого сообщения:

    struct { select (KeyExchangeAlgorithm) 
      { case rsa: EncryptedPreMasterSecret;
    case diffie_hellman: ClientDiffieHellmanPublic; } exchange_keys;
    } ClientKeyExchange;

    Сообщение зашифрованного RSA предмастерного секретного кода

    Если для согласования ключей и аутентификации применен алгоритм RSA, клиент генерирует 48-байтовый предмастерный секретный код, шифрует его с помощью общедоступного ключа из сертификата сервера или временного RSA -ключа, переданного в сообщении ключевого обмена сервера, и посылает результат в сообщении зашифрованного предмастерного секретного кода (encrypted premaster secret). Эта структура является вариантом сообщения ключевого обмена клиента.

    Структура этого сообщения:

    struct { ProtocolVersion client_version; opaque random[46];} 
      PreMasterSecret;
    client_version Последняя (новейшая) версия, поддерживаемая клиентом. Она используется для детектирования атак связанных с понижением номера версии. После получения предмастерного секретного кода сервер должен проверить, что данное значение согласуется с величиной, переданной клиентом в сообщении hello.
    random 46 байт псевдослучайного кода.
    struct{public-key-encrypted PreMasterSecret pre_master_secret;} 
       EncryptedPreMasterSecret;

    Атака, рассмотренная Даниэлем Блайхенбахером (Daniel Bleichenbacher) [BLEI], может быть предпринята против TLS -сервера, который использует PKCS#1, закодированный с помощью RSA.

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

    pre_master_secret Это случайное число генерируется клиентом и используется для формирования мастерного секретного кода.

    Общедоступный Diffie-Hellman-ключ клиента

    Эта структура передает общедоступную величину ( Yc ) алгоритма Диффи-Хелмана для клиента, если она не была уже включена в сертификат клиента. Шифрование, используемое для Yc, определяется нумерованным параметром PublicValueEncoding. Эта структура является вариантом сообщения ключевого обмена клиента. Структура этого сообщения имеет вид:

    enum { implicit, explicit } PublicValueEncoding;
    implicit Если сертификат клиента уже содержит подходящий ключ алгоритма Diffie-Hellman, тогда Yc является неявным и не должно пересылаться снова. В этом случае будет послано сообщение ключевого обмена клиента ( Client Key Exchange ), но оно будет пустым.
    explicit Yc должно быть послано.
    struct { select (PublicValueEncoding) {
    case implicit: struct { };
    case explicit: opaque dh_Yc<1..2^16-1>; } dh_public;
    } ClientDiffieHellmanPublic;
    dh_Yc Общедоступный ключ Диффи-Хелмана клиента ( Yc ).

    Верификация сертификата

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

    Структура этого сообщения имеет вид:

    struct { Signature signature; } CertificateVerify;
    CertificateVerify.signature.md5_hash MD5(handshake_messages);
    Certificate.signature.sha_hash SHA(handshake_messages);

    Здесь handshake_messages относятся ко всем сообщениям диалога, посланным или полученным, начиная с hello клиента и вплоть до (но исключая) данное сообщение, содержащее поля типа и длины сообщений диалога. Это представляет собой соединение всех структур диалога.

    Сообщение Finished

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

    Сообщение finished является первым, защищенным с использованием только что согласованных алгоритмов, ключей и секретных кодов. Получатели сообщений finished должны верифицировать корректность содержимого. Раз партнер послал свое сообщение finished и получил корректное сообщение от другой стороны, он может начать посылать и получать прикладные данные.

    struct { opaque verify_data[12];} finished;
    verify_data PRF(master_secret, finished_label, MD5(handshake_messages) + SHA1(handshake_messages)) [0..11];
    finished_label Для сообщений finished, посланных клиентом, это строка "client finished". Для сообщений finished, посланных сервером, это строка "server finished".
    handshake_messages Все данные от сообщений диалога до этого сообщения (но не включительно). Это единственные данные, видимые на уровне диалога, они не включают заголовки уровня записей. Это соединение всех структур диалога.

    Если в соответствующей точке диалога за сообщением finished не следует сообщение об изменении шифровой спецификации, это считается фатальной ошибкой.

    Хэши, которые содержатся в сообщениях finished, посланных серверам, включают в себя Sender.server ; а посланные клиентом содержат Sender.client. Значение handshake_messages включает все сообщения диалога, начиная с hello клиента и вплоть до (но не включая) сообщения finished. Следует иметь в виду, что handshake_messages для сообщения finished, посланного клиентом, будет отличаться от посланного сервером, так как второе включает первое.

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

    Криптографические вычисления

    Для того чтобы начать защиту соединения, протоколу записей TLS необходима спецификация набора алгоритмов, мастерный секретный код и случайные коды клиента и сервера. Алгоритмы аутентификации, шифрования и MAC определяются cipher_suite, выбранным сервером и указанным в сообщении server hello. Алгоритм сжатия согласуется в сообщениях hello, а случайные коды пересылаются в сообщениях hello. Все что остается — это вычислить мастерный секретный код.

    Вычисление мастерного секретного кода

    Для всех методов ключевого обмена используется один и тот же алгоритм преобразования pre_master_secret в master_secret. Значение pre_master_secret следует стереть из памяти, как только завершится вычисление master_secret.

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

    Мастерный секретный код всегда имеет длину 48 байт. Длина предмастерного секретного кода варьируется в зависимости от метода ключевого обмена.

    RSA

    Когда для аутентификации сервера и ключевого обмена применяется RSA, 48-байтовый pre_master_secret генерируется клиентом, шифруется с помощью общедоступного ключа сервера и посылается серверу. Сервер использует свой секретный ключ для дешифрования pre_master_secret. Оба партнера преобразуют затем pre_master_secret в master_secret, как это специфицировано выше.

    Цифровые подписи RSA реализуются с помощью PKCS #1 [PKCS1] с типом блока 1. Шифрование RSA с использованием общедоступного ключа выполняется с помощью PKCS #1 с типом блока 2.

    Diffie-Hellman

    Выполняются обычные вычисления по алгоритму Diffie-Hellman. В качестве pre_master_secret используется согласованный ключ ( Z ), преобразование его в master_secret, описано выше.

    Параметры Diffie-Hellman специфицируются сервером и могут быть одноразовыми или взятыми из сертификата сервера.

    В отсутствие стандарта на прикладной профайл приложение TLS должно использовать шифровой набор TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.

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

    Страницы:

    Введение

    Первоначальной целью протокола TLS (Transport Layer Security, RFC-2246) является обеспечение конфиденциальности и целостности данных при коммуникации двух приложений. Протокол имеет два уровня: протокол записей TLS и протокол диалога TLS. На нижнем уровне, работающем поверх надежного транспортного протокола (напр., TCP [TCP]), размещается протокол записей TLS. Этот протокол обеспечивает безопасность соединений, которые имеют два основных свойства.

  • Соединение является конфиденциальным. Для шифрования данных используется симметричная криптография (напр., DES [DES], RC4 [RC4], и т.д.). Ключи для шифрования генерируются независимо для каждого соединения и базируются на секретном коде, получаемом с помощью другого протокола (такого, как протокол диалога TLS ). Протокол записей может использоваться и без шифрования.
  • Соединение является надежным. Процедура передачи сообщения включает в себя проверку целостности с помощью вычисления MAC. Для расчета MAC используются хэш-функции (напр., SHA, MD5 и т.д.). Протокол записей может работать и без MAC, но в этом режиме он применяется только в случае, когда другой протокол использует протокол записей в качестве транспортного при выяснении параметров безопасности.
  • Протокол записей TLS используется для инкапсуляции различных протоколов высокого уровня. Один из таких инкапсулируемых объектов, протокол диалога TLS, позволяет серверу и клиенту аутентифицировать друг друга и согласовать алгоритм шифрования и крипто-ключи, до того как приложение передаст или примет первый байт информации. Протокол диалога TLS обеспечивает безопасное соединение, которое имеет три базовых свойства.

  • Идентичность партнеров может быть выяснена с использованием асимметричной криптографии (например, RSA [RSA], DSS [DSS] и т.д.). Эта аутентификация может быть сделана опционной, но она необходима, по крайней мере, для одного из партнеров.
  • Выявление общего секретного кода является безопасным: этот секретный код недоступен злоумышленнику, даже если он ухитрится подключиться к соединению.
  • Диалог надежен: атакующий не может модифицировать соединение без того, чтобы быть обнаруженным партнерами обмена.
  • Одним из преимуществ TLS является то, что он не зависит от протокола приложения. Протоколы верхнего уровня могут размещаться поверх протокола TLS прозрачным образом. Стандарт TLS, однако, не специфицирует то, как протоколы увеличивают безопасность с помощью TLS ; решение о том, как инициализировать TLS -диалог и как интерпретировать сертификаты аутентификации, оставляется на усмотрение разработчиков протоколов и программ, которые работают поверх TLS.

    Цели

    Целями протокола TLS в порядке приоритетности являются:

  • Криптографическая безопасность. TLS должен применяться для установления безопасного соединения между двумя партнерами.
  • Совместимость. Независимые программисты должны быть способны разрабатывать приложения, использующие TLS, которые смогут успешно обмениваться криптографическими параметрами без знания особенностей программ друг друга.
  • Расширяемость. TLS ищет способ, как при необходимости встроить в систему новые ключи и методы шифрования. Здесь имеются две побочные цели: исключить необходимость создания нового протокола (что может быть сопряжено с введением новых слабых мест) и сделать ненужным внедрение новой библиотеки, обеспечивающей безопасность.
  • Относительная эффективность. Криптографические операции требуют больших мощностей ЦПУ, особенно этим славятся операции с открытыми ключами. По этой причине протокол TLS имеет опционную схему кэширования сессии, что позволяет уменьшить число соединений, устанавливаемых с использованием новых временных буферов.
  • Язык представления

    Представление данных в этом документе напоминает синтаксис языка Си и XDR [XDR], но эти параллели достаточно приблизительны и не имеют никакого отношения к самому протоколу TLS. Эти представления применены лишь для целей упрощения восприятия материала.

    Базовым блоком данных считается один байт (т.e. 8 бит). Многобайтовые информационные элементы представляют собой объединение последовательности байтов слева направо и сверху вниз. Многобайтовые элементы извлекаются из байтового потока (используя нотацию Си) следующим образом:

    value = (байт[0] << 8*(n-1)) | (байт[1] << 8*(n-2)) | ... | байт[n-1];

    Этот порядок байтов для многобайтовых последовательностей является стандартным для сетей (big endian).

    Комментарии начинаются с "/*" и завершаются "*/". Опционные компоненты выделяются с помощью помещения их в двойные квадратные скобки "[[ ]]". Однобайтовые объекты, содержащие не интерпретируемые данные, имеют непрозрачный тип (opaque).

    Вектор (одномерный массив) является потоком однородных информационных элементов. Размер вектора может быть специфицирован во время документирования или оставаться не специфицированным вплоть до начала работы. В любом случае длина определяет число байтов, а не число элементов в векторе. Синтаксис спецификации нового типа T', который является вектором фиксированной длины типа T, имеет вид TT'[n] ;

    Здесь T' занимает в информационном потоке n байт, где n кратно размеру T. Длина вектора не включается в кодированный поток.

    В следующем примере Datum определен как три последовательные байта, которые не интерпретируются протоколом, в то время как Data представляет собой три вектора Datum, состоящие из девяти байт.

    opaque Datum[3];    /* три не интерпретируемые байта */
    Datum Data[9];      /* 3 последовательных 3-байтовых вектора */

    Векторы переменной длины определяются путем спецификации субдиапазона легальных длин, используя нотацию <floor..ceiling>. При кодировании реальная длина предшествует потоку байтов, образующих вектор. Длина имеет форму числа, занимающего столько байт, сколько нужно, чтобы специфицировать максимально возможную длину вектора ( ceiling ). Вектор переменной длины с действительным полем длины равным нулю является пустым вектором.

    T T' <floor..ceiling>;

    В следующем примере, обязательным является вектор, который должен содержать от 300 до 400 байт непрозрачного типа. Он не должен быть пустым. Поле действительной длины занимает два байта, uint16, достаточных, чтобы представить значение 400. С другой стороны, longer может представить до 800 байт данных, или 400 uint16 элементов, и может быть пустым. Его кодовое представление будет включать два байта поля реальной длины, за которым будет следовать вектор. Длина закодированного вектора должна быть четной, кратной длине одиночного элемента (например: 17-байтовый вектор uint16 будет нелегальным ).

    opaque mandatory<300..400>;  
        /* поле длины имеет 2 байта, не может быть пустым */
    uint16 longer<0..800>;       
       /* 0 - 400 16-битовое целое число без знака */

    Базовый числовой тип данных представляет собой байт без знака ( uint8 ). Все более длинные типы цифровых данных образуются из фиксированной последовательности байт без знака, объединенных вместе. Следующие числовые типы являются предопределенными.

    uint8 uint16[2];
    uint8 uint24[3];
    uint8 uint32[4];
    uint8 uint64[8];

    Все значения здесь и в дальнейшем записываются в сетевом порядке (big-endian); uint32 представленное шестнадцатеричными байтами 01 02 03 04 эквивалентно десятичному значению 16909060.

    Еще одним типом данных является enum ( enumerated ). Поле типа enum предполагает, что величина декларирована при определении. Каждое определение дает новый тип. Только нумерованные элементы того же типа могут присваиваться и сравниваться. Каждому нумерованному элементу должно быть присвоено значение, как это показано в следующем примере. Так как нумерованные элементы неупорядочены, им может быть присвоено любое уникальное значение в любом порядке.

    enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;

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

    enum { red(3), blue(5), white(7) } Color;

    Можно опционно специфицировать значение без ассоциированной с ним метки, чтобы задать ширину без определения избыточного элемента. В следующем примере Taste в потоке данных занимает два байта, но может предполагать значения 1, 2 или 4.

    enum { sweet(1), sour(2), bitter(4), (32000) } Taste;

    Имена элементов нумерации собраны в пределах определенного типа. В первом примере полная ссылка на второй элемент будет выглядеть как Color.blue. Такое описание не требуется, если объект присвоения ( target ) хорошо специфицирован.

    Color color = Color.blue;  
       /* чрезмерная спецификация, допустимо */
    Color color = blue;         
       /* правильно, тип задан неявно */

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

    enum { low, medium, high } Amount;

    Типы структуры могут быть сформированы для удобства из примитивных типов. Каждая спецификация декларирует новый, уникальный тип. Синтаксис определения весьма похож на используемый в Си.

    struct {
      T1 f1;
      T2 f2;
      ...
      Tn fn;} [[T]];

    Поля в структуре могут быть квалифицированы, используя имена типов, которые синтаксис подобный нумерованным элементам. Например, T.f2 относится ко второму полю предыдущей декларации. Структурные определения допускают вложения.

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

    struct {
      T1 f1;
      T2 f2;
      ....
      Tn fn;
      select (E) {
      case e1: Te1;
      case e2: Te2;
      ....
      case en: Ten;
      } [[fv]];} [[Tv]];

    Например:

    enum { apple, orange } VariantTag;
    struct {
        uint16 number;
        opaque string<0..10>; /* переменная длина */
        } V1;
    struct {
        uint32 number;
        opaque string[10]; /* фиксированная длина */
        } V2;
    struct {
        select (VariantTag) { /* value of selector is implicit */
        case apple: V1; /* VariantBody, tag = apple */
        case orange: V2; /* VariantBody, tag = orange */
        } variant_body; /* optional label on variant */
        } VariantRecord;

    Структуры варианта могут быть подготовлены (сужены) путем спецификации значения селектора до спецификации типа. Например: orange VariantRecord является суженным типом для VariantRecord, содержащего variant_body типа V2.

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

    Алгоритм цифровой подписи включает в себя однопроходные хэшфункции, служащие для преобразования подписываемого текста. Элемент с цифровой подписью кодируется как непрозрачный вектор <0..216-1>, где длина специфицируется алгоритмом подписи и ключом.

    В подписи RSA, 36-байтовая структура двух хэшей (один SHA и один MD5 ) кодируется с помощью секретного ключа. Описание кодировки смотри в [PKCS1].

    В DSS, 20 байтов хэша SHA передаются непосредственно алгоритму цифровой подписи DSA (Digital Signing Algorithm) без дополнительного хэширования. В результате получаются числа r и s. Подпись DSS представляет собой непрозрачный вектор, содержимое которого представляет собой результат DER-кодирования:

    DssSigValue ::= SEQUENCE { r INTEGER,
                 s INTEGER}

    При поточном шифровании исходный текст сначала объединяется с псевдослучайным кодом идентичной длины (формируется специальным генератором) с помощью операции исключающее ИЛИ.

    При использовании блочного шифра каждый блок исходного текста преобразуется в зашифрованный кодовый блок той же длины. Все блочные шифрования выполняются в режиме CBC (Cipher Block Chaining), и все зашифрованные блочные элементы будут иметь размер, кратный длине шифрового блока.

    При шифровании с использованием общедоступного ключа алгоритм открытого ключа используется для шифрования данных так, чтобы их можно было дешифровать только с помощью секретного ключа, который образует пару с открытым ключом. Элемент, зашифрованный с помощью открытого ключа, выглядит как непрозрачный вектор <0..216-1>, где длина определяется алгоритмом подписи и ключом.

    В следующем примере:

    stream-ciphered struct {
           uint8 field1;
           uint8 field2;
           digitally-signed opaque hash[20];} UserType;

    содержимое хэша передается алгоритму подписи, затем вся структура шифруется с привлечением поточного шифра. Длина этой структуры в байтах будет равна 2 байтам для поля field1 и field2, плюс два байта для длины подписи, плюс длина выходных данных алгоритма подписи. Это определено благодаря тому факту, что алгоритм и ключ для подписи известны до кодирования или декодирования этой структуры.

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

    Например,

    struct { uint8 f1; uint8 f2;} Example1;
    Example1 ex1 = {1, 4};     /* assigns f1 = 1, f2 = 4 */

    HMAC и псевдослучайные функции

    Ряд операций на уровне записей и диалога требуют ключевого MAC; это дайджест определенных данных, защищенных секретным кодом. Фальсификация MAC невозможна без знания секретного кода. Конструкция, которая используется для этой операции, имеет название HMAC и описана в [HMAC].

    HMAC может использоваться с разными хэш-алгоритмами. TLS использует ее при диалоге с другими алгоритмами: MD5 и SHA-1, обозначая их как HMAC_MD5(secret, data) и HMAC_SHA(secret, data). Для других шифровых наборов и защищенных данных могут быть определены дополнительные хэш-алгоритмы, но в данной версии протокола для целей диалога жестко заданы MD5 и SHA-1.

    Кроме того, необходима схема расширения применения секретных кодов ( secret ) на блоки данных с целью генерации ключей и валидации. Такая псевдослучайная функция ( PRF ) применяет в качестве входной информации секретный код, порождающий код ( seed ) и идентификационную метку ( label ). При этом формируется выходной массив произвольной длины.

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

    Сначала, определена функция разложения данных, P_hash(secret, data), которая задействует одну хэш функцию для распространения секретного кода на произвольное число выходов:

    P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                     HMAC_hash(secret, A(2) + seed) +
                     HMAC_hash(secret, A(3) + seed) + ...

    где + обозначает объединение.

    A() определено как:

    A(0) = seed
    A(i) = HMAC_hash(secret, A(i-1))

    Для требуемого качества данных P_hash может итерироваться столько раз, сколько нужно. Например: если P_SHA-1 использовался для формирования 64 байт данных, его следует итерировать четыре раза (до A(4) ), создавая 80 байт выходных данных; последние 16 байт последней итерации будут отброшены, оставляя 64 байта.

    PRF TLS создана путем расщепления секретного кода на две части и использования одной половины для генерации данных с помощью P_MD5, а другой половины — для формирования данных посредством P_SHA-1, выходные данных этих двух процедур объединяются затем с помощью операции исключающего ИЛИ.

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

    L_S = длина секретного кода в байтах;
    L_S1 = L_S2 = ceil(L_S / 2);

    PRF определяется как результат смешения двух псевдослучайных потоков с помощью операции исключающее ИЛИ.

    PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);

    Метка представляет собой ASCII-строку. Она должна быть включена в исходном виде без байта длины или завершающего нуля. Например: метка "slithy toves" будет представлена в виде:

    73 6C 69 74 68 79 20 74 6F 76 65 73

    Заметим, что, так как MD5 выдает на выход 16 байт, а SHA-1 — 20 байт, границы их внутренних итераций не будут выровнены; чтобы сформировать на выходе 80 байт, P_MD5 осуществит итерации до A(5), в то время как P_SHA-1 - до A(4).

    Протокол записей TLS

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

    Четыре протокола описаны в данном документе: протокол диалога, протокол уведомления, протокол спецификации изменения шифра, и прикладной информационный протокол. Чтобы позволить расширение протокола TLS, разрешена поддержка протоколом дополнительных типов записей. Любые новые типы записей должны размещать значения типа немедленно за величинами ContentType четырех типов, описанных здесь. Если реализация TLS получает рекорд нераспознаваемого типа, она должна его игнорировать. Любой протокол, предназначенный для использования поверх TLS, должен быть тщательно сконфигурирован, чтобы противостоять любым атакам. Заметим, что из-за того, что тип и длина записи не защищены шифрованием, следует принимать меры, чтобы минимизировать трафик анализа этих величин.

    Состояния соединений

    Состояние соединения TLS является операционной средой протокола записей TLS. Оно специфицирует алгоритмы сжатия, шифрования и MAC. Кроме того, известны параметры этих алгоритмов: секретный код MAC, а также ключи шифрования и IV (вектор инициализации) соединения для направлений чтения и записи. Логически существует четыре состояния соединения: текущие состояния чтения и записи и отложенные состояния чтения и записи. Все записи обрабатываются в текущих состояниях чтения или записи. Параметры безопасности для отложенных состояний могут быть установлены протоколом диалога TLS. Протокол диалога может селективно переводить любое отложенное состояние в текущее, при этом соответствующее текущее состояние становится отложенным. Не допускается формировать состояние, которое не инициализировано с учетом параметров безопасности текущего состояния. Исходное текущее состояние всегда специфицировано без компрессии, шифрования или MAC. Параметры безопасности для состояния чтения и записи соединения TLS задаются путем определения следующих величин (таблица 16.1).

    Эти параметры определены в языке представления в виде:

    enum { server, client } ConnectionEnd;
    enum { null, rc4, rc2, des, 3des, des40 } BulkCipherAlgorithm;
    enum { stream, block } CipherType;
    enum { true, false } IsExportable;
    enum { null, md5, sha } MACAlgorithm;
    enum { null(0), (255) } CompressionMethod;
    /* Алгоритмы, специфицированные в CompressionMethod,
    BulkCipherAlgorithm и MACAlgorithm могут быть добавлены. */
    struct { ConnectionEnd       entity;
             BulkCipherAlgorithm bulk_cipher_algorithm;
             CipherType          cipher_type;
             uint8               key_size;
             uint8               key_material_length;
             IsExportable        is_exportable;
             MACAlgorithm        mac_algorithm;
             uint8               hash_size;
             CompressionMethod   compression_algorithm;
             Opaque              master_secret[48];
             Opaque              client_random[32];
             Opaque              server_random[32];} SecurityParameters;
    Конец соединения Клиент или сервер участник соединения.
    Алгоритм массового шифрования Алгоритм, используемый для массового шифрования. Эта спецификация включает размер ключа алгоритма, степень секретности ключа, является ли этот шифр блочным или поточным, размер блока и является ли шифр экспортным.
    Алгоритм MAC Алгоритм аутентификации сообщений. Эта спецификация включает размер хэша, который возвращается алгоритмом MAC.
    Алгоритм сжатия Алгоритм сжатия данных. Эта спецификация должна включать всю информацию, необходимую для выполнения компрессии.
    Секретный код сервера (master secret) 48-байтовый секретный код, общий для обоих партеров соединения.
    Случайный код клиента 32-байтный код, предоставляемый клиентом.
    Случайный код сервера 32-байтный код, предоставляемый сервером.

    Уровень записей будет использовать параметры безопасности для формирования следующих шести объектов:

  • Секретный код MAC записи клиента
  • Секретный код MAC записи сервера
  • Ключ записи клиента
  • Ключ записи сервера
  • IV записи клиента (только для блочного шифра)
  • IV записи сервера (только для блочного шифра)
  • Параметры записи клиента используются сервером при получении и обработке записей и наоборот. Раз параметры безопасности определены и ключи сформированы, состояния соединения могут быть в любой момент реализованы путем перевода их в текущее состояние. Эти текущие состояния должны актуализоваться после обработки записи. Каждое состояние соединения включает в себя следующие элементы:

    Состояние сжатия Текущее состояние алгоритма сжатия.
    Состояние шифра Текущее состояние алгоритма шифрования. Оно состоит из текущего ключа для данного соединения. Кроме того, для блочного шифра, работающего в режиме CBC (единственный режим, специфицированный в TLS ), оно в исходный момент содержит IV для данного состояния соединения и должно актуализоваться, чтобы содержать текст последнего шифрованного или дешифрованного блока. Для поточных шифров оно содержит всю необходимую информацию для продолжения шифрования или дешифрования данных.
    Секретный код MAC Секретный код MAC для данного соединения.
    Номер по порядку Каждое состояние соединения содержит номер по порядку, который поддерживается независимо для состояний чтения и записи. Номер по порядку должен быть установлен равным нулю, как только соединение переведено в активное состояние. Номера по порядку имеют тип uint64 и не могут превышать 264-1. Номер по порядку инкрементируется после прихода каждой записи: в частности, первая запись, передаваемая через некоторое соединение, имеет порядковый номер 0.

    Уровень записей

    Уровень записей TLS получает неинтерпретированные данные от верхних уровней в непустых блоках произвольного размера.

    Уровень записей фрагментирует информационные блоки и превращают их в записи TLSPlaintext, несущие данные в виде последовательностей длиной 214 байтов или меньше. Границы сообщения клиента на уровне записей не сохраняются (т.e., несколько сообщений клиента одного и того же ContentType могут быть объединены в одну запись TLSPlaintext, или одно сообщение может быть фрагментировано).

    struct { uint8 major, minor;} ProtocolVersion;
    enum         { change_cipher_spec(20), alert(21), handshake(22),
                 application_data(23), (255)
                 } ContentType;
    struct { ContentType type;
                 ProtocolVersion version;
                 uint16 length;
                 opaque fragment[TLSPlaintext.length];
                 } TLSPlaintext;
    type Протокол верхнего уровня, использованный для обработки вложенного фрагмента.
    version Версия примененного протокола.
    length Длина (в байтах) следующего TLSPlaintext.fragment. Длина не должна превосходить 214.
    Fragment Прикладные данные. Эти данные прозрачны и обрабатываются как независимые блоки, с которыми должен работать протокол верхнего уровня, специфицированный полем тип.

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

    Все записи сжаты с использованием алгоритма сжатия, определенным состоянием текущей сессии. Всегда имеется активный алгоритм сжатия; однако в исходный момент он определен как CompressionMethod. null. Алгоритм сжатия преобразует структуру TLSPlaintext в структуру TLSCompressed. Функции сжатия инициализируются информацией по умолчанию при переходе соединения в активное состояние.

    Должно использоваться сжатие без потерь, а длина содержимого не может стать больше чем 1024 байт. Если функция восстановления встречает фрагмент TLSCompressed.fragment, длина которого окажется больше 214 байт, она должна выдать уведомление о фатальной ошибке преобразования.

    Struct { ContentType type;       /* то же самое, что и TLSPlaintext.type */
           ProtocolVersion version;  /* то же самое, что и TLSPlaintext.version */
           uint16 length;
           opaque fragment
           [TLSCompressed.length];
           } TLSCompressed;
    length Длина (в байтах) следующего TLSCompressed.fragment. Длина не должна превосходить 214 + 1024.
    Fragment Сжатая форма TLSPlaintext.fragment.

    Операция CompressionMethod.null является идентификационной; ни одно из полей здесь не меняется.

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

    Функции шифрования и MAC преобразуют структуру TLSCompressed в TLSCiphertext. Функции дешифрования выполняют обратную процедуру. MAC записи включает также номер по порядку, чтобы было можно детектировать лишние или повторные сообщения.

    Struct { ContentType type;
           ProtocolVersion version;
           uint16 length;
           select (CipherSpec.cipher_type) {
           case stream: GenericStreamCipher;
           case block: GenericBlockCipher; } fragment;
           } TLSCiphertext;
    type Поле тип идентично TLSCompressed.type.
    Version Поле версия идентично TLSCompressed.version.
    length Длина (в байтах) последующего TLSCiphertext.fragment. Длина не может превосходить 214 + 2048.
    Fragment Зашифрованная форма TLSCompressed.fragment, с MAC.

    Нуль или стандартный поточный шифр

    Поточные шифры (включая BulkCipherAlgorithm.null) преобразуют структуры TLSCompressed.fragment в (или из) структуры TLSCiphertext. fragment.

    stream-ciphered struct { opaque content[TLSCompressed.length];
    opaque MAC[CipherSpec.hash_size];} GenericStreamCipher;

    MAC генерируется как:

    HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
    TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment));

    где "+" означает объединение (слияние).

    seq_num Номер по порядку для данной записи.
    hash Алгоритм хэширования, специфицированный в SecurityParameters.mac_algorithm.

    Заметим, что MAC вычисляется до шифрования. Поточный шифр преобразует весь блок, включая MAC. Для поточных шифров, которые не используют вектор синхронизации (такой, как RC4), состояние шифра записи используется в последующих пакетах. Если CipherSuite равен TLS_NULL_WITH_NULL_NULL, шифрование представляет собой операцию идентичного преобразования (т.e., данные не шифруются, а размер MAC равен нулю, что говорит о том, что MAC не применяется). TLSCiphertext.length равна TLSCompressed.length плюс CipherSpec.hash_size.

    Блочный шифр CBC

    Для блочных шифров (таких, как RC2 или DES), функции шифрования и MAC преобразуют структуры TLSCompressed.fragment в блоки структур TLSCiphertext.fragment или обратно.

    block-ciphered struct {
                           opaque content[TLSCompressed.length];
                           opaque MAC[CipherSpec.hash_size];
                           uint8 padding[GenericBlockCipher.padding_length];
                           uint8 padding_length;
                           } GenericBlockCipher;
    Padding Заполнитель, который добавляется, чтобы сделать длину исходного текста кратной длине блочного шифра. Заполнитель может иметь длину вплоть до 255 байт. Длины больше необходимой могут оказаться желательными, для того чтобы блокировать атаки на протокол, базирующийся на анализе длин сообщений. Каждый uint8 в векторе заполняющих данных должен быть заполнен значением длины.
    padding_length Длина заполнения должна быть такой, чтобы общий размер структуры GenericBlockCipher являлся кратным длине блока шифра. Диапазон легальных значений лежит в диапазоне 0-255, включительно. Эта длина специфицирует длину поля заполнителя, исключая само поле padding_length.

    Длина шифрованных данных ( TLSCiphertext.length ) на единицу больше, чем сумма TLSCompressed.length, CipherSpec.hash_size и padding_length.

    Если длина блока равна 8 байт, длина содержимого ( TLSCompressed.length ) равна 61 байту, а длина MAC равна 20 байтам, длина до заполнения составляет 82 байта. Таким образом, длина заполнения по модулю 8 должна быть равна 6, для того чтобы сделать полную длину четной, кратной 8 байтам (длина блока). Длина заполнения может быть 6, 14, 22 и т.д. до 254. Если бы длина заполнения была минимально необходимой (6), заполнитель имел бы 6 байтов, каждый из которых содержал число 6. Таким образом, последние 8 октетов GenericBlockCipher до блочного шифрования были бы xx 06 06 06 06 06 06 06, где xx последний октет MAC.

    Для блочного шифра в режиме CBC (Cipher Block Chaining) вектор инициализации (IV) для первой записи генерируется с другими ключами и секретными кодами, когда параметры безопасности заданы. IV для последующих записей равен последнему блоку шифрованного текста предыдущей записи.

    Протокол записей требует алгоритма для генерации ключей, IV и секретных кодов MAC из параметров безопасности, поставляемых протоколом диалога.

    Мастерный секретный код (master secret) хэшируется в последовательность байтов, которая присваивается секретным кодам MAC, ключам и IV, требуемых текущим состоянием соединения. CipherSpecs требует чтобы клиент и сервер записали секретный код MAC, ключ и IV, которые сформированы из мастерного секретного кода в указанном порядке. Неиспользованные значения остаются пустыми.

    Для генерации ключей вычисляется:

    key_block = PRF(SecurityParameters.master_secret, "key expansion",
    SecurityParameters.server_random + SecurityParameters.client_random);

    до тех пор, пока не будет сформирован выходной код. Затем key_block позиционируется следующим образом:

    client_write_MAC_secret[SecurityParameters.hash_size]
    server_write_MAC_secret[SecurityParameters.hash_size]
    client_write_key[SecurityParameters.key_material_length]
    >server_write_key[SecurityParameters.key_material_length]
    client_write_IV[SecurityParameters.IV_size]
    server_write_IV[SecurityParameters.IV_size]

    Значения client_write_IV и server_write_IV генерируются только для не экспортных блочных шифров. Для экспортируемых блочных шифров векторы инициализации генерируются позже, как это описано ниже. Любой лишний материал key_block отбрасывается.

    Спецификация шифра требует 2 x 24 байтовых ключей, 2 x 20 байтовых секретных кодов MAC, и 2 x 8 байтов IV, для 104 байтов материала ключей.

    Алгоритмы экспортируемого шифрования (для которого CipherSpec.is_exportable равно 'истинно') требуют дополнительной обработки для получения ключей записи, как это показано ниже:

    final_client_write_key =
    PRF(SecurityParameters.client_write_key,
                 "client write key",
                 SecurityParameters.client_random +
                 SecurityParameters.server_random);
        final_server_write_key =
    PRF(SecurityParameters.server_write_key,
                 "server write key",
                 SecurityParameters.client_random +
                 SecurityParameters.server_random);

    Алгоритмы экспортируемого шифрования получают свои IV исключительно из случайных кодов сообщений hello:

    iv_block = PRF("", "IV block", 
       SecurityParameters.client_random + SecurityParameters.server_random);

    Блок iv_block делится на два инициализационных вектора, как это делалось выше для key_block:

    client_write_IV[SecurityParameters.IV_size]
    server_write_IV[SecurityParameters.IV_size]

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

    TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 требует пяти случайных байт для каждого из двух ключей шифрования и 16 байт для каждого ключа MAC, что составляет 42 байта ключевого материала. Выход PRF запоминается в key_block. Блок key_block делится, а ключи записи запоминаются, так как это алгоритм экспортного шифрования.

    key_block                = PRF(master_secret,
                             "key expansion",
                             server_random +
                             client_random)[0..41]
    client_write_MAC_secret  = key_block[0..15]
    server_write_MAC_secret  = key_block[16..31]
    client_write_key         = key_block[32..36]
    server_write_key         = key_block[37..41]
    final_client_write_key   = PRF(client_write_key,
                             "client write key",
                             client_random +
                             server_random)[0..15]
    final_server_write_key   = PRF(server_write_key,
                             "server write key",
                             client_random +
                             server_random)[0..15]
    iv_block                 = PRF("", "IV block", client_random +
                             server_random)[0..15]
    client_write_IV          = iv_block[0..7]
    server_write_IV          = iv_block[8..15]

    Протокол диалога TLS

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

    Протокол диалога ответственен за согласования характеристик сессии, куда входят следующие объекты:

    идентификатор сессии Произвольная последовательность байтов, выбранная сервером для идентификации состояния сессии (активная/ возобновляемая).
    сертификат партнера X509v3 [X509] сертификат партнера. Этот элемент состояния может быть равен нулю.
    метод сжатия Алгоритм, используемый для сжатия информации перед шифрованием.
    спецификация шифра Специфицирует алгоритм массового шифрования (такой как нуль, DES, и т.д.) и алгоритм MAC (такой как MD5 или SHA). Она определяет также криптографические атрибуты, такие, как hash_size.
    мастерный секретный код 48-байтовый секретный код, общий для сервера и клиента.
    'is resumable' Флаг, указывающий, может ли сессия использоваться для инициализации нового соединения.

    Эти объекты применяются затем для определения параметров безопасности уровня записей при защите прикладных данных. Многие соединения могут реализоваться в рамках той же сессии с помощью процедуры возобновления протокола диалога.

    Протокол изменения спецификации шифра

    Протокол изменения спецификации шифра предназначен для оповещения об изменении стратегии шифрования. Протокол использует одно сообщение, которое зашифровано и архивировано в рамках текущего состояния соединения. Сообщение состоит из одного байта со значением 1.

    struct { enum { change_cipher_spec(1), (255) } type;} ChangeCipherSpec;

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

    Протокол оповещения

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

    enum { warning(1), fatal(2), (255) } AlertLevel;
    enum { close_notify(0),
            unexpected_message(10),
            bad_record_mac(20),
            decryption_failed(21),
            record_overflow(22),
            decompression_failure(30),
            handshake_failure(40),
            bad_certificate(42),
            unsupported_certificate(43),
            certificate_revoked(44),
            certificate_expired(45),
            certificate_unknown(46),
            illegal_parameter(47),
            unknown_ca(48),
            access_denied(49),
            decode_error(50),
            decrypt_error(51),
            export_restriction(60),
            protocol_version(70),
            insufficient_security(71),
            internal_error(80),
            user_canceled(90),
            no_renegotiation(100),
    (255)} AlertDescription;
    struct { AlertLevel level; AlertDescription description;} Alert;

    Оповещения закрытия

    Клиент и сервер должны оба знать, что соединение завершается, для того чтобы избежать атаки усечения ( truncation ). Оба партнера могут запустить обмен сообщениями закрытия.

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

    Оба партнера могут инициализировать закрытие, послав уведомление close_notify. Любые данные, полученные после оповещения о закрытии, игнорируются.

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

    Предполагается, что закрытие соединения надежно доставляет все данные, ждущие передачи, прежде чем транспортная система будет блокирована.

    Оповещения об ошибках

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

    unexpected_message Получено непредусмотренное сообщение. Это оповещение является всегда фатальным и не должно встречаться при обменах между корректными реализациями.
    bad_record_mac Это оповещение присылается, если получена запись с неверным MAC. Это сообщение всегда вызывает фатальную ошибку.
    decryption_failed TLSCiphertext дешифрован неверно: либо текст не имел длину, четную и кратную размеру блока, либо их значения (заполнители) при проверке оказались некорректными. Это сообщение всегда вызывает фатальную ошибку.
    record_overflow Получена запись TLSCiphertext, которая имеет длину больше 214+2048 байт, запись дешифрована TLSCompressed в запись с более чем 214+1024 байтов. Это сообщение всегда вызывает фатальную ошибку.
    decompression_failure Функция декомпрессии получила неприемлемые данные (напр., данные, которые после восстановления будут иметь слишком большой объем). Это сообщение вызывает фатальную ошибку.
    handshake_failure Получение сообщения оповещения handshake_failure указывает, что отправитель не мог согласовать приемлемый набор параметров безопасности из числа предлагаемых опций. Это фатальная ошибка.
    bad_certificate Сертификат был поврежден, содержал подписи, которые не прошли проверку, и т.д..
    unsupported_certificate Сертификат имел не поддерживаемый тип.
    certificate_revoked Сертификат был отозван его подписантом.
    certificate_expired Сертификат имеет истекший срок годности или не пригоден по другой причине.
    certificate_unknown Некоторая другая, не специфицированная причина при обработке сертификата, делающая его неприемлемым.
    illegal_parameter Поле при диалоге оказалось вне диапазона допустимых значений или не согласуется с другими полями. Это фатальная ошибка.
    unknown_ca Получена корректная сертификатная последовательность или ее часть, но сертификат не был воспринят из-за того, что CA-сертификата не может быть обнаружен или не согласуется с известным проверенным CA. Это сообщение всегда вызывает фатальную ошибку.
    access_denied Получен правильный сертификат, но при проверке доступа отправитель решил не продолжать согласование. Это сообщение всегда вызывает фатальную ошибку.
    decode_error Сообщение не может быть дешифровано из-за того, что некоторое поле выходит за пределы допустимого или сообщение имеет неверный размер. Это сообщение всегда вызывает фатальную ошибку.
    decrypt_error Диалог криптографической операции не прошел, это может включать неудачу проверки подписи, обмена ключами или контроль завершающего сообщения.
    export_restriction Согласование параметров вошло в противоречие с экспортными регламентациями. Например: попытка передать 1024 битов краткосрочного RSA -ключа для метода диалога RSA_EXPORT. Это сообщение всегда вызывает фатальную ошибку.
    protocol_version Протокольная версия клиента распознана, но не поддерживается. Например, старые версии протокола могут отвергаться по соображениям безопасности. Это сообщение всегда вызывает фатальную ошибку.
    insufficient_security Возвращается вместо handshake_failure, когда согласование не прошло, в частности, из-за того, что сервер требует более секретного шифра, чем может поддержать клиент. Это сообщение всегда вызывает фатальную ошибку.
    internal_error Внутренняя ошибка, не связанная с партнером, или требования протокола не допускают продолжения процедуры (например, ошибка при выделении памяти). Это сообщение всегда вызывает фатальную ошибку.
    user_canceled Этот диалог аннулирован по какой-то причине, не связанной с протокольной ошибкой. Если пользователь аннулирует операцию после завершения диалога, закрытие соединения путем посылки close_notify является более приемлемым.
    no_renegotiation Посылается клиентом в ответ на запрос hello или сервером — в ответ на hello клиента после стартового диалога. Любое из этих сообщений должно, в норме, вызывать повторное согласование параметров. Когда это не приемлемо, получатель должен реагировать посылкой этого уведомления ( alert ). В этой точке отправитель исходного запроса может решить, следует ли сохранять соединение. Случаем, когда это приемлемо, может оказаться ситуация, когда сервер запускает процесс в ответ на запрос. Процесс может получить параметры безопасности (длину ключа, аутентификацию и т.д.) при запуске, и может быть трудно сообщить об изменении этих параметров в этой точке процесса. Это сообщение всегда является предупреждением.

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

    Обзор протокола диалога

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

  • Обмен сообщениями hello, чтобы согласовать алгоритмы, обмен случайными кодами и проверка перезапуска сессии.
  • Обмен необходимыми криптографическими параметрами, чтобы позволить клиенту и серверу согласовать предмастерные секретные коды.
  • Обмен сертификатами и криптографической информацией, чтобы позволить клиенту и серверу аутентифицировать друг друга.
  • Генерация мастерного секретного кода из предмастерного и обмен случайными кодами.
  • Предоставление параметров безопасности уровню записей.
  • Разрешение клиенту и серверу проверить, что их партнер вычислил те же самые параметры безопасности и что диалог прошел без вмешательства хакера.
  • Заметим, что верхние слои не должны слишком полагаться на TLS, всегда согласуя самые безопасные из возможных соединений между партнерами:

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

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

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

    Протокол TLS является безопасным, и любой шифровой набор предлагает свой уровень безопасности. Если вы согласуете использование 3DES с 1024-битовым RSA -ключом при связи с ЭВМ, чей сертификат вы проверили, вы можете быть уверены в безопасности. Однако вы никогда не должны посылать данные по каналу, где применяется 40-битовая шифрование, если только вы не уверены, что данные не стоят того, чтобы кто-то тратил силы на их дешифрование.

    Эти цели достигаются протоколом диалога, который может быть суммирован следующим образом. Клиент посылает сообщение hello, на которое сервер должен также откликнуться сообщением hello, в противном случае возникает ситуация фатальной ошибки и соединение разрывается. Сообщения client hello и server hello используются для установления более безопасного взаимодействия клиента и сервера. Сообщения client hello и server hello устанавливают следующие атрибуты: версия протокола, ID-сессии, шифровой набор и метод сжатия. Кроме того, партнеры генерируют и пересылают друг другу два случайных числа: ClientHello.random и ServerHello.random.

    Реальный обмен ключами подразумевает до четырех сообщений: сертификат сервера, ключевой обмен сервера, сертификат клиента и ключевой обмен клиента. Новые методы ключевого обмена могут быть созданы с помощью спецификации формата для этих сообщений, чтобы позволить клиенту и серверу согласовать использование общего секретного кода. Этот секретный код должен быть достаточно длинным. Современные методы ключевого обмена пересылают коды длиной от 48 до 128 байт.

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

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

    (рис 16.1) Обмен сообщениями в процессе диалога

    * отмечает опционные или зависящие от ситуации сообщения, которые посылаются не всегда.

    Когда клиент и сервер решают возобновить предыдущую сессию или задублировать существующую сессию (вместо согласования новых параметров безопасности), следует обмен следующими сообщениями.

    Клиент посылает .) Если соответствия с ID-сессии не найдено, сервер генерирует новый ID сессии, а клиент TLS и сервер осуществляют полный диалог.

    (рис 16.2) Обмен сообщениями для упрощенного диалога

    Протокол диалога

    Протокол диалога TLS представляет собой один из фиксированных клиентских протоколов высокого уровня (записей) TLS. Этот протокол используется для согласования атрибутов безопасности сессии. Сообщения диалога передаются уровню записей TLS, где они инкапсулируются в одну или более структур TLSPlaintext, которые обрабатываются и передаются так, как это специфицировано текущим состоянием активной сессии.

    enum { hello_request(0), client_hello(1), server_hello(2),
    certificate(11), server_key_exchange (12),
    certificate_request(13), server_hello_done(14),
    certificate_verify(15), client_key_exchange(16),
    finished(20), (255)
    } HandshakeType;
    struct { HandshakeType msg_type; /* тип диалога */
             uint24 length;          /* байтов в сообщении */
             select (HandshakeType) {
             case hello_request: HelloRequest;
             case client_hello: ClientHello;
             case server_hello: ServerHello;
             case certificate: Certificate;
             case server_key_exchange: ServerKeyExchange;
             case certificate_request: CertificateRequest;
             case server_hello_done: ServerHelloDone;
             case certificate_verify: CertificateVerify;
             case client_key_exchange: ClientKeyExchange;
             case finished: Finished; } body;
             } Handshake;

    Сообщения протокола диалога представлены ниже в порядке, в котором они должны быть посланы. Посылка сообщений диалога в неправильном порядке приведет к фатальной ошибке. Ненужные сообщения диалога могут быть опущены. Обратите внимание на одно исключение: сообщение сертификата используется в диалоге дважды (от клиента к серверу, а затем от сервера к клиенту), но оно описано лишь для первого случая его использования. Одно сообщение не привязано к этим правилам порядка обмена, — это сообщение запроса Hello, которое может быть послано в любое время, но которое должно игнорироваться клиентом, если приходит в середине диалога.

    Сообщения Hello

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

    Запрос Hello

    Сообщение-запрос hello может быть послано сервером в любое время. Запрос Hello является простым уведомлением о том, что клиент должен начать согласование параметров путем посылки сообщения client hello. Это сообщение будет проигнорировано клиентом, если он участвует в сессии согласования или если он не хочет заново согласовывать параметры сессии, или клиент может, если хочет, реагировать уведомлением no_renegotiation. Так как сообщения диалога предназначены для осуществления определенных действий над прикладными данными, ожидается, что согласование начнется до того, как будут получены новые записи от клиента. Если сервер посылает запрос hello, но не получает отклика client hello, он может разорвать соединение с фатальным уведомлением.

    После посылки запроса hello серверы не должны повторять запрос до тех пор, пока диалог согласования не завершится.

    Структура этого сообщения: struct { } HelloRequest;

    Это сообщение не должно никогда включаться в хэши сообщений и использоваться в завершающих сообщениях ( finished ), а также в сообщении верификации сертификатов.

    Hello клиента

    Когда клиент инициализирует соединение с сервером, первым должно быть послано сообщение client hello. Клиент может также послать client hello в качестве отклика на запрос hello или по своей собственной инициативе, для того чтобы заново согласовать параметры безопасности существующего соединения.

    Сообщение hello клиента включает в себя случайную структуру, которая позднее используется протоколом.

    struct { uint32 gmt_unix_time; opaque random_bytes[28];} Random;
    gmt_unix_time Текущее время и дата в соответствии со стандартом UNIX в 32-битовом формате (число секунд с момента полуночи 1-го января, 1970, GMT) согласно показаниям внутренних часов отправителя. Часы могут и не быть точно выверены на уровне протокола TLS. Прикладные протоколы могут накладывать дополнительные ограничения.
    random_bytes 28 байт сформированных безопасным генератором случайных чисел.

    Сообщение client hello включает в себя идентификатор сессии переменной длины. Если это поле не пусто, его значение идентифицирует сессию, чьи параметры безопасности клиент желает использовать повторно. Идентификатор сессии может соответствовать какому-то предшествующему соединению, текущему соединению, или другому активному соединению. Вторая опция полезна, если клиент хочет изменить случайные структуры и получить текущие значения параметров соединения, в то время как третья опция делает возможным установить несколько независимых безопасных соединений без повторения всей процедуры протоколы диалога. Эти независимые соединения могут существовать последовательно или одновременно; SessionID становится действенным, когда диалог согласования завершается обменом сообщениями Finished, и сохраняется до тех пор, пока не состарится или пока не произойдет фатальная ошибка. Действительное содержимое SessionID определяется сервером.

    opaque SessionID<0..32>;

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

    Список CipherSuite, передаваемый от клиента серверу в сообщении client hello, содержит комбинации криптографических алгоритмов, поддерживаемых клиентом в порядке их предпочтения (предпочтительный вариант — первый). Каждый CipherSuite определяет алгоритм пересылки ключей, алгоритм массового шифрования (включая длину секретного ключа) и алгоритм MAC. Сервер выберет шифровой набор или, если приемлемого варианта нет, пришлет уведомление об ошибке и прервет соединение.

    uint8 CipherSuite[2]; /* Селектор криптографического набора */

    Hello клиента включает в себя список алгоритмов сжатия, поддерживаемых клиентом, в порядке их предпочтения.

    enum { null(0), (255) } CompressionMethod;
    struct { ProtocolVersion client_version;
           Random random;
           SessionID session_id;
           CipherSuite cipher_suites<2..216-1>;
           CompressionMethod compression_methods<1..28-1>;
           } ClientHello;
    client_version Версия протокола TLS, которой хочет воспользоваться клиент во время сессии. Это должна быть последняя версия (с наибольшим кодом), поддерживаемая клиентом. Для данной спецификации значение версии равно 3.1.
    Random Псевдослучайная структура, генерируемая клиентом.
    session_id ID-сессия, которую клиент хочет использовать для данного соединения. Это поле должно быть пустым, если нет ни одного session_id или клиент хочет выработать новые параметры безопасности.
    cipher_suites Список криптографических опций, поддерживаемых клиентом в порядке предпочтения. Если поле session_id не пусто (запрос восстановления сессии), этот вектор должен включать, по крайней мере, cipher_suite данной сессии.
    compression_methods Список методов сжатия, поддерживаемых клиентом в порядке их предпочтения. Если поле session_id не пусто (запрос восстановления сессии) он должен включать compression_method данной сессии. Этот вектор должен содержать, а все реализации должны поддерживать CompressionMethod.null. Таким образом, клиент и сервер всегда могут согласовать метод сжатия информации.

    После посылки сообщения client hello клиент ждет сообщения server hello. Любое другое сообщение диалога, присланное сервером, рассматривается как фатальная ошибка.

    В интересах прямой совместимости, клиенту разрешено включать в сообщение client hello после методов сжатия дополнительные данные. Эти данные должны быть включены в хэши диалога, в противном случае они игнорируются. Hello — единственное сообщение диалога, для которого это допускается; для всех остальных сообщений объем данных должен точно соответствовать описанию сообщения.

    Hello сервера

    Сервер посылает это сообщение в ответ на сообщение client hello, когда он может найти приемлемый набор алгоритмов. Если он не может сделать приемлемый выбор, он реагирует уведомлением об ошибке диалога.

    Структура этого сообщения:

    Struct { ProtocolVersion server_version;
           Random random;
           SessionID session_id;
           CipherSuite cipher_suite;
           CompressionMethod compression_method;
           } ServerHello;
    server_version Это поле будет содержать самое низкое значение, которое предлагается клиентом в client hello, и наибольшее значение версии, поддерживаемое сервером. Значение версии данной спецификации равно 3.1.
    Random Эта структура генерируется сервером и должна быть отличной от ClientHello.random.
    session_id Идентифицирует сессию, соответствующую данному соединению. Если ClientHello.session_id не пусто, сервер будет искать соответствие с содержимым своего кэша сессий. Если соответствие найдено и сервер хочет установить новое соединение, используя специфицированное состояние сессии, он откликнется тем же значением ID, что было прислано клиентом. Это индицирует возобновляемую сессию. Партнеры должны продолжить обмен сообщениями finished. В противном случае, это поле будет содержать другое значение, идентифицирующее новую сессию. Сервер может вернуть пустое поле session_id, чтобы индицировать, что сессия не кэшируется и, следовательно, не может быть возобновлена. Если сессия возобновлена, она должна использовать тот же шифровой набор, который был согласован ранее.
    cipher_suites Шифровой набор, выбранный сервером из списка в ClientHello.cipher_suites. Для возобновленных сессий это поле несет в себе значение, взятое из состояния возобновляемой сессии.
    Compression_method Алгоритм сжатия, выбранный сервером из списка в ClientHello.compression_methods. Для возобновляемых сессий это поле содержит значение из состояния возобновляемой сессии.

    Сертификат сервера

    Сервер должен посылать сертификат всякий раз, когда согласованный метод обмена ключами не является анонимным. За этим сообщением всегда непосредственно следует сообщение server hello.

    Тип сертификата должен соответствовать выбранному алгоритму обмена ключами шифров, обычно это сертификат X.509v3. Он должен содержать ключ, который соответствует методу обмена ключами. Если не специфицировано обратного, алгоритм подписи для сертификата должен быть тем же, что и алгоритм для ключа сертификата. Если не специфицировано обратного, общедоступный ключ может иметь любую длину.

    Алгоритм обмена ключами Тип сертификата ключа
    RSA Общедоступный ключ RSA ; сертификат должен допускать использование ключа для шифрования.
    RSA_EXPORT Общедоступный ключ RSA с длиной больше чем 512 бит, который может быть использован для подписи, или ключ длиной 512 бит или короче, который может быть использован для шифрования или подписи.
    DHE_DSS Общедоступный ключ DSS.
    DHE_DSS_EXPORT Общедоступный ключ DSS.
    DHE_RSA Общедоступный ключ RSA, который может использоваться для подписи.
    DHE_RSA_EXPORT Общедоступный ключ RSA, который может использоваться для подписи.
    DH_DSS Ключ Diffie-Hellman. Алгоритмом, используемым для подписи сертификата, должен быть DSS.
    DH_RSA Ключ Diffie-Hellman. Алгоритмом, используемым для подписи сертификата, должен быть RSA.

    Все сертификатные профайлы, ключи и криптографические форматы определены рабочей группой IETF PKIX [PKIX]. Когда присутствует расширение применения ключа, бит digitalSignature должен быть установлен для ключа, выбранного для подписи, как это описано выше, а бит keyEncipherment должен присутствовать, чтобы разрешить шифрование, как это описано выше. Бит keyAgreement должен быть установлен для сертификатов Diffie-Hellman.

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

    Структура этого сообщения имеет вид:

    opaque ASN.1Cert<1..224-1>;
    struct { ASN.1Cert certificate_list<0..224-1>; } Certificate;
    certificate_list Это последовательность (цепочка) сертификатов X.509v3. Сертификат отправителя должен быть записан в списке первым. Каждый следующий сертификат должен непосредственно сертифицировать предшествующий сертификат. Так как верификация сертификата требует, чтобы корневые ключи распределялись независимо, самоподписывающий сертификат, который специфицирует корневой источник сертификата, может быть опционно удален из цепочки, в предположении, что партнер должен уже иметь его, чтобы проверить его в любом случае.

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

    Сообщение ключевого обмена сервера

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

  • RSA_EXPORT (если открытый ключ в сертификате длиннее, чем 512 бит)
  • DHE_DSS
  • DHE_DSS_EXPORT
  • DHE_RSA
  • DHE_RSA_EXPORT
  • DH_anon
  • Некорректно посылать сообщение ключевого обмена сервера для следующих методов пересылки ключей:

  • RSA
  • RSA_EXPORT (когда открытый ключ в сертификате сервера короче или равен 512 бит)
  • DH_DSS
  • DH_RSA
  • Это сообщение передает криптографическую информацию, чтобы позволить клиенту оперировать с предмастерным секретным кодом: либо общедоступный ключ RSA, чтобы зашифровать предмастерный секретный код, либо общедоступный ключ Diffie-Hellman, с помощью которого клиент может завершить обмен ключами.

    В качестве дополнительных определены наборы CipherSuites, которые включают в себя новые алгоритмы обмена ключами. Сервер пошлет сообщение обмена ключами тогда и только тогда, когда тип сертификата, ассоциированный с алгоритмов обмена ключами, не предоставил достаточно информации клиенту, чтобы осуществить пересылку предмастерного секретного кода.

    Согласно экспортному закону США, модули RSA больше 512 бит не могут применяться для ключевого обмена в программах, экспортируемых из США. Более длинные ключи RSA, зашифрованные в сертификатах, могут быть использованы для подписи более коротких ключей RSA в случае метода ключевого обмена RSA_EXPORT.

    Структура этого сообщения:

    enum { rsa, diffie_hellman } KeyExchangeAlgorithm;
    struct { opaque rsa_modulus<1..2^16-1>; opaque rsa_exponent
       <1..2^16-1>;} ServerRSAParams;
    rsa_modulus Модуль временного RSA -ключа сервера.
    rsa_exponent Общедоступный показатель временного RSA -ключа сервера.
    struct { opaque dh_p<1..2^16-1>;
    opaque dh_g<1..2^16 1>;
    opaque dh_Ys<1..2^16 1>;} ServerDHParams; /* Временные DH параметры */
    dh_p Простой модуль, используемый для операции Diffie-Hellman.
    dh_g Генератор, используемый для операции Diffie-Hellman.
    dh_Ys Общедоступное значение (gX mod p) метода Diffie-Hellman для сервера.
    struct { select (KeyExchangeAlgorithm) {
              case diffie_hellman:
              ServerDHParams params;
              Signature signed_params;
              case rsa:
              ServerRSAParams params;
              Signature signed_params; };
              } ServerKeyExchange;
    Params Параметры ключевого обмена сервера.
    signed_params Для неанонимных ключевых обменов. Хэш соответствующих значений параметров с подписью, согласованной с примененным хэшем.
    md5_hash MD5(ClientHello.random + ServerHello.random + ServerParams);
    sha_hash SHA(ClientHello.random + ServerHello.random + ServerParams);
    enum { anonymous, rsa, dsa } SignatureAlgorithm;
    select (SignatureAlgorithm)
             { case anonymous: struct { };
             case rsa:
             digitally-signed struct
             {
             opaque md5_hash[16];
             opaque sha_hash[20];
             };
             case dsa:
             digitally-signed struct {
             opaque sha_hash[20];
             };
             } Signature;

    Запрос сертификата

    Не анонимный сервер может опционно запросить сертификат от клиента, если это возможно для выбранного шифрового набора. За этим сообщением, если оно послано, непосредственно следует сообщение ключевого обмена сервера ( Server Key Exchange ) (в противном случае, сообщение сертификата сервера).

    Структура этого сообщения:

    enum { rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), 
       dss_fixed_dh(4), (255)} ClientCertificateType;
    opaque DistinguishedName<1..216-1>;
    struct { ClientCertificateType certificate_types<1..28-1>;
    DistinguishedName certificate_authorities<3..216-1>;
    } CertificateRequest;
    certificate_types Это поле представляет собой список типов запрошенных сертификатов, расположенных в порядке предпочтения сервера.
    certificate_authorities Список имен приемлемых провайдеров сертификатов. Эти имена могут специфицировать уникальное имя корневого или подчиненного CA. Таким образом, это сообщение может быть использовано как для описания известных корней и желательного пространства авторизации.

    DistinguishedName получается из [X509]. Считается фатальной ошибкой (оповещение handshake_failure), если анонимный сервер запрашивает идентификацию клиента.

    Hello done сервера

    Сообщение сервера hello done посылается сервером, чтобы индицировать завершение операций с hello сервера и связанных с ним сообщений. После отправки этого сообщения сервер ждет отклика клиента.

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

    После получения сообщения сервера hello done клиент должен проверить, что сервер предоставил корректный сертификат, если это требуется, и что параметры, присланные сервером, приемлемы.

    Структура этого сообщения: struct { } ServerHelloDone;

    Сертификат клиента

    Это первое сообщение, которое может послать клиент после получения сообщения от сервера hello done. Это сообщение посылается только в случае запроса присылки сертификата со стороны сервера. Если приемлемого сертификата нет, клиент должен послать пустое сообщение сертификата. Если серверу для продолжения диалога требуется аутентификация клиента, он может откликнуться, послав уведомление о фатальной ошибке.

    Сообщение обмена ключами клиента

    Это сообщение непосредственно следует за сообщением сертификата клиента (если оно посылается). В противном случае оно будет первым сообщением, посылаемым клиентом после получения сообщения сервера hello done.

    Предмастерный секретный код устанавливается с помощью этого сообщения, либо путем прямой его передачи, зашифровав с применением RSA, либо с помощью передачи параметров Diffie-Hellman, которые позволят каждой из сторон согласовать применение одного и того же предмастерного секретного кода. Когда в качестве метода передачи ключей использован DH_RSA или DH_DSS, запрашивается сертификация клиента, и клиент может откликаться посылкой сертификата, который содержит общедоступный ключ Diffie-Hellman, чьи параметры (группа и генератор) соответствуют, специфицированным в сертификате сервера. Это сообщение не содержит никаких других данных.

    Структура этого сообщения:

    struct { select (KeyExchangeAlgorithm) 
      { case rsa: EncryptedPreMasterSecret;
    case diffie_hellman: ClientDiffieHellmanPublic; } exchange_keys;
    } ClientKeyExchange;

    Сообщение зашифрованного RSA предмастерного секретного кода

    Если для согласования ключей и аутентификации применен алгоритм RSA, клиент генерирует 48-байтовый предмастерный секретный код, шифрует его с помощью общедоступного ключа из сертификата сервера или временного RSA -ключа, переданного в сообщении ключевого обмена сервера, и посылает результат в сообщении зашифрованного предмастерного секретного кода (encrypted premaster secret). Эта структура является вариантом сообщения ключевого обмена клиента.

    Структура этого сообщения:

    struct { ProtocolVersion client_version; opaque random[46];} 
      PreMasterSecret;
    client_version Последняя (новейшая) версия, поддерживаемая клиентом. Она используется для детектирования атак связанных с понижением номера версии. После получения предмастерного секретного кода сервер должен проверить, что данное значение согласуется с величиной, переданной клиентом в сообщении hello.
    random 46 байт псевдослучайного кода.
    struct{public-key-encrypted PreMasterSecret pre_master_secret;} 
       EncryptedPreMasterSecret;

    Атака, рассмотренная Даниэлем Блайхенбахером (Daniel Bleichenbacher) [BLEI], может быть предпринята против TLS -сервера, который использует PKCS#1, закодированный с помощью RSA.

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

    pre_master_secret Это случайное число генерируется клиентом и используется для формирования мастерного секретного кода.

    Общедоступный Diffie-Hellman-ключ клиента

    Эта структура передает общедоступную величину ( Yc ) алгоритма Диффи-Хелмана для клиента, если она не была уже включена в сертификат клиента. Шифрование, используемое для Yc, определяется нумерованным параметром PublicValueEncoding. Эта структура является вариантом сообщения ключевого обмена клиента. Структура этого сообщения имеет вид:

    enum { implicit, explicit } PublicValueEncoding;
    implicit Если сертификат клиента уже содержит подходящий ключ алгоритма Diffie-Hellman, тогда Yc является неявным и не должно пересылаться снова. В этом случае будет послано сообщение ключевого обмена клиента ( Client Key Exchange ), но оно будет пустым.
    explicit Yc должно быть послано.
    struct { select (PublicValueEncoding) {
    case implicit: struct { };
    case explicit: opaque dh_Yc<1..2^16-1>; } dh_public;
    } ClientDiffieHellmanPublic;
    dh_Yc Общедоступный ключ Диффи-Хелмана клиента ( Yc ).

    Верификация сертификата

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

    Структура этого сообщения имеет вид:

    struct { Signature signature; } CertificateVerify;
    CertificateVerify.signature.md5_hash MD5(handshake_messages);
    Certificate.signature.sha_hash SHA(handshake_messages);

    Здесь handshake_messages относятся ко всем сообщениям диалога, посланным или полученным, начиная с hello клиента и вплоть до (но исключая) данное сообщение, содержащее поля типа и длины сообщений диалога. Это представляет собой соединение всех структур диалога.

    Сообщение Finished

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

    Сообщение finished является первым, защищенным с использованием только что согласованных алгоритмов, ключей и секретных кодов. Получатели сообщений finished должны верифицировать корректность содержимого. Раз партнер послал свое сообщение finished и получил корректное сообщение от другой стороны, он может начать посылать и получать прикладные данные.

    struct { opaque verify_data[12];} finished;
    verify_data PRF(master_secret, finished_label, MD5(handshake_messages) + SHA1(handshake_messages)) [0..11];
    finished_label Для сообщений finished, посланных клиентом, это строка "client finished". Для сообщений finished, посланных сервером, это строка "server finished".
    handshake_messages Все данные от сообщений диалога до этого сообщения (но не включительно). Это единственные данные, видимые на уровне диалога, они не включают заголовки уровня записей. Это соединение всех структур диалога.

    Если в соответствующей точке диалога за сообщением finished не следует сообщение об изменении шифровой спецификации, это считается фатальной ошибкой.

    Хэши, которые содержатся в сообщениях finished, посланных серверам, включают в себя Sender.server ; а посланные клиентом содержат Sender.client. Значение handshake_messages включает все сообщения диалога, начиная с hello клиента и вплоть до (но не включая) сообщения finished. Следует иметь в виду, что handshake_messages для сообщения finished, посланного клиентом, будет отличаться от посланного сервером, так как второе включает первое.

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

    Криптографические вычисления

    Для того чтобы начать защиту соединения, протоколу записей TLS необходима спецификация набора алгоритмов, мастерный секретный код и случайные коды клиента и сервера. Алгоритмы аутентификации, шифрования и MAC определяются cipher_suite, выбранным сервером и указанным в сообщении server hello. Алгоритм сжатия согласуется в сообщениях hello, а случайные коды пересылаются в сообщениях hello. Все что остается — это вычислить мастерный секретный код.

    Вычисление мастерного секретного кода

    Для всех методов ключевого обмена используется один и тот же алгоритм преобразования pre_master_secret в master_secret. Значение pre_master_secret следует стереть из памяти, как только завершится вычисление master_secret.

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

    Мастерный секретный код всегда имеет длину 48 байт. Длина предмастерного секретного кода варьируется в зависимости от метода ключевого обмена.

    RSA

    Когда для аутентификации сервера и ключевого обмена применяется RSA, 48-байтовый pre_master_secret генерируется клиентом, шифруется с помощью общедоступного ключа сервера и посылается серверу. Сервер использует свой секретный ключ для дешифрования pre_master_secret. Оба партнера преобразуют затем pre_master_secret в master_secret, как это специфицировано выше.

    Цифровые подписи RSA реализуются с помощью PKCS #1 [PKCS1] с типом блока 1. Шифрование RSA с использованием общедоступного ключа выполняется с помощью PKCS #1 с типом блока 2.

    Diffie-Hellman

    Выполняются обычные вычисления по алгоритму Diffie-Hellman. В качестве pre_master_secret используется согласованный ключ ( Z ), преобразование его в master_secret, описано выше.

    Параметры Diffie-Hellman специфицируются сервером и могут быть одноразовыми или взятыми из сертификата сервера.

    В отсутствие стандарта на прикладной профайл приложение TLS должно использовать шифровой набор TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.

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

    Вернуться к учебному плану