Данный пост представляет собой что-то вроде конспекта, составленного по книге Брюса Шнайера и Нильса Фергюсона «Практическая криптография». Книга эта невероятно интересная! Если вы интересуетесь криптографией, то просто обязаны держать ее на полке (в крайнем случае — скачать электронную версию), ровно как и любую другую книгу этих авторов, которую сможете найти.

Важно! Если перед вами стоит практическая задача, советую найти настоящего специалиста по криптографии, а не полагаться на бложик никому неизвестного программиста. Учтите также, что книга была написана в 2005 году и к тому моменту, когда вы найдете эту заметку, информация может устареть.

Есть несколько причин, по которой я взялся за написание этой памятки. Для начала, я хочу иметь под рукой краткую заметку по созданию безопасного соединения. Есть подозрения, что когда-нибудь она мне пригодится. Объем «Практической криптографии» — более 400 страниц, так что на краткое руководство оно не тянет. Тем не менее, ознакомиться с книгой стоит для понимания того, почему авторы предлагают то решение, а не иное. Другая причина создания этого поста — мой экземпляр книги начинает потихоньку разваливаться. Третья — информация в интернете доступна отовсюду, а не только у меня дома.

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

1. Постановка задачи

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

2. Шифрование данных

В качестве алгоритма шифрования Шнайер и Фергюсон рекомендуют использовать AES (Rijndael с размером блока 128 бит ) с длиной ключа 256 бит в режиме CTR . В этом режиме максимальный размер одного сообщения составляет 16 · 2 32 байт. Вряд ли кого-то стеснит это ограничение. В крайнем случае можно разбить сообщение на части.

Для шифрования сообщения с номером i, имеющего размер L бит, необходимо вычислить L бит следующим способом:

b 1 , b 2 , …, b L = E K (0 [32 бита] || i [32 бита] || 0 [64 бита] ) || E K (1 || i || 0 ) ||
E K (2 || i || 0) || E K (3 || i || 0) || …

где функция E K (128 бит на выходе) — алгоритм шифрования, K (256 бит) — ключ шифрования, аргумент функции (128 бит) — шифруемые данные, а оператор || означает конкатенацию данных. После вычисления битов b 1 … b L складываем их по Жегалкину (исключающее ИЛИ, операция XOR, ⊕) с битами сообщения, в результате получаем зашифрованный текст. Для расшифровки нужно проделать точно такую же последовательность действий над шифротекстом.

Здесь важно обратить внимание на два момента. Во-первых, ключи шифрования при посылке данных от А к Б и от Б к А должны быть разными. Во-вторых, необходимо согласовать последовательность байт в аргументе функции E K . Порядок байт в 32-х разрядных числах может меняться в зависимости от архитектуры используемого процессора. Традиционно в сетевых протоколах и форматах файлов используется порядок байт от старшего к младшему .

3. Функция хэширования

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

Допустим, мы успешно установили сеансовый ключ с помощью алгоритма Диффи-Хеллмана , в результате А и Б получили общий ключ К. Затем производится следующая последовательность действий:

// Избавляемся от алгебраической структуры ключа
K = SHA d -256(K)
// Строим 4 дочерних ключа:
// 1. Шифрование от А к Б
key_send_enc = SHA d -256(K || «Enc from A to B»)
// 2. Шифрование от Б к А
key_recv_enc = SHA d -256(K || «Enc from B to A»)
// 3. Аутентификация от А к Б
key_send_auth = SHA d -256(K || «Auth from A to B»)
// 4. Аутентификация от Б к А
key_recv_auth = SHA d -256(K || «Auth from B to A»)

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

Каждое сообщение должно сопровождаться имитовставкой (MAC) . В качестве функции MAC Шнайер и Фергюсон рекомендуют использовать HMAC-SHA-256:

a i = HMAC K (i || L(x i ) || x i || m i )
HMAC K (m) = SHA256{(K ⊕ 0x5c5c..5c) || SHA256[(K ⊕ 0x3636..36) || m]}

Здесь a i — код аутентификации i-го сообщения m i . HMAC K (m) представляет собой хэш-функцию, значение которой зависит не только от сообщения m, но и от ключа K. Как и в случае с шифрованием, в MAC должно использоваться два независимых ключа для А и Б. Как уже отмечалось, ⊕ — это операция побитового исключающего ИЛИ (она же XOR).

В «Практической криптографии» делается особый акцент на том, как важно производить аутентификацию не только сообщения m i , являющегося обычной последовательностью байт, но и его смысла. Пусть m i = a || b || c, то есть состоит из нескольких полей определенной длины. Представим, что после очередного обновления протокола размеры полей изменилось. Тогда, если злоумышленник Е сможет подменить версию протокола, сообщение будет интерпретировано неверно. Притом неважно как злоумышленнику удастся это сделать — безопасность одной части системы не должна зависеть от безопасности остальных.

