Инструментарий производства компании «лисси софт»[править]
Компания «Лисси СОФТ» — ещё один производитель ПО, предлагающий полный набор инструментов для работы с электронной подписью. Кроме ПО для УЦ и традиционных программных криптопровайдеров в арсенале программных средств «Лисси СОФТ» есть ряд уникальных технических решений. Например, криптопровайдеры для хранения программных токенов в облаке.
Для ПО «Лисси СОФТ» характерно строгое соблюдение стандартов и технических спецификаций. Криптопровайдеры, в том числе уникальные, оформлены в виде библиотек PKCS#11. По заявлениям разработчиков, они хорошо стыкуются с открытым программным обеспечением, также ориентированным на поддержку стандартов. Например, с html-браузерами и утилитами из комплекта OpenSSL.
Применительно к организации доступа к сайтам государственных услуг продукты «Лисси СОФТ» также представляют интерес. В первую очередь — совместимостью с «IFCPlugin» — браузерным плагином, реализующим механизмы ЭЦП на портале Госуслуг. Во-вторых — возможностью ПО удостоверяющих центров работать с запросами на подпись сертификата в формате PKCS#10.
Иногда, для доступа к сайтам, на которых мы планируем применять механизмы электронной подписи, приходится задействовать другие технологии, использующие российскую криптографию. Например, алгоритмы ГОСТ могут использоваться для организации шифрованного канала передачи данных.
В этом случае необходима поддержка соответствующих алгоритмов в браузере. К сожалению, официальные сборки Firefox и Chromium пока не поддерживают российскую криптографию в полной мере. Поэтому приходится пользоваться альтернативными сборками. Такие сборки есть, например, в арсенале криптосредств компаний «КриптоПро» и «Лисси СОФТ», а также, конечно, в дистрибутивах Альт.
Среди сайтов, для работы с которыми необходимо применение технологий электронной подписи, в первую очередь нас интересуют сайты, предоставляющие государственные услуги, а так же электронные торговые площадки (ЭТП). К сожалению, некоторые ЭТП не поддерживают пока работу с ОС из Реестра отечественного ПО. Но ситуация постепенно меняется в лучшую сторону.
Применение ЭЦП для веб сайтов обычно сводится к аутентификации и подписи документов, сформированных в интерфейсе сайта. Принципиально аутентификация выглядит так же, как и подпись любого другого документа: сайт, на котором клиент хочет аутентифицироваться, генерирует последовательность символов, которую отправляет клиенту.
Клиент отправляет обратно выполненную с помощью его секретного ключа подпись этой последовательности и свой сертификат (сертификат — чуть раньше). Сайт берёт из клиентского сертификата публичный ключ и проверяет подпись оригинальной последовательности. То же происходит и при подписи документов. Здесь вместо произвольной последовательности выступает собственно документ.
Краткая аннотация[править]
Вниманию читателей предлагается исследовательская работа, в ходе которой выявлен ряд проблем с применением средств ЭЦП, возникающих у пользователей Linux. В частности, выяснились следующие не вполне очевидные моменты:
- возможность получить и использовать персональный сертификат электронной цифровой подписи для доступа к государственным услугам на сегодняшний день предоставляется только за плату коммерческими организациями;
- носители данных электронной подписи, выданные конечным пользователям разными уполномоченными организациями, могут быть несовместимы как между собой, так и с порталами, предоставляющими доступ к услугам, в том числе — к государственным;
- уровень защищённости носителей данных электронной подписи, массово выдаваемых конечным пользователям, как правило, существенно снижен по отношению к доступному на сегодняшний день технологическому уровню;
- для большинства пользователей ОС из Реестра отечественного ПО механизмы ЭЦП на Едином портале государственных услуг недоступны из-за несовместимости программного обеспечения ЕПГУ и ПО уполномоченных организаций, выдающих персональные сертификаты электронной подписи;
- в некоторых случаях разработчики порталов, предоставляющих государственные услуги, рекомендуют использовать не входящие в Реестр операционные системы, а также программные средства и конфигурации, заведомо снижающие защищённость пользовательских данных.
Авторы работы рассчитывают, что полученные результаты будут полезны пользователям решений, задействующих механизмы ЭЦП, интеграторам, внедряющим соответствующие решения, а также будут приняты во внимание организациями, отвечающими за предоставление государственных информационных услуг и за реализацию конкретных механизмов инфраструктуры ЭЦП, а также разработчиками соответствующего программного и аппаратного обеспечения.
Статья посвящена поддержке электронной цифровой подписи (ЭЦП) документов в ОС ALT, а также специфике применения ЭЦП в Российской федерации.
Основная задача — понять, что нужно сделать «обычному пользователю»™ — не важно, физическому или юридическому лицу, — действующему на общих основаниях, чтобы, работая в ОС из Реестра отечественного ПО, полноценно использовать возможности электронных торговых площадок и порталов государственных услуг.
Под «полноценным использованием» подразумевается в первую очередь возможность аутентификации на соответствующем сайте по сертификату, размещённому на отдельном физическом носителе, и возможность электронной подписи документов, сформированных в интерфейсе сайта.
На эту тему уже проведён ряд исследовательских работ, результатом которых стало заключение о том, что, в принципе, всё работает. Здесь важно понимать, что большинство исследований совместимости с порталами государственных услуг РФ современных криптосредств, работающих под ОС Linux, проводилось в лабораторных условиях.
Криптопро csp[править]
Самый популярный сертифицированный криптопровайдер. Поддерживает программные (или аппаратные в режиме программных) токены, инициализированные с помощью него же или с помощью ПО КриптоПро УЦ. Библиотека PKCS#11 из состава КриптоПро CSP поддерживается ПО «Cades плагин» — популярным браузерным плагином.
Поддерживает практически все продававшиеся и продающиеся на территории РФ программные и аппаратные токены. Правда, в режиме программных — с потенциально извлекаемым закрытым ключом. Потенциально — потому что сообщения об успешных атаках с целью получения закрытого ключа на актуальную версию КриптоПро CSP не публиковались.
Версия под Linux имеет интерфейс командной строки с нестандартным (несовместимым с POSIX.2) синтаксисом. Поддерживается большинство современных дистрибутивов, в том числе ALT.
В версиях КриптоПро CSP до 4.0 включительно применяется ручное управление локальными кэшами для хранения пользовательских сертификатов и сертификатов УЦ. Для полноценной работы с пользовательским сертификатом необходимо, чтобы его копия лежала в одном локальном кэше, а полная цепочка сертификатов УЦ до корневого включительно — в другом.
Технологически эта особенность КриптоПро, строго говоря, не вполне обоснована: цепочку имеет смысл проверять при аутентификации; собственно факт валидности сертификата на возможность подписи не влияет. Тем более, если это наш собственный сертификат и мы знаем, откуда он взялся.
В свежих на момент написания статьи бета-версиях КриптоПро CSP, по словам разработчиков, реализована автоматическая загрузка цепочки сертификатов УЦ. Но за тем, чтобы в локальном кэше пользовательских сертификатов находился только тот, который в данный момент используется, похоже, всё ещё приходится следить вручную.
Сторонние разработчики предпринимают попытки написания графических интерфейсов к КриптоПро CSP, облегчающие проведение рутинных пользовательских операций. Примером такой утилиты может служить rosa-crypto-tool, автоматизирующий подписание и зашифрование документов. Пакет доступен в дистрибутивах ALT.
Самое популярное в России ПО для удостоверяющих центров. Для хранения ключей создаёт на токене контейнер собственного закрытого формата. Для работы с контейнером необходимо ПО КриптоПро CSP. Таким образом, после инициализации с помощью КриптоПро УЦ любой современный токен превращается в программный, поддерживаемый единственным совместимым криптопровайдером. Закрытым, требующим покупки лицензии.
Единственный браузерный плагин для работы с ЭЦП в Linux, поддерживаемый электронными торговыми площадками. Использует для доступа к криптопровайдеру библиотеку PKCS#11 из состава КриптоПро CSP. Соответственно, поддерживает только токены, инициализированные КриптоПро CSP или КриптоПро УЦ.
Другой популярный набор из совместимых друг с другом сертифицированного криптопровайдера и ПО УЦ. Умеет работать с аппаратными токенами, но не даёт преимуществ в части поддержки электронных торговых площадок и сайтов государственных услуг.
Криптопровайдеры[править]
Программное обеспечение, предоставляющее оператору доступ к криптографическим функциям — электронной подписи, зашифрованию, расшифрованию, хэшированию — называется криптопровайдером, провайдером криптографических функций. В случае аппаратного токена криптопровайдер реализован непосредственно на токене, в случае программного токена или в случае хранения ключей на диске компьютера — в виде обычного пользовательского приложения.
С точки зрения защищённости пользовательских данных, на криптопровайдер направлен один из основных векторов атаки — именно в памяти криптопровайдера содержится в расшифрованном виде секретный ключ. В случае успешной атаки злоумышленник сможет подменить код приложения своим и снять копию секретного ключа, даже если ключ в обычном состоянии хранится в зашифрованном виде.
Поэтому в случае применения криптографии для выполнения электронной подписи, имеющей юридическую силу, государство стремится оградить граждан от возможной утечки секретных ключей. Выражается это в том, что для работы с квалифицированной электронной подписью официально разрешено использовать только криптопровайдеры, имеющие соответствующий сертификат и прошедшие, следовательно, соответствующие проверки.
К сертифицированным на территории Российской Федерации для ЭЦП криптопровайдерам относятся, в частности: «Рутокен ЭЦП», «JaCarta ГОСТ», «КриптоПро CSP», «ЛИССИ-CSP», «VipNet CSP». Первые два реализованы непосредственно на аппаратных токенах, остальные — в виде пользовательских приложений.
Кроме набора поддерживаемых алгоритмов, криптопровайдеры различаются также набором криптографических функций — зашифрованию и расшифрованию документов, подписанию и проверке подписи, наличию графического интерфейса пользователя и так далее. Причём, из всего этого набора функций сертифицированный криптопровайдер должен выполнять только те, которые относятся непосредственно к реализации криптографических алгоритмов.
Всё остальное может быть выполнено сторонним приложением. Именно так и работают криптопровайдеры на аппаратных токенах: пользовательский интерфейс реализуется сторонним приложением, не подлежащим обязательной сертификации. Приложение, реализующее пользовательский интерфейс общается с криптопровайдером на токене через другой стандартный интерфейс — PKCS#11.
При этом с точки зрения пользователя работа с ключами происходит, например, непосредственно из html-браузера Firefox. На самом же деле браузер через интерфейс PKCS#11 задействует специальную программную прослойку, в которой реализованы механизмы доступа к конкретному аппаратному токену.
Кроме термина «криптопровайдер» существует ещё один близкий по смыслу термин — «средство криптографической защиты информации» (СКЗИ). Чёткого различия между этими двумя понятиями нет. Первый термин менее официальный, второй чаще используется применительно к сертифицированным техническим решениям.
Открытые инструменты[править]
Открытое программное обеспечение на текущий момент позволяет реализовать практически полный программный стек для работы с ЭЦП.
Реализация PC/SC, разработанная в рамках проекта PCSC lite, является эталонной для ОС семейства Linux. Она входит в состав любого из рассматриваемых в настоящем документе вариантов программного стека для работы с ЭЦП. В дальнейшем, если конкретный вариант реализации не указан, будем считать её задействованной по умолчанию.
Библиотека, реализующая интерфейс PKCS#11, а также набор прикладных инструментов, задействующих её функциональность, была разработана в рамках проекта OpenSC. Инструментарием поддерживается множество аппаратных токенов, среди которых Рутокен ЭЦП, поддержка которого была добавлена специалистами компании «Актив», разрабатывающей токены семейства Рутокен.
С помощью утилит OpenSC можно выполнить на аппаратном токене, в частности, следующие действия:
- инициализировать токен — сбросить настройки к первоначальному состоянию;
- установить PIN-коды администратора и пользователя (если поддерживаются);
- сгенерировать пару ключей (если поддерживается библиотекой PKCS#11);
- импортировать на токен сертификат, подписанный удостоверяющим центром.
Библиотека PKCS#11 из комплекта OpenSC позволяет выполнять на поддерживаемых токенах полный набор операций с электронной подписью. К поддерживаемым токенам относится также Рутокен ЭЦП.
Здесь важно понимать, что для Рутокен ЭЦП существует два разных варианта поддержки программным обеспечением, не совместимых между собой по формату хранения ключей на токене. При использовании библиотеки PKCS#11 из состава OpenSC мы можем пользоваться открытым стеком ПО, а при использовании бесплатно распространяемой закрытой библиотеки производства компании «Актив», — закрытым стеком «Актива».
Чтобы полноценно использовать возможности ЭЦП, кроме собственно библиотеки PKCS#11 должны быть реализованы пользовательские приложения, предоставляющие оператору доступ к функциям библиотеки и возможностям токена. Ярким примером такой реализации является открытое ПО из проекта OpenSSL. В нём поддерживаются, в частности, следующие функции:
- зашифрование данных;
- расшифрование данных;
- подпись документа;
- проверка подписи;
- формирование запроса на подпись сертификата;
- импорт сертификата;
- экспорт сертификата.
Кроме того, с помощью OpenSSL можно реализовать функциональность полноценного удостоверяющего центра, в том числе:
- выдачу клиентских сертификатов путём подписания запросов на подпись в формате PKCS#10;
- отзыв клиентских сертификатов;
- учёт выданных и отозванных сертификатов.
Единственная на текущий момент недоработка OpenSSL, не позволяющая пока реализовать полнофункциональный вариант программного стека ЭЦП на базе открытого ПО — отсутствие открытого модуля для взаимодействия с библиотеками PKCS#11 с поддержкой алгоритмов ГОСТ.
Существует закрытая реализация такого модуля, выполненная в компании «Актив», но она не входит в базовую поставку OpenSSL, и поэтому с выходом новых версий OpenSSL совместимость периодически нарушается. Открытая реализация этого модуля пока не поддерживает алгоритмы ГОСТ.
Кроме OpenSSL взаимодействовать с библиотеками PKCS#11 умеет также всем известный html-браузер Firefox. Для подключения библиотеки PKCS#11 нужно зайти в меню управления настройками «Preferences», далее — в вертикальном списке слева выбрать «Advanced», в горизонтальном списке выбрать «Certificates», нажать кнопку «Security Devices», в появившемся диалоговом окне нажать кнопку «Load».
Появится ещё одно окно с возможностью выбора пути к файлу с библиотекой PKCS#11 и возможностью ввода локального имени для этой конкретной библиотеки. Можно таким образом загрузить несколько разных модулей PKCS#11 для разных типов физических и виртуальных устройств.
К сожалению, функциональности Firefox пока недостаточно для подписи документов в интерфейсе веб-сайта. Поэтому для полноценного открытого программного стека ЭЦП с поддержкой ГОСТ не хватает ещё подключаемого модуля (плагина), позволяющего обращаться к объектам на токене из ПО сайта. Надеемся, что в ближайшее время такой плагин будет написан. Или открыт.
Компания «КриптоПро» в настоящий момент является крупнейшим игроком на рынке ПО для ЭЦП в России. Среди её разработок отметим криптопровайдер «КриптоПро CSP», ПО для обеспечения работы УЦ «КриптоПро УЦ» и браузерный плагин «Cades плагин».
Причина 3
UPD 16.04.2022:
В процессе настройки среды и оборудования выяснилось, что носитель, первым оказавшийся в распоряжении, был вовсе не JaCarta PKI Nano, как ожидалось, а устройство работающее в режиме SafeNet Authentication Client eToken PRO.
UPD 16.04.2022: Некогда Банку требовалось устройство, которое могло бы работать в той же инфраструктуре, что и eToken PRO (Java). В качестве такого устройства компания “ЗАО Аладдин Р.Д.” предложила токен JaCarta PRO, который был выбран банком.
UPD 16.04.2022: Благодарю компанию Аладдин Р.Д., за то что помогли разобраться и установить истину.
В этой ошибке нет никаких политических и скрытых смыслов, а только техническая ошибка сотрудника при подготовке документов. Токен JaCarta PRO является продуктом компании ЗАО “Аладдин Р.Д.”. Апплет, выполняющий функциональную часть, разработан компанией “ЗАО Аладдин Р.Д”.
Этот eToken PRO относился к партии, выпущенной до 1 декабря 2022 года. После этой даты компания «Аладдин Р.Д.» прекратила продажу устройств eToken PRO (Java).
Забегая немного вперед, нужно сказать, что работа с ним настраивалась через соответствующие драйверы — SafenetAuthenticationClient-10.0.32-0.x86_64, которые можно получить только в поддержке Аладдин Р.Д. по отдельной online заявке.
В КриптоПро CSP для работы с этим токеном требовалось установить пакет cprocsp-rdr-emv-64 | EMV/Gemalto support module.
Данный токен определялся и откликался. При помощи утилиты SACTools из пакета SafenetAuthenticationClient можно было выполнить его инициализацию. Но при работе с СКЗИ он вел себя крайне странно и непредсказуемо.
Проявлялось это следующим образом, на команду:
csptest -keyset -cont '\.Aladdin R.D. JaCarta [SCR Interface] (205D325E5842) 00 00alfa_shark' -check
Выдавался ответ, что все хорошо:
[ErrorCode: 0x00000000]
Но сразу после попытки зачитать ключи программно эта же проверка начинала выдавать ошибку:
[ErrorCode: 0x8009001a]
Согласно перечню кодов ошибок объектной модели компонентов Microsoft
NTE_KEYSET_ENTRY_BAD
0x8009001A
Keyset as registered is invalid.
«Невалидный набор ключей» — причина такого сообщения, возможно, кроется либо в старом чипе, прошивке и апплете Gemalto, либо в их драйверах для ОС, которые не поддерживают новые стандарты формирования ЭП и функции хэширования ГОСТ Р 34.10-2022 и ГОСТ Р 34.11-2022.
В таком состоянии токен блокировался. СКЗИ начинало показывать неактуальное состояние считывателя и ключевого контейнера. Перезапуск службы криптографического провайдера cprocsp, службы работы с токенами и смарт-картами pcscd и всей операционной системы не помогали, только повторная инициализация.
Справедливости ради требуется отметить, что SafeNet eToken PRO корректно работал с ключами ГОСТ Р 34.10-2001 в ОС Windows 7 и 10.
Можно было бы попробовать установить СКЗИ КриптоПро CSP 4.0 ФКН (Gemalto), но целевая задача — защитить наши ключи ЭП и шифрования с помощью сертифицированных ФСБ и ФСТЭК изделий семейства JaCarta, в которых поддерживаются новые стандарты.
Проблему удалось решить, взяв настоящий токен JaCarta PKI в (XL) обычном корпусе.
Но на попытки заставить работать Safenet eToken PRO времени было потрачено немало. Хотелось обратить на это внимание и, возможно, кого-то оградить от подобного.
Программная часть[править]
Программный стек для работы с электронной подписью состоит из следующих компонентов:
- реализации интерфейса для низкоуровневого доступа к хранилищам контейнеров с ключами — например, интерфейса PC/SC для доступа к физическим устройствам — токенам;
- модуля, реализующего интерфейс PKCS#11 для взаимодействия с криптопровайдером — например, выполненном на аппаратном токене;
- криптопровайдера, реализующего соответствующие криптографические алгоритмы и выполняющего действия с ними — например, электронную подпись или шифрование данных;
- пользовательского приложения, взаимодействующего с другой — по отношению к криптопровайдеру — стороны с модулем PKCS#11, и выполняющего от имени пользователя такие операции, как электронная подпись или аутентификация.
Пример стека:
- открытое программное обеспечение pcsc-lite реализует интерфейс PC/SC для взаимодействия с аппаратным токеном «Рутокен ЭЦП»;
- библиотека libcppksc11.so из состава ПО «КриптоПро CSP» обеспечивает взаимодействие с размещённым на токене контейнером, в котором хранится закрытый ключ и сертификат пользователя;
- приложение командной строки cryptcp из состава ПО «КриптоПро CSP» взаимодействует с библиотекой libcppksc11.so через интерфейс PKCS#11 и осуществляет возможность подписания документов секретным ключом пользователя; при этом функции криптопровайдера полностью реализованы в компонентах ПО «КриптоПро CSP», криптопровайдер аппаратного токена не задействуется.
Другой пример:
- открытое программное обеспечение pcsc-lite реализует интерфейс PC/SC для взаимодействия с аппаратным токеном «Рутокен ЭЦП»;
- библиотека libcppksc11.so из состава ПО «КриптоПро CSP» обеспечивает взаимодействие с размещённым на токене контейнером, в котором хранится закрытый ключ и сертификат пользователя;
- приложение «КриптоПро ЭЦП Browser plugin», работающее в составе html-браузера Firefox взаимодействует с библиотекой libcppksc11.so через интерфейс PKCS#11 и осуществляет возможность подписания документов в интерфейсе браузера на сайтах электронных торговых площадок; при этом функции криптопровайдера полностью реализованы в компонентах ПО «КриптоПро CSP», криптопровайдер аппаратного токена не задействуется.
Третий пример:
- открытое программное обеспечение pcsc-lite реализует интерфейс PC/SC для взаимодействия с аппаратным токеном «Рутокен ЭЦП»;
- библиотека librtpksc11.so производства компании «Актив» обеспечивает взаимодействие с криптопровайдером токена;
- криптопровайдер токена осуществляет функции подписи секретным ключом передаваемых ему данных; секретный ключ при этом не покидает пределов токена;
- приложение «Рутокен Плагин», работающее в составе html-браузера Firefox взаимодействует с библиотекой librtpksc11.so через интерфейс PKCS#11 и осуществляет возможность подписания документов в интерфейсе браузера на совместимых сайтах; при этом функции криптопровайдера полностью реализованы на аппаратном токене.
Выбор конкретной реализации стека в данный момент определяется, в первую очередь, тремя факторами — предполагаемой областью применения ЭЦП, уровнем технической грамотности пользователя и готовностью удостоверяющего центра работать с запросом на подпись сертификата.
Кроме перечисленного выше набора программного обеспечения, работающего на стороне пользователя, существует ещё два приложения, особенности которых необходимо учитывать. Первое — ПО, обслуживающее удостоверяющий центр. Второе — ПО, с которым мы хотим быть совместимыми. Например, сайт Госуслуг. Или ПО для подписи электронных документов.
Если удостоверяющий центр, в котором мы планируем получить сертификат, не готов работать с запросами на подпись в формате PKCS#10 и умеет работать только с программными токенами (то есть, воспринимает любой токен как программный), у нас не остаётся выбора.
Как правило, в этом случае ПО УЦ сгенерирует для нас пару ключей, запишет её на токен, тут же сгенерирует на базе открытого ключа и наших персональных данных запрос на подпись, подпишет его, и сохранит сертификат на токене. Сертификат и ключ при этом будут находиться в контейнере закрытого формата, известного только разработчику ПО УЦ.
Соответственно, для доступа к контейнеру с ключами придётся покупать криптопровайдер того же разработчика. И область применения ЭЦП будет ограничена возможностями ПО одного конкретного разработчика. Покупать в этом случае аппаратный токен смысла нет — можно обойтись программным. Токен в этом случае обязательно придётся нести в УЦ.
Если удостоверяющий центр, в котором мы планируем получить сертификат, готов работать с запросами на подпись в формате PKCS#10, то нам не важно, какое именно ПО используется в этом УЦ. Мы сможем использовать у себя тот криптопровайдер, который совместим с нашими целевыми приложениями.
Например, с электронными торговыми площадками или сайтом Госуслуг. Нести токен в УЦ в этом случае не нужно, достаточно сгенерировать запрос на подпись и предъявить его там вместе со своими бумажными документами; получить сертификат, и самостоятельно сохранить его на токене с помощью выбранного криптопровайдера.
К сожалению, очень немногие УЦ в данный момент (конец 2022 года) готовы работать с запросом на подпись. Отчасти такая ситуация обусловлена недостаточным уровнем технической подготовки пользователей, которые не в состоянии проследить за тем, чтобы запрос на подпись был оформлен надлежащим образом — с указанием всех необходимых атрибутов и их значений. На решение этой проблемы направлено, в том числе, данное руководство.
Угрозы и атаки[править]
В отличие от шифрования, в случае электронной подписи основная задача атакующего сводится не к получению расшифрованного текста, а к возможности подделать или произвести электронную подпись произвольного документа. То есть, условно говоря, не к расшифрованию, а к зашифрованию.
По стандартам, принятым в Российской Федерации, при электронной подписи документов применяются криптографические алгоритмы, соответствующие Государственному стандарту РФ (ГОСТ Р). На момент написания данного текста (вторая половина 2022 года) ни для одного из алгоритмов, имеющих отношение к ЭЦП и имеющих в РФ статус стандарта, неизвестны способы атак, существенно отличающиеся по трудоёмкости от простого перебора.
Таким образом, в случае электронной подписи основные векторы атаки направлены на получение атакующим секретного ключа. Если ключ хранится в файле на диске, украсть его можно в любой момент, получив соответствующий доступ на чтение. Например, с помощью вируса.
Если ключ хранится в зашифрованном виде, получить его можно в момент применения — например, когда программа, выполняющая электронную подпись, уже расшифровала ключ и обрабатывает им данные. В данном случае задача существенно усложняется — нужно найти уязвимость в программе, или ключ придётся расшифровывать самостоятельно.
Ещё более существенно усложнить задачу получения злоумышленником секретного ключа можно, разместив секретный ключ на отдельном аппаратном носителе таким образом, чтобы ключ никогда не покидал пределов этого физического устройства. В таком случае получить доступ к ключу злоумышленник сможет только похитив физическое устройство.
Пропажа устройства будет сигналом для владельца о том, что ключ похищен. В любом другом случае — и это важнейший нюанс — похищение ключа может остаться незамеченным, и владелец ключа не узнает вовремя, что необходимо срочно обратиться в УЦ и отозвать действие своего сертификата.
Здесь опять таки уместна аналогия с банковской картой, которая является средством аутентификации для доступа к банковскому счёту. Пока она у владельца, он спокоен. Если карта теряется — нужно срочно её заблокировать. Задача злоумышленника при этом — не украсть карту, а незаметно сделать её копию, чтобы владелец не заблокировал доступ. Для современных аппаратных токенов способы клонирования на текущий момент неизвестны.
В данный момент общепринятый термин для обозначения отдельных физических устройств, используемых для хранения ключей электронной подписи — токен. Токены могут подключаться к компьютеру через интерфейсы USB, Bluetooth, или через специальные устройства-считыватели.
Большинство современных токенов используют интерфейс USB. Но главное различие между типами токенов не в интерфейсе подключения. Токены можно разделить на два типа — те, которые используются фактически только как хранилище ключей, и те, которые «умеют» выполнять криптографические операции собственными средствами.
Токены первого типа отличаются от обычных flash-накопителей по сути только тем, что считать с них данные можно только с помощью специального программного обеспечения. В остальном — это обычное внешнее хранилище данных, и, если мы храним в нём секретный ключ, то украсть его может любой, кто получит права на доступ к устройству и будет знать, как считать с него ключ.
Токены второго типа называются аппаратными, и основное их отличие в том, что секретный ключ является неизвлекаемым — он никогда не покидает пределов токена. Для этого на токене размещается специальный набор программного обеспечения, который активируется в момент подключения токена к компьютеру.
Секретный ключ никогда не покидает пределов аппаратного токена, потому что он генерируется прямо на самом токене. Для подписи документа в токен загружается хэш документа, прямо «на борту» токена производятся вычисления с помощью хранящегося там же секретного ключа, и готовая подпись выгружается обратно. Таким образом, зная местонахождение токена, мы всегда знаем местонахождение секретного ключа.
Одной из основных характеристик аппаратного токена является набор поддерживаемых криптографических алгоритмов. Например, если мы хотим использовать аппаратный токен для аутентификации на своём домашнем компьютере, подойдёт любой современный токен. А если мы хотим аутентифицироваться на портале Госуслуг, то необходим токен, поддерживающий сертифицированные в России криптографические алгоритмы.
Ниже приведены списки поддерживаемых криптографических механизмов для токенов eToken и JaCarta ГОСТ. Для запроса списка механизмов применена открытая утилита pkcs11-tool с параметром «-M» (от слова «mechanism»), которая может выступать в роли клиентского приложения для любой библиотеки, реализующей в её сторону интерфейс PKCS#11.
В качестве библиотек PKCS#11 применены libeToken.so и libjcPKCS11.so.1 для eToken и JaCarta соответственно. Библиотека для eToken распространяется в составе ПО «SafeNet», библиотека для JaCarta доступна для загрузки с сайта компании «Аладдин Р.Д.»
$ pkcs11-tool --module /usr/local/lib64/libeToken.so.9.1.7 -M Supported mechanisms: DES-MAC, keySize={8,8}, sign, verify DES-MAC-GENERAL, keySize={8,8}, sign, verify DES3-MAC, keySize={24,24}, sign, verify DES3-MAC-GENERAL, keySize={24,24}, sign, verify AES-MAC, keySize={16,32}, sign, verify AES-MAC-GENERAL, keySize={16,32}, sign, verify RC4, keySize={8,2048}, encrypt, decrypt DES-ECB, keySize={8,8}, encrypt, decrypt, wrap, unwrap DES-CBC, keySize={8,8}, encrypt, decrypt, wrap, unwrap DES-CBC-PAD, keySize={8,8}, encrypt, decrypt, wrap, unwrap DES3-ECB, keySize={24,24}, hw, encrypt, decrypt, wrap, unwrap DES3-CBC, keySize={24,24}, hw, encrypt, decrypt, wrap, unwrap DES3-CBC-PAD, keySize={24,24}, hw, encrypt, decrypt, wrap, unwrap AES-ECB, keySize={16,32}, encrypt, decrypt, wrap, unwrap AES-CBC, keySize={16,32}, encrypt, decrypt, wrap, unwrap AES-CBC-PAD, keySize={16,32}, encrypt, decrypt, wrap, unwrap mechtype-0x1086, keySize={16,32}, encrypt, decrypt, wrap, unwrap mechtype-0x1088, keySize={16,32}, encrypt, decrypt, wrap, unwrap RSA-PKCS-KEY-PAIR-GEN, keySize={1024,2048}, hw, generate_key_pair RSA-PKCS, keySize={1024,2048}, hw, encrypt, decrypt, sign, sign_recover, verify, verify_recover, wrap, unwrap RSA-PKCS-OAEP, keySize={1024,2048}, hw, encrypt, decrypt, wrap, unwrap RSA-PKCS-PSS, keySize={1024,2048}, hw, sign, verify SHA1-RSA-PKCS-PSS, keySize={1024,2048}, hw, sign, verify mechtype-0x43, keySize={1024,2048}, hw, sign, verify mechtype-0x44, keySize={1024,2048}, hw, sign, verify mechtype-0x45, keySize={1024,2048}, hw, sign, verify RSA-X-509, keySize={1024,2048}, hw, encrypt, decrypt, sign, sign_recover, verify, verify_recover, wrap, unwrap MD5-RSA-PKCS, keySize={1024,2048}, hw, sign, verify SHA1-RSA-PKCS, keySize={1024,2048}, hw, sign, verify SHA256-RSA-PKCS, keySize={1024,2048}, hw, sign, verify SHA384-RSA-PKCS, keySize={1024,2048}, hw, sign, verify SHA512-RSA-PKCS, keySize={1024,2048}, hw, sign, verify RC4-KEY-GEN, keySize={8,2048}, generate DES-KEY-GEN, keySize={8,8}, generate DES2-KEY-GEN, keySize={16,16}, generate DES3-KEY-GEN, keySize={24,24}, generate AES-KEY-GEN, keySize={16,32}, generate PBE-SHA1-RC4-128, keySize={128,128}, generate PBE-SHA1-RC4-40, keySize={40,40}, generate PBE-SHA1-DES3-EDE-CBC, keySize={24,24}, generate PBE-SHA1-DES2-EDE-CBC, keySize={16,16}, generate GENERIC-SECRET-KEY-GEN, keySize={8,2048}, hw, generate PBA-SHA1-WITH-SHA1-HMAC, keySize={160,160}, hw, generate PBE-MD5-DES-CBC, keySize={8,8}, generate PKCS5-PBKD2, generate MD5-HMAC-GENERAL, keySize={8,2048}, sign, verify MD5-HMAC, keySize={8,2048}, sign, verify SHA-1-HMAC-GENERAL, keySize={8,2048}, sign, verify SHA-1-HMAC, keySize={8,2048}, sign, verify mechtype-0x252, keySize={8,2048}, sign, verify mechtype-0x251, keySize={8,2048}, sign, verify mechtype-0x262, keySize={8,2048}, sign, verify mechtype-0x261, keySize={8,2048}, sign, verify mechtype-0x272, keySize={8,2048}, sign, verify mechtype-0x271, keySize={8,2048}, sign, verify MD5, digest SHA-1, digest SHA256, digest SHA384, digest SHA512, digest mechtype-0x80006001, keySize={24,24}, generate
$ pkcs11-tool --module /usr/local/lib64/libjcPKCS11.so.1 -M Supported mechanisms: GOSTR3410-KEY-PAIR-GEN, hw, generate_key_pair GOSTR3410, hw, sign, verify GOSTR3410-WITH-GOSTR3411, hw, sign, verify mechtype-0x1204, hw, derive GOSTR3411, hw, digest mechtype-0x1220, generate mechtype-0xC4321101 mechtype-0xC4321102 mechtype-0xC4321103 mechtype-0xC4321104 mechtype-0xC4900001
Видно, что список поддерживаемых механизмов для eToken очень длинный, но не включает алгоритмы ГОСТ. Список поддерживаемых механизмов JaCarta включает только алгоритмы ГОСТ, но зато в объёме, необходимом для реализации функциональности ЭЦП на аппаратном токене.
Важно понимать, что современные аппаратные токены, как правило, можно использовать также и в качестве программных. То есть, на них обычно есть небольшой участок памяти, доступный извне, который при желании можно использовать для записи и хранения сгенерированного снаружи секретного ключа.
Технологически в этом нет никакого смысла, но фактически такой метод используется, к сожалению, довольно широко. К сожалению, потому что зачастую владелец токена не знает, что его современный аппаратный токен, честно купленный не за символическую плату, используется как программный.
В качестве примеров исключительно программных токенов можно привести «Рутокен S» и «Рутокен Lite». В качестве примеров аппаратных токенов, не поддерживающих сертифицированные в России криптографические алгоритмы, — устройства «eToken»; в качестве поддерживающих российскую криптографию — «Рутокен ЭЦП», «JaCarta ГОСТ».
Установка пакетов криптопро csp
При установке КриптоПро CSP по умолчанию нужные пакеты для работы с токенами и смарт-картами отсутствуют.
zypper search cprocsp
Выполняем установку в CSP компонента поддержки JaCarta components for CryptoPro CSP
zypper install cprocsp-rdr-jacarta-64-3.6.408.683-4.x86_64.rpm
Некоторые компоненты имеют зависимости. Так, например, если попытаться выполнить установку пакета поддержки SafeNet eToken PRO cprocsp-rdr-emv-64-4.0.9944-5.x86_64.rpm — EMV/Gemalto support module, то получим сообщение о необходимости сначала установить базовый компонент CSP поддержки считывателей cprocsp-rdr-pcsc-64-4.0.9944-5.x86_64.rpm — PC/SC components for CryptoPro CSP readers:
zypper install cprocsp-rdr-emv-64-4.0.9944-5.x86_64.rpm
Loading repository data...
Reading installed packages...
Resolving package dependencies...
Problem: nothing provides cprocsp-rdr-pcsc-64 >= 4.0 needed by cprocsp-rdr-emv-64-4.0.9944-5.x86_64
Solution 1: do not install cprocsp-rdr-emv-64-4.0.9944-5.x86_64
Solution 2: break cprocsp-rdr-emv-64-4.0.9944-5.x86_64 by ignoring some of its dependencies
Choose from above solutions by number or cancel [1/2/c] (c): c
Устанавливаем базовые пакеты поддержки считывателей и ключевых носителей:
zypper install cprocsp-rdr-pcsc-64-4.0.9944-5.x86_64.rpm
zypper install lsb-cprocsp-pkcs11-64-4.0.9944-5.x86_64.rpm
Теперь можно установить модули для работы с остальными видами носителей и компонент GUI:
zypper install cprocsp-rdr-emv-64-4.0.9944-5.x86_64.rpm
zypper install cprocsp-rdr-novacard-64-4.0.9944-5.x86_64.rpm
zypper install cprocsp-rdr-mskey-64-4.0.9944-5.x86_64.rpm
zypper install cprocsp-rdr-gui-gtk-64-4.0.9944-5.x86_64.rpm
Проверяем итоговую конфигурацию КриптоПро CSP:
zypper search cprocsp
Loading repository data...
Reading installed packages...
S | Name | Summary | Type— —————————– —————————————————- ——–i | cprocsp-curl-64 | CryptoPro Curl shared library and binaris. Build 9944. | packagei | cprocsp-rdr-emv-64 | EMV/Gemalto support module | packagei | cprocsp-rdr-gui-gtk-64 | GUI components for CryptoPro CSP readers. Build 9944. | packagei | cprocsp-rdr-jacarta-64 | JaCarta components for CryptoPro CSP. Build 683. | packagei | cprocsp-rdr-mskey-64 | Mskey support module | packagei | cprocsp-rdr-novacard-64 | Novacard support module | packagei | cprocsp-rdr-pcsc-64 | PC/SC components for CryptoPro CSP readers. Build 9944.| packagei | lsb-cprocsp-base | CryptoPro CSP directories and scripts. Build 9944. | packagei | lsb-cprocsp-ca-certs | CA certificates. Build 9944. | packagei | lsb-cprocsp-capilite-64 | CryptoAPI lite. Build 9944. | packagei | lsb-cprocsp-kc2-64 | CryptoPro CSP KC2. Build 9944. | packagei | lsb-cprocsp-pkcs11-64 | CryptoPro PKCS11. Build 9944. | packagei | lsb-cprocsp-rdr-64 | CryptoPro CSP readers. Build 9944. | package
Чтобы применить изменения, выполняем перезапуск службы криптографического провайдера и проверяем ее статус:
/etc/init.d/cprocsp restart
/etc/init.d/cprocsp status
Читаем закрытый ключ и конвертируем
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include "gost_lcl.h"
/* Convert little-endian byte array into bignum */
BIGNUM *reverse32bn(char *b, BN_CTX *ctx)
{
BIGNUM *res;
char buf[32];
BUF_reverse(buf, b, 32);
res = BN_bin2bn(buf, 32, BN_CTX_get(ctx));
OPENSSL_cleanse(buf, sizeof(buf));
return res;
}
void xor_material(char *buf36, char *buf5C, char *src)
{
int i;
for(i = 0; i < 32; i )
{
buf36[i] = src[i] ^ 0x36;
buf5C[i] = src[i] ^ 0x5C;
}
}
int make_pwd_key(char *result_key, char *start12, int start12_len, char *passw)
{
int result;
int i;
char pincode4[1024];
int pin_len;
char current[32];
char material36[32];
char material5C[32];
char hash_result[32];
gost_hash_ctx ctx;
init_gost_hash_ctx(&ctx, &GostR3411_94_CryptoProParamSet);
memset(pincode4, 0, sizeof(pincode4));
pin_len = strlen(passw);
if (pin_len*4 > sizeof(pincode4)) { result = 1; goto err; }
for(i = 0; i < pin_len; i )
pincode4[i*4] = passw[i];
start_hash(&ctx);
hash_block(&ctx, start12, start12_len);
if (pin_len)
hash_block(&ctx, pincode4, pin_len * 4);
finish_hash(&ctx, hash_result);
memcpy(current, (char*)"DENEFH028.760246785.IUEFHWUIO.EF", 32);
for(i = 0; i < (pin_len?2000:2); i )
{
xor_material(material36, material5C, current);
start_hash(&ctx);
hash_block(&ctx, material36, 32);
hash_block(&ctx, hash_result, 32);
hash_block(&ctx, material5C, 32);
hash_block(&ctx, hash_result, 32);
finish_hash(&ctx, current);
}
xor_material(material36, material5C, current);
start_hash(&ctx);
hash_block(&ctx, material36, 32);
hash_block(&ctx, start12, start12_len);
hash_block(&ctx, material5C, 32);
if (pin_len)
hash_block(&ctx, pincode4, pin_len * 4);
finish_hash(&ctx, current);
start_hash(&ctx);
hash_block(&ctx, current, 32);
finish_hash(&ctx, result_key);
result = 0; //ok
err:
return result;
}
BIGNUM *decode_primary_key(char *pwd_key, char *primary_key, BN_CTX *bn_ctx)
{
BIGNUM *res;
char buf[32];
gost_ctx ctx;
gost_init(&ctx, gost_cipher_list->sblock);
gost_key(&ctx, pwd_key);
gost_dec(&ctx, primary_key, buf, 4);
res = reverse32bn(buf, bn_ctx);
OPENSSL_cleanse(buf, sizeof(buf));
return res;
}
BIGNUM *remove_mask_and_check_public(char *oid_param_set8, BIGNUM *key_with_mask, BIGNUM *mask, char *public8, BN_CTX *ctx)
{
int result;
EC_KEY *eckey = NULL;
const EC_POINT *pubkey;
const EC_GROUP *group;
BIGNUM *X, *Y, *order, *raw_secret, *mask_inv;
char outbuf[32], public_X[32];
ASN1_OBJECT *obj;
int nid;
order = BN_CTX_get(ctx);
mask_inv = BN_CTX_get(ctx);
raw_secret = BN_CTX_get(ctx);
X = BN_CTX_get(ctx);
Y = BN_CTX_get(ctx);
if (!order || !mask_inv || !raw_secret || !X || !Y) { result = 1; goto err; }
obj = ASN1_OBJECT_create(0, oid_param_set8 1, *oid_param_set8, NULL, NULL);
nid = OBJ_obj2nid(obj);
ASN1_OBJECT_free(obj);
if (!(eckey = EC_KEY_new())) { result = 1; goto err; }
if (!fill_GOST2001_params(eckey, nid)) { result = 1; goto err; }
if (!(group = EC_KEY_get0_group(eckey))) { result = 1; goto err; }
if (!EC_GROUP_get_order(group, order, ctx)) { result = 1; goto err; }
if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }
if (!EC_KEY_set_private_key(eckey, raw_secret)) { result = 1; goto err; }
if (!gost2001_compute_public(eckey)) { result = 1; goto err; }
if (!(pubkey = EC_KEY_get0_public_key(eckey))) { result = 1; goto err; }
if (!EC_POINT_get_affine_coordinates_GFp(group, pubkey, X, Y, ctx)) { result = 1; goto err; }
store_bignum(X, outbuf, sizeof(outbuf));
BUF_reverse(public_X, outbuf, sizeof(outbuf));
if (memcmp(public_X, public8, 8) != 0) { result = 1; goto err; }
result = 0; //ok
err:
if (eckey) EC_KEY_free(eckey);
if (result == 0) return raw_secret;
return NULL;
}
int file_length(char *fname)
{
int len;
FILE *f = fopen(fname, "rb");
if (f == NULL) return -1;
fseek(f, 0, SEEK_END);
len = ftell(f);
fclose(f);
return len;
}
int read_file(char *fname, int start_pos, char *buf, int len)
{
int read_len;
FILE *f = fopen(fname, "rb");
if (f == NULL) return 1;
if (start_pos) fseek(f, start_pos, SEEK_SET);
read_len = fread(buf, 1, len, f);
fclose(f);
if (read_len != len) return 1;
return 0; //ok
}
int get_asn1_len(unsigned char *buf, int *size_hdr)
{
int n, i, res;
int pos = 0;
if ((buf[pos]&0x80) == 0) {
*size_hdr = 1;
return buf[pos];
}
n = buf[pos ]&0x7f;
res = 0;
for(i = 0; i < n; i ) {
res = res*256 buf[pos ];
}
*size_hdr = n 1;
return res;
}
#define MAX_HEADER 20000
int read_container(char *fpath, int flag2, char *salt12, char *primary_key, char *masks_key, char *public8, char *oid_param_set8)
{
int result;
char primary_path[1024 30];
char masks_path[1024 30];
char header_path[1024 30];
char header_buf[MAX_HEADER];
int header_len;
int i, len, pos, size_hdr;
if (strlen(fpath)>1024) { result = 1; goto err; }
sprintf(header_path, "%s/header.key", fpath);
if (flag2 == 0)
{
sprintf(primary_path, "%s/primary.key", fpath);
sprintf(masks_path, "%s/masks.key", fpath);
}
else
{
sprintf(primary_path, "%s/primary2.key", fpath);
sprintf(masks_path, "%s/masks2.key", fpath);
}
if (read_file(primary_path, 4, primary_key, 32)) { result = 1; goto err; }
if (read_file(masks_path, 4, masks_key, 32)) { result = 1; goto err; }
if (read_file(masks_path, 0x26, salt12, 12)) { result = 1; goto err; }
header_len = file_length(header_path);
if (header_len < 0x42 || header_len > MAX_HEADER) { result = 1; goto err; }
if (read_file(header_path, 0, header_buf, header_len)) { result = 1; goto err; }
//------------- skip certificate ---------------------------
pos = 0;
for(i = 0; i < 2; i )
{
get_asn1_len(header_buf pos 1, &size_hdr);
pos = size_hdr 1;
if (pos > header_len-8) { result = 2; goto err; }
}
//------------------ get oid_param_set8 -----------------------
#define PARAM_SET_POS 34
if (memcmp(header_buf pos PARAM_SET_POS, "x6x7", 2) != 0) { result = 2; goto err; }
memcpy(oid_param_set8, header_buf pos PARAM_SET_POS 1, 8);
//------------------ get public8 -----------------------
result = 2; //not found
pos = 52;
for(i = 0; i < 3; i )
{
len = get_asn1_len(header_buf pos 1, &size_hdr);
if (len == 8 && memcmp(header_buf pos, "x8ax8", 2) == 0)
{
memcpy(public8,header_buf pos 2,8);
result = 0; //ok
break;
}
pos = len size_hdr 1;
if (pos > header_len-8) { result = 2; goto err; }
}
err:
OPENSSL_cleanse(header_buf, sizeof(header_buf));
return result;
}
#define START_OID 0x12
#define START_KEY 0x28
unsigned char asn1_private_key[72] = {
0x30,0x46,2,1,0,0x30,0x1c,6,6,0x2a,0x85,3,2,2,0x13,0x30,0x12,6,7,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,6,7,0x2a,0x85,3,2,2,0x1e,1,4,0x23,2,0x21,0
};
int main(int argc, char **argv)
{
int result;
char *container_path;
char *passw;
char salt12[12];
char primary_key[32];
char masks_key[32];
char public8[8];
char oid_param_set8[8];
BN_CTX *ctx;
BIGNUM *key_with_mask;
BIGNUM *mask;
BIGNUM *raw_key;
char pwd_key[32];
char outbuf[32];
ctx = BN_CTX_new();
if (argc == 2)
{
container_path = argv[1];
passw = "";
}
else
if (argc == 3)
{
container_path = argv[1];
passw = argv[2];
}
else
{
printf("get_private container_path [passw]n");
result = 1;
goto err;
}
if (read_container(container_path, 0, salt12, primary_key, masks_key, public8, oid_param_set8) != 0 &&
read_container(container_path, 1, salt12, primary_key, masks_key, public8, oid_param_set8) != 0)
{
printf("can not read container from %sn", container_path);
result = 2;
goto err;
}
make_pwd_key(pwd_key, salt12, 12, passw);
key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);
OPENSSL_cleanse(pwd_key, sizeof(pwd_key));
mask = reverse32bn(masks_key, ctx);
raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);
if (raw_key)
{
BIO *bio;
store_bignum(raw_key, outbuf, sizeof(outbuf));
memcpy(asn1_private_key START_OID, oid_param_set8, 8);
memcpy(asn1_private_key START_KEY, outbuf, 32);
//bio = BIO_new_file("private.key", "w");
bio = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT);
PEM_write_bio(bio, "PRIVATE KEY", "", asn1_private_key, sizeof(asn1_private_key));
BIO_free(bio);
OPENSSL_cleanse(outbuf, sizeof(outbuf));
OPENSSL_cleanse(asn1_private_key, sizeof(asn1_private_key));
result = 0; //ok
}
else
{
printf("Error check public keyn");
result = 3;
}
err:
BN_CTX_free(ctx);
OPENSSL_cleanse(salt12, sizeof(salt12));
OPENSSL_cleanse(primary_key, sizeof(primary_key));
OPENSSL_cleanse(masks_key, sizeof(masks_key));
return result;
}