2026 г.

Криптография с полки: что брать готовым и почему не изобретать

Проект ЦИТадель

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

«С полки» здесь означает не первый найденный пакет и не низкоуровневый примитив, а реализацию опубликованной конструкции или протокола в сопровождаемой библиотеке с понятным интерфейсом и безопасными настройками по умолчанию. Раздел 1 объясняет границу между использованием и изобретением. Раздел 2 сопоставляет задачи и свойства. Разделы 3–6 разбирают хэши и MAC, симметричное шифрование, установление ключей и подписи. Раздел 7 посвящён жизненному циклу ключей, раздел 8 — постквантовой миграции, раздел 9 — практикуму на учебных данных.

1. Золотое правило и почему оно так жёстко

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

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

Один из примеров — сравнение тега MAC или секретного токена. Сравнение с досрочным выходом может связать время работы с совпавшим префиксом; возможность практической атаки зависит от шума и окружения, но писать и проверять такую реализацию самостоятельно незачем. Библиотеки дают функции вроде hmac.compare_digest, разработанные без зависимого от содержимого досрочного выхода. При этом длина, тип данных и остальная ветвящаяся логика всё ещё могут раскрывать информацию.

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

2. Карта задач: что криптография умеет

Криптографические средства дают разные свойства, и название алгоритма ничего не говорит о том, подходит ли он к поставленной задаче.

  • Конфиденциальность ограничивает чтение содержимого. Её даёт шифрование, но оно не обязано скрывать размер, время обмена, адреса и другие метаданные.
  • Целостность и аутентичность сообщения позволяют обнаружить подмену и удостовериться, что криптографическое подтверждение создал обладатель соответствующего секрета. Для общего секрета таким подтверждением служит код аутентичности сообщения (MAC), для публичной проверки — цифровая подпись. Не снабжённый ключом хэш сам по себе от атакующего не защищает.
  • Публичная проверяемость происхождения позволяет независимому участнику проверять подпись по открытому ключу, не получая возможности подписывать. Связь открытого ключа с человеком, службой или производителем устанавливается сертификатом, доверенным каталогом либо другим механизмом вне самой подписи.
  • Проверка пароля и выработка ключа — тоже разные задачи. Пароли проверяют с помощью функции с настраиваемой стоимостью; ключевой материал преобразуют и разделяют стандартной функцией выработки ключей (KDF). Быстрый хэш общего назначения не подходит ни в качестве парольной функции, ни в качестве замены стандартной KDF.
  • Установление общего ключа через открытый канал выполняют протоколы согласования или механизмы инкапсуляции ключа; без аутентификации собеседника они не защищают от человека посередине.

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

3. Хэши: отпечаток данных

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

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

  • MD5 и SHA-1 не подходят для новых защитных схем. Для них практически продемонстрированы коллизии. В новом проекте выбирают алгоритм, заданный современным протоколом или профилем, обычно из семейств SHA-2 или SHA-3. Старый MD5 как контрольная сумма от случайного повреждения не превращает систему автоматически в уязвимую, но его нельзя считать доказательством целостности в присутствии атакующего.
  • Пароль нельзя хранить как быстрый хэш. Для него используют парольную функцию с уникальной солью и настроенной стоимостью, предпочтительно Argon2id через штатный API фреймворка; возможны scrypt и предусмотренные профилем варианты, а bcrypt в основном сохраняют для совместимости со старыми системами. Параметры и соль хранят рядом с результатом, чтобы стоимость можно было повышать при следующем входе. Подробнее это разобрано в статье об аутентификации.
  • Хэш нельзя превращать в MAC простым добавлением ключа. Конструкции вида hash(key || message) могут иметь свойства, которых разработчик не учитывает. Используют определённый стандартом MAC, например HMAC с разрешённой хэш-функцией, и сравнивают теги функцией без досрочного выхода. MAC не скрывает сообщение и подтверждает лишь то, что тег создал кто-то из обладателей общего ключа.

4. Симметричное шифрование: один ключ на двоих

Симметричное шифрование использует общий секретный ключ и эффективно обрабатывает прикладные данные. Для нового формата сообщений обычно выбирают готовую схему AEAD — аутентифицированное шифрование с ассоциированными данными, например AES-GCM или ChaCha20-Poly1305, — либо протокол более высокого уровня, который уже задаёт схему и формат.

  • AEAD защищает шифртекст и связанные с ним открытые поля. Ассоциированные данные (AAD) не шифруются, но входят в проверку тега. Туда помещают, например, версию формата, тип записи или идентификатор объекта, если их нельзя подменять отдельно от содержимого.
  • Одноразовое значение не является ключом и обычно передаётся открыто. Для AES-GCM и ChaCha20-Poly1305 оно обязано быть уникальным при данном ключе; повтор нарушает конфиденциальность и аутентичность. Одни высокоуровневые форматы управляют значениями сами, другие API требуют их от вызывающего кода. Уникальность должна сохраняться при параллельной работе, перезапуске и восстановлении снимка. Термины nonce и IV используются в разных схемах с разными требованиями, поэтому заменять документацию общим правилом нельзя.
  • Ошибка проверки означает полный отказ расшифрования. Изменение шифртекста, тега, AAD, ключа или одноразового значения должно приводить к одному безопасному исходу без выдачи открытого текста. Кроме уникальности, схема задаёт предел размера сообщения и объёма данных на ключ; их также учитывают при проектировании.

ECB раскрывает равенство одинаковых блоков и не применяется для защиты сообщений. Режимы без аутентификации допустимы лишь там, где их требует уже спроектированный и проанализированный формат. Для дисков, больших потоков и архивов нужны отдельные схемы с разбиением на записи, порядком блоков и восстановлением после обрыва: единичный вызов AEAD не заменяет такой протокол.

5. Ключевой обмен и асимметрика: разговор без общего секрета

Асимметричная криптография использует пару связанных ключей — открытый и закрытый, но дальнейшая операция зависит от конкретной схемы. При согласовании ключа, например на основе X25519, стороны объединяют собственный закрытый материал с открытым материалом собеседника. В механизме инкапсуляции, например ML-KEM, отправитель по открытому ключу получает шифртекст и общий секрет, а обладатель закрытого ключа восстанавливает тот же секрет. Цифровая подпись использует другую схему. Объяснение «зашифровать одним ключом и расшифровать другим» описывает упрощённую картину RSA и не является общей моделью асимметричной криптографии.

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

Прикладные системы работают гибридно: асимметричная часть устанавливает или передаёт небольшой общий секрет, KDF получает из него раздельные сеансовые ключи, а основной поток защищает AEAD. Так устроено типичное соединение TLS 1.3 с сертификатом: эфемерное установление секрета и подпись рукопожатия решают разные задачи. Самостоятельно применять низкоуровневое асимметричное шифрование к произвольным данным обычно не требуется; следует выбрать готовый протокол, который определяет аутентификацию, формат, обработку ошибок и обновление алгоритмов.

6. Подписи: публичная проверка происхождения

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

Подпись сама по себе не называет владельца ключа и не доказывает его намерение. Для аутентичности нужен доверенный способ связать открытый ключ с субъектом и проверить срок, назначение и статус ключа. Безусловной неотрекаемости подпись тоже не обеспечивает: ключ мог быть украден, разделён между операторами или использован автоматикой. Юридически или организационно значимое доказательство требует правил выпуска и хранения ключа, журналов, времени подписи, отзыва и процедуры разрешения спора.

Разница с MAC состоит в распределении полномочий. Каждый обладатель общего ключа MAC может и проверять, и создавать допустимые теги, поэтому третьей стороне нельзя доказать, кто именно из них создал сообщение. Обладатель открытого ключа подписи может проверять, но не подписывать. MAC удобен при небольшом числе участников с безопасно распределённым общим секретом; подпись — когда проверяющих много или им нельзя давать право создавать сообщения. На подписях строятся сертификаты и проверка выпусков ПО, но подпись подтверждает происхождение и неизменность артефакта, а не его безопасность.

7. Жизненный цикл ключей

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

  • Назначение и создание. Ключи создают криптографическим генератором или выводят стандартной KDF из подходящего ключевого материала. Один ключ не используют одновременно для шифрования, MAC, разных сред и независимых арендаторов. Идентификатор и версия ключа входят в аутентифицируемые метаданные формата, но не заменяют сам секрет.
  • Хранение и доступ. Ключ не помещают в исходный код, репозиторий или образ контейнера. Переменная окружения может служить каналом доставки секрета процессу, но не является хранилищем и может попасть в дамп, диагностику или дочерний процесс. Менеджер секретов, система управления ключами (KMS) или аппаратный модуль безопасности (HSM) выдаёт материал либо выполняет операцию после проверки прав; доступ ограничивают назначением и временем и журналируют. Для долговременных ключей отдельно определяют резервирование и восстановление, а эфемерный ключ, напротив, должен быть уничтожен после сеанса.
  • Случайность и одноразовые значения. Ключи и токены должны быть непредсказуемы, поэтому используют системный криптографический генератор, например API семейства secrets, а не генератор для моделирования. Одноразовое значение шифрования не всегда обязано быть случайным или секретным: для многих AEAD важна уникальность при данном ключе. Состояние счётчика нельзя терять при перезапуске или восстановлении снимка, а параллельным копиям нужны непересекающиеся диапазоны или другая схема обеспечения уникальности.
  • Криптопериод и ротация. Срок ключа выбирают по назначению, объёму обработанных данных, требованиям алгоритма и последствиям компрометации, а не только по календарю. Формат должен указывать версию ключа. Новые записи шифруют или подписывают новым ключом, а старые данные оставляют доступными для разрешённых операций. Ротацию регулярно проверяют; сама по себе она не исправляет уже произошедшую утечку.
  • Компрометация и завершение использования. План определяет, как прекратить новые операции, отозвать или заблокировать ключ, выпустить замену, установить затронутые данные и при необходимости перешифровать их. Подписи, созданные в период возможной компрометации, нельзя восстановить простым повторным подписанием: сначала нужно заново установить происхождение данных. Старые открытые ключи могут понадобиться для проверки истории; важно сохранить их подлинность. Ключи расшифрования могут понадобиться для разрешённого чтения архива; их хранят отдельно от активных ключей и удаляют после окончания обязательного срока.

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

8. Постквантовая миграция — коротко

Криптографически значимого квантового компьютера пока нет, и срок его появления неизвестен. Однако достаточно мощная машина с алгоритмом Шора сможет нарушить стойкость широко применяемых RSA, Диффи — Хеллмана и схем на эллиптических кривых. Это не означает «слом всей асимметричной криптографии»: постквантовые схемы тоже асимметричны, но основаны на других задачах. Симметричные шифры и хэш-функции не подвержены той же атаке; их параметры выбирают по актуальному профилю, а не по универсальному правилу «удвоить длину».

В 2024 году NIST утвердил первые стандарты: ML-KEM для установления ключей, ML-DSA и SLH-DSA для подписей. Переход уже относится к эксплуатации, а не к отдалённой теории. Его начинают с криптографической описи: где находятся уязвимые для квантовой атаки алгоритмы, как долго должны оставаться секретными данные, кто обновляет библиотеку, протокол, сертификаты и устройства. Долгоживущие секреты требуют раннего внимания из-за сценария «собрать сейчас — расшифровать потом».

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

9. Практикум: готовые средства и ошибки применения

Все задания выполняйте локально, на одноразовых ключах и вымышленных данных. Не проверяйте временные атаки на чужом сервисе и не сохраняйте результаты заведомо небезопасных режимов. Для Python понадобятся стандартные модули hashlib, hmac, random, secrets и сопровождаемые пакеты с AEAD, Ed25519 и Argon2id, например cryptography и argon2-cffi.

Задание 1. Функциональный тест и время. Напишите только для лаборатории уязвимое сравнение двух байтовых строк: проходите их слева направо, завершайте работу при несовпадении и после каждого совпавшего байта добавляйте небольшую задержку. Для догадок с префиксами разной длины соберите по нескольку десятков измерений и сравните медианы. Затем замените функцию на hmac.compare_digest. Объясните, почему обычный тест на равенство проходил в обоих вариантах, а чистого временного сигнала от встроенного оператора == конкретная версия среды показывать не обязана.

Задание 2. ECB и повторяющиеся блоки. В изолированном примере зашифруйте строку из восьми одинаковых 16-байтовых блоков с AES-ECB. Разбейте шифртекст на блоки и посчитайте одинаковые. Обработайте те же данные высокоуровневым AES-GCM с новым одноразовым значением и повторите подсчёт. Наблюдение демонстрирует утечку структуры ECB, но случайный вид второго шифртекста сам по себе не служит доказательством стойкости.

Задание 3. Подмена и AEAD. В учебном скрипте зашифруйте сообщение AES-CTR, измените один бит шифртекста и сравните открытые тексты после расшифрования. Затем используйте AES-GCM или ChaCha20-Poly1305: передайте версию учебного формата, алгоритм и идентификатор ключа как AAD и по очереди измените шифртекст, AAD и одноразовое значение. Во всех случаях AEAD должен вернуть ошибку проверки, а программа — не использовать частичный открытый текст. Выпишите поля получившегося конверта: версия, алгоритм, идентификатор ключа, одноразовое значение, шифртекст и тег; секретный ключ в конверт не входит.

Задание 4. Хэш, пароль и MAC. Для тестового файла вычислите SHA-256 и проверьте его по заранее сохранённому доверенному значению. Тестовый пароль обработайте Argon2id дважды с автоматически созданными уникальными солями и проверьте оба результата штатной функцией. Для сообщения создайте HMAC-SHA-256 со случайным ключом и проверьте его через compare_digest. Для каждого результата укажите, кто способен его пересчитать или подделать и какая часть данных должна быть секретной.

Задание 5. Источник случайности. Создайте два экземпляра random.Random с одинаковым известным зерном и убедитесь, что последовательности совпадают. Отдельно получите токены через secrets. По внешнему виду коротких выборок нельзя определить их криптографическую стойкость: вывод делают из назначения API и модели генератора. Проведите поиск вызовов обычного генератора в учебном проекте и разделите их на моделирование, где он уместен, и ключи, токены или пароли, где требуется криптографический источник.

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

Задание 7. Жизненный цикл ключа. Для одного учебного случая заполните карточку: назначение и алгоритм, владелец, идентификатор, создание, хранилище, получатели доступа, одноразовые значения, криптопериод и предел данных, ротация, резервирование, если оно требуется, действия при компрометации и судьба старых данных. Не записывайте в карточку значение ключа. Проверьте сценарии: новый ключ уже используется для записи, старый ещё читает архив; активный ключ отозван; хранилище временно недоступно.

Задание 8*. Криптографическая опись. Для небольшого сервиса перечислите TLS, сертификаты, подписи токенов и выпусков, шифрование базы и резервных копий, SSH и встроенную криптографию зависимостей. Рядом укажите алгоритм, библиотеку, владельца обновления и требуемый срок конфиденциальности или проверки подписи. Отметьте применения RSA, Диффи — Хеллмана и эллиптических кривых, но не меняйте их в рабочей системе: результат задания — приоритетный план миграции и проверок совместимости.

Итоги

  • Начинать следует со стандартного протокола или высокоуровневого API. Функциональный тест криптографического кода не доказывает стойкость, а готовый примитив не исправляет ошибочный протокол вокруг него.
  • Хэш даёт отпечаток, но не защищает его от атакующей стороны. Пароли требуют функции с солью и настраиваемой стоимостью, а аутентичность сообщения с общим секретом — стандартного MAC.
  • Для новых форматов сообщений применяют AEAD, однако вызывающий код по-прежнему отвечает за правила одноразовых значений, ключи, AAD, пределы данных и полный отказ при ошибке проверки.
  • Согласование ключа, KEM, шифрование и подпись — разные асимметричные операции. Открытый ключ нужно связать с участником, а прикладные системы сочетают асимметричное установление секрета с симметричной защитой данных.
  • Подпись — не шифрование закрытым ключом. Она позволяет публично проверить, что для точной последовательности байтов создана подпись соответствующим закрытым ключом; связь с субъектом требует инфраструктуры доверия, а использование подписи как доказательства — дополнительных эксплуатационных процедур.
  • Ключи разделяют по назначению и управляют всем их жизненным циклом: созданием, доступом, одноразовыми значениями, криптопериодом, ротацией, отзывом, архивом и удалением.
  • Постквантовая миграция уже опирается на утверждённые стандарты. Её начинают с криптографической описи и срока ценности данных, а новые алгоритмы включают через обновляемые протоколы и библиотеки.

Литература

  1. D. Boneh, V. Shoup, "A Graduate Course in Applied Cryptography" — подробный открытый учебник по конструкциям и доказательствам.
  2. Latacora, "Cryptographic Right Answers" — прикладная шпаргалка по выбору готовых средств.
  3. RFC 5116, "An Interface and Algorithms for Authenticated Encryption" — интерфейс AEAD, AAD и требования к одноразовым значениям.
  4. RFC 9106, "Argon2 Memory-Hard Function" — Argon2id, соль и параметры функции.
  5. NIST SP 800-57 Part 1 Rev. 5 — общий жизненный цикл и защита ключевого материала.
  6. Стандарты NIST: FIPS 203 (ML-KEM), FIPS 204 (ML-DSA) и FIPS 205 (SLH-DSA).
  7. Документация libsodium — пример высокоуровневых интерфейсов шифрования, хэширования и выработки ключей.
  8. Связанные статьи проекта: «Модель угроз», «TLS: как устроено защищённое соединение», «Аутентификация и авторизация», «Цепочка поставок ПО», «Инженерный конвейер» и курс «Квантовые вычисления».
404 Not Found

404 Not Found


nginx/1.24.0 (Ubuntu)

Связь с редакцией