Вот для чего нужна дополнительная информация x i , включающая в себя id протокола (сигнатуру, IP:порт получателя и отправителя), версию протокола, id сообщения, размер и имена полей сообщения. Притом информация x i должна быть закодирована таким образом, чтобы ее можно было однозначно декодировать. L(x i ) в нашей формуле — это, очевидно, длина x i . К счастью, всего этого гемора можно избежать, просто передавая сообщения в XML-подобном формате данных, то есть таком, где дополнительная информация о смысле сообщения уже включена в само сообщение. Тут важно не забыть включить в каждое сообщение всю дополнительную информацию, перечисленную выше.

Что еще следует знать о MAC?

  • Можно обрезать значение HMAC-SHA-256 до 16-и байт . Делать это не желательно, но такое решение лучше, чем использование HMAC-MD5 с целью уменьшить объем передаваемых данных.
  • Рекомендуется сначала вычислять MAC, а затем производить шифрование сообщения вместе с MAC , а не наоборот.

5. Описание обмена сообщениями

Будем считать, что пользователи А и Б уже произвели согласование сеансового ключа и вычислили дочерние ключи, как это было описано в пункте 3.

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

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

Теперь А и Б обмениваются сообщениями в формате, подобном XML (либо следуют инструкциям, которые предусмотрены для иной ситуации, см пункт 4). Каждому сообщению присваивается уникальный 32-х битный номер. Нумерация сообщений начинается с единицы — так проще отследить момент, когда номера закончатся. Когда это произойдет, необходимо заново произвести согласование ключей. Можно использовать и 64-х битные номера сообщений, но тогда каждое сообщение станет на 4 байта длиннее. К тому же, время от времени следует производить обновление ключей, так что 32 бита — в самый раз.

Если для передачи данных используется протокол UDP, следует производить повторную посылку сообщения по некоторому таймауту в случае, если другая сторона на него не реагирует. При использовании TCP этого не требуется — транспортный уровень позаботиться о гарантированной доставке пакетов. Злоумышленник Е может нарушить сеанс связи между А и Б, испортив одно из сообщений или переставив пару сообщений местами. Но раз у нас есть «человек посередине», то он в любом случае может нарушить связь между А и Б. Отсюда вывод:

Не стоит тратить время на написание «своего TCP» поверх другого протокола — просто используйте протокол TCP. Если, конечно, в вашем приложении это возможно.

Когда пользователь А хочет послать сообщение m i , он вычисляет MAC этого сообщения a i согласно пункту 4. Затем он шифрует сообщение m i || a i , как это описано в пункте 2 и посылает пользователю Б следующее: i || m’ i || a’ i . После этого А увеличивает значение счетчика отправленных сообщений i на единицу.

Когда пользователь Б получает i || m’ i || a’ i , он расшифровывает m i и a i , проверяет код аутентификации. Сообщения с неверным MAC игнорируются (их мог отправить злоумышленник Е). Затем производится проверка значения i — если оно меньше ожидаемого номера сообщения, для определенности назовем его j, сообщение отбрасывается. В противном случае (i >= j) присваиваем j значение i + 1 и обрабатываем сообщение m i .

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

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

6. Заключение

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

Дополнение: Эти вопросы рассматриваются в заметке, посвященной эллиптической криптографии . Также вас может заинтересовать статья Основные криптографические алгоритмы на языке Си .

Не менее интересные вопросы — как правильно генерировать псевдослучайные числа? Как противодействовать тайминг-атакам? Что делать, если операционная система поместит запущенное приложение со всеми ключами в файл подкачки? Как бесследно удалить файл с устаревшей парой RSA-ключей? Злоумышленник Е хоть и не знает содержимого сообщений, но знает их размер и время отправки. Опасно ли это и как этому противостоять?

Мир информационной безопасности неописуемо интересен, хоть и сложен. В этом блоге непременно появятся новые посты о криптографии.

admin

Share
Published by
admin

Recent Posts

Консоль удаленного рабочего стола(rdp console)

Клиент удаленного рабочего стола (rdp) предоставляет нам возможность войти на сервер терминалов через консоль. Что…

4 недели ago

Настройка сети в VMware Workstation

В VMware Workstation есть несколько способов настройки сети гостевой машины: 1) Bridged networking 2) Network…

4 недели ago

Логи брандмауэра Windows

Встроенный брандмауэр Windows может не только остановить нежелательный трафик на вашем пороге, но и может…

4 недели ago

Правильный способ отключения IPv6

Вопреки распространенному мнению, отключить IPv6 в Windows Vista и Server 2008 это не просто снять…

4 недели ago

Ключи реестра Windows, отвечающие за параметры экранной заставки

Параметры экранной заставки для текущего пользователя можно править из системного реестра, для чего: Запустите редактор…

4 недели ago

Как управлять журналами событий из командной строки

В этой статье расскажу про возможность просмотра журналов событий из командной строки. Эти возможности можно…

4 недели ago