>
Май 2017
Пн Вт Ср Чт Пт Сб Вс
« Апр    
1234567
891011121314
15161718192021
22232425262728
293031  

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

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

В этой статье я покажу, как реализовать основные операции по работе с PKI. Речь идет о подписи, проверке подписи, шифровании и расшифровании в контексте PKI. Теоретически данный код может использоваться с любым CSP (Cryptography Service Provider), поддерживающим интерфейс MS Crypto API. Я пользуюсь бесплатным отечественным CSP. Почему отечественным? Дело в том, что в .NET нет поддержки алгоритмов ГОСТ. А если мы работаем с реальными проектами и, следовательно, вынуждены соответствовать требованиям регуляторов в России, то без гостовских алгоритмов нам никак. Но если установить отечественный криптопровайдер, поддерживающий MS Crypto API, то все будет тип-топ, потому что .NET при работе с CMS дергает именно MS Crypto API.

Картина мира

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

Информация, необходимая для работы PKI, содержится в сертификате X.509. В PKI участвуют как минимум три стороны: Алиса, Боб и удостоверяющий центр (УЦ). У Алисы и Боба есть сертификаты с закрытым ключом, подписанные так называемым корневым сертификатом УЦ. У Алисы есть сертификат Боба с открытым ключом, а у Боба — сертификат Алисы с открытым ключом. Алиса и Боб доверяют УЦ и благодаря этому могут доверять друг другу.

Упрощенная структура PKI

Упрощенная структура PKI

Сертификаты X.509

Так повелось, что основным «активом» в PKI является сертификат X.509. Сертификат — это что-то вроде паспорта, он содержит информацию, позволяющую идентифицировать субъект, которому выдан сертификат (поле Subject), указывает, кем он был выпущен (поле Issuer), серийный номер сертификата и многое другое. В Windows управлять сертификатами можно с помощью оснастки «Сертификаты» (run->certmgr.msc).

Менеджер сертификатов

Менеджер сертификатов

Сертификаты хранятся в хранилищах («Личнoе», «Доверенные центры сертификации», «Доверенные лица»…).

При получении сертификата важно установить его в правильное хранилище. Так, сертификаты, которые ты хочешь использовать для электронной подписи, должны быть установлены в хранилище «Личное», а сертификаты получателей, которым нужно будет отправлять зашифрованные сообщения, — в хранилище «Доверенные лица». Сертификаты удостоверяющих центров (УЦ) должны быть установлены в хранилище «Доверенные корневыe центры сертификации». При установке сертификата система предлагает два варианта: выбрать хранилище автоматически либо указать вручную. Рекомендую использовать второй вариант, так как автоматика иногда устанавливает сертификат не в то хранилище. Сертификат, которым мы хотим подписывать сообщения, должен иметь закрытый ключ. О наличии закрытого ключа можно узнать, посмотрев на свойства сертификата, где русским по белому будет написано: «есть закрытый ключ для этого сертификата».

Закрытый ключ для сертификата

Закрытый ключ для сертификата

Самое интересное о сертификате мы можем узнать на вкладке «Состав».

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

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

Обрати внимание на поля «Алгоритм подписи», «Алгоритм хеширования подписи» и «Открытый ключ». Если хочешь использовать сертификат для осуществления транзакций в России, во всех этих полях ты должен видеть слово «ГОСТ». Также следует обратить внимание на значение поля «Использование ключа» и поля «Действителен с» и «Действителен по»: первое позволит понять, возможно ли использование сертификата для выполнения нужной нам операции (шифрование, подпись), а второе и третье — возможно ли использовать данный сертификат в указанный момент времени. В дополнение к этому cледует убедиться, что сертификат действителен. В этом нам поможет вкладка «Путь сертификации». Если с сертификатом все хорошо, мы увидим надпись: «Этот сертификат действителен».

Состояние сертификата

Состояние сертификата

Цифровая подпись

Представь, дорогой читатель, что ты занимаешься некой очень ответственной работой. И результаты своей работы отправляешь в виде отчетов, от которых в конечном итоге зависят чьи-то конкретные судьбы и жизни. Получатели твоих отчетов принимают на их основе очень важные решения, и, если ты напортачишь, вполне можешь получить срок. Так вот, в таких ответственных организациях без электронной подписи никуда. Она позволяет тебе подписать тот самый суперважный секретный отчет своим сертификатом с закрытым ключом. Закрытый ключ, в идеале, может храниться на токене — специальном съемном устройстве, похожем на флешку, которое ты в редкие моменты достаешь из сейфа. Подпись гарантирует, что твой отчет отправлен именно тобой, а не уборщицей или сторожем. С другой стороны, ты не сможешь отказаться от авторства (это называется «неотрекаемость») и, если накосячишь в своем суперважном документе, на сторожа свалить вину не получится.

Электронная подпись применяется не только в спецслужбах и органах, но и в бизнесе. Например, для перевода пенсионных накоплений в НПФ: мы генерируем запрос на сертификат, отправляем его в удостоверяющий центр (УЦ). УЦ выпускает сертификат, мы подписываем сертификатом заявление на перевод пенсионных накоплений, отправляем — и вуаля. Подпись также позволяет осуществлять контроль целостности подписываемых данных. Если данные будут изменены, подпись не пройдет проверку.

Для программирования подписи необходимо ознакомиться с несколькими классами .NET Framework:

  • X509Certificate2 — представляет собой сертификат X.509. Имя класса, оканчивающееся на 2, говорит о том, что класс является усовершенствованным аналогом класса X509Certificate.
  • X509Chain.aspx) — позволяет строить и проверять цепочку сертификатов. Необходима для того, чтобы убедиться в действительности сертификата.
  • SignedCms.aspx) — позволяет подписывать и проверять сообщения PKCS#7.

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

Мы проверили сертификат и убедились, что он в порядке. Переходим непосредственно к подписыванию данных. Подпись бывает двух видов: прикрепленная и открепленная.

Прикрепленная и открепленная подписи

Прикрепленная и открепленная подписи

Результатом прикрепленной подписи будет CMS (Cryptography Message Syntax) — сообщение, содержащее как подписываемые данные, так и саму подпись. Открепленная подпись содержит только саму подпись. Рекомендую использовать именно открепленную подпись, потому что с ней намного меньше мороки. В нее проще поставить метку времени, она меньше весит, так как не содержит подписываемые данные. Подписываемые данные легко открыть, посмотреть. В случае прикрепленной подписи для того, чтобы просмотреть подписанные данные, CMS-сообщение необходимо сначала декодировать. В общем, прикрепленной подписи я рекомендую избегать всеми силами. Если потребуется передавать подпись и контент вместе, рассмотри вариант архивирования (вместо использования прикрепленной подписи используй открепленную, просто заархивируй подписываемый файл и открепленную подпись). Посмотрим на код подписи (С#):

Но, как обычно это бывает у Microsoft, стоит сделать маленький шаг в сторону, и розовый волшебный мир рушится

Глядя на примеры кода, можно подумать, что работа с подписью в .NET реализована достаточно хорошо. Но рассмотрим, например, случай, в котором необходимо осуществить подпись большого файла, размером 600 MiB. Внимательные читатели обратили внимание на сигнатуру метода подписи — он принимает на вход массив байтов. При попытке загрузить в массив байтов 600 MiB мы получим OutOfMemoryException.

Что же делать, спросишь ты? Обращаться к основам — отвечу я! Очевидно, раз нельзя загрузить в память 600 MiB, то необходимо файл грузить и обрабатывать по кусочкам. .NET-обертки над CMS так не умеют. На помощь нам приходит MS Crypto API. MS Crypto API содержит два набора функций для работы с CMS: Simplified Message Fuctions и Low Level Message Functions. Для работы с большими файлами нам нужны Low Level. Полную реализацию на C# можно посмотреть тут. Я же предпочитаю работать с криптографией на языке C++. Кода в результате писать приходится меньше, а работает он быстрее. Рассмотрим порядок действий для реализации подписи в поточном режиме:

  • Получаем PCCERT\_CONTEXT;
  • Заполняем структуры CMSG\_STREAM\_INFO, CRYPT\_ALGORITHM\_IDENTIFIER, CMSG\_SIGNER\_ENCODE\_INFO, CMSG\_SIGNED\_ENCODE\_INFO;
  • Получаем хендл сообщения с помощью функции CryptMsgOpenToEncode. Для открепленной подписи необходимо передать соответствующий флаг CMSG\_DETACHED\_FLAG;
  • В цикле вызываем функцию CryptMsgUpdate и «скармливаем» ей по кусочкам файл, который необходимо подписать.

На C++ будет что-то вроде:

Вызов кода на C++ из C# будет выглядеть примерно так:

Внимательный читатель удивится — что это за IStreamReader* inputStream, IStreamWriter* outputStream, ICertificate* signCertificate? Ответ следует из названия переменных, но есть одна тонкость, которая для многих окажется сюрпризом. IStreamReader, IStreamWriter, ICertificate — это интерфейсы, реализованные на C#, и это не COM-объекты. При этом мы спокойно можем вызывать их методы в нативном C++. Как сделать такую красоту — тема отдельной статьи. В результате успешного выполнения операции мы получим криптографическое сообщение. Для кодирования сертификатов X.509 и криптографических сообщений используется Abstract Syntax Notation One, или, по-простому, ASN 1. Для просмотра файлов, закодированных в ASN 1, можно воспользоваться бесплатным ASN.1 Editor.

Подпись изнутри

Подпись изнутри

Проверка подписи и декодирование

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

Если пользователь прислал тебе отчет в виде прикрепленной подписи, тебе для чтения придется его декодировать:

Нетрудно догадаться, что и тут разработчики .NET Framework подложили нам свинью. Не можем мы проверить подпись большого файла! По той же самой причине — OutOfMemoryException. Но и эту проблему несложно решить, обратившись к магии MS Crypto API. Так как код поточной проверки подписи достаточно длинный, остановлюсь на основных моментах:

А так будет выглядеть код проверки подписи при вызове из C#:

Шифрование

Зачем нужно шифрование, все уже знают. PKI нам дает полезную плюшку: мы можем зашифровать один документ так, что расшифровать его смогут несколько получателей. Это очень удобно. Для этого нам нужно иметь сертификаты получателей.

Расшифрование

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

Заключение

В статье не удалось охватить все аспекты PKI, так как их очень много. Тем не менее закодить основные операции ты теперь сможешь без проблем. Разработчикам, которые умеют писать на C#, рекомендую освоить C++ хотя бы на базовом уровне. Это очень пригодится, когда придется работать с нативными функциями. А до многих возможностей ОС по-другому и не добраться, так как .NET реализует весьма ограниченный набор возможностей. Например, .NET не имеет полной поддержки MS Crypto API и CNG (cryptography next geberation), поэтому тебе придется писать тонны P/invoke-кода на С# либо значительно меньше на C++.

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

За кадром остались поточное шифрование и расшифрование, подпись несколькими сертификатами, генерация запросов на сертификат и многое другое, но основу я тебе показал. Код примеров с тестами можно скачать на GitHub.

Share Button
[Всего голосов: 5    Средний: 4.2/5]

Вам может быть интересно также:

Last updated by at .

Leave a Reply

You can use these HTML tags

<a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">