Интеграция .NET-приложения с внешним API по ГОСТ TLS через CryptoPro
Всем привет. Представлюсь — меня зовут Евгений Думчев и я Team Lead .NET разработки в DDPlanet.
В какой-то момент в моей практике появилась задача по интеграции с внешним API. Для взаимодействия требовалось применять предоставленный публичный доверенный сертификат сервера .cer и клиентский .pfx сертификат. Особенность в том, что .pfx сертификат был выпущен через CryptoPro CSP — а это вносит свои тонкости в процесс интеграции.
В этой статье я расскажу, как интегрировать .NET-приложение с внешним API, требующему двусторонней TLS-аутентификации по национальным криптографическим стандартам (ГОСТ) с использованием сертификатов, выпущенных через CryptoPro CSP.
Безопасность и сферы применения двустороннего TLS с CryptoPro
При работе с государственными или банковскими API в РФ часто предъявляются требования к применению национальных криптографических стандартов. В таких случаях сертификаты формируются через CryptoPro CSP и содержат ГОСТ-алгоритмы, которые отличают эти сертификаты от обычных X.509 сертификатов, применяемых в международной практике.
Более того, для повышения уровня безопасности при взаимодействии по API может применяться двусторонняя TLS-аутентификация (mTLS), которая обеспечивает повышенный уровень доверия между клиентом и сервером. Это означает, что:
- Сервер предоставляет свой сертификат — клиент проверяет, доверять ли серверу.
- Клиент предъявляет свой сертификат (client certificate) — сервер проверяет его подлинность.
Работает это по следующему принципу:
- При вызове внешнего API, клиент (в нашем случае .NET Web API приложение) выполняет запрос соединения с сервером.
- В процессе TLS-рукопожатия сервер отсылает клиенту свой публичный сертификат.
- Клиент проверяет валидность публичного сертификата, сравнивая его с заранее известным и доверенным сертификатом сервера (например, .cer файлом) или по цепочке доверия к корневому УЦ. Если сертификат соответствует ожидаемому, то можно доверять этому серверу.
- Так как на стороне сервера включена двусторонняя аутентификация — он отправляет запрос о необходимости предоставления клиентского сертификата.
- Клиент отправляет публичный сертификат (и при необходимости цепочку) из контейнера .pfx и подтверждает владение приватным ключом из контейнера .pfx.
- Сервер проверяет подпись и цепочку доверия, а также сверяет, выдан ли сертификат от доверенного УЦ.
- Если все проверки пройдены успешно — устанавливается защищенное соединение — API становится доступным и клиенту можно выполнять обработку запросов.
Сертификат .pfx (Personal Information Exchange) применяется из соображений безопасности. Это стандартизированный двоичный формат контейнера сертификатов PKCS #12 (Public-Key Cryptography Standards). Он представляет собой единый файл, который включает в себя закрытый ключ (private key), открытый ключ / сертификат (public key) и цепочку доверия (промежуточные и корневые сертификаты). К особенностям можно отнести то, что содержимое .pfx-файла шифруется и защищается паролем.
Дополнительно .pfx-файл может быть экспортирован с включенной опцией «расширенной защиты», что позволяет повысить уровень безопасности хранения закрытого ключа внутри контейнера. Это достигается посредством применения более стойкого шифрования и добавления ограничения на экспорт — даже после импорта сертификата его нельзя будет снова выгрузить с приватным ключом.
Получаем следующую схему взаимодействия:

С таким уровнем безопасности с применением CryptoPro чаще всего можно столкнуться при взаимодействии с отечественным ПО в следующих сферах:
- Системы электронного документооборота (ЭДО).
- Порталы государственных услуг.
- Банковские и финансовые API.
- Торговые площадки, работающие с ЭЦП (электронной подписью).
- Юридически значимые интеграции — в которых важно соблюдение требований к квалифицированной электронной подписи (КЭП).
Особенности взаимодействия с API, использующим сертификат CryptoPro
При взаимодействии со сторонней API по REST в нашем случае должен использоваться .pfx сертификат, созданный через CryptoPro. Это означает, что алгоритмы и структуры сертификата могут содержать специфичные для ГОСТ и CryptoPro криптографические идентификаторы OID, например, 1.2.840.113549.1.12.1.80, которые не поддерживаются большинством стандартных библиотек (например, OpenSSL, curl). Что требует использования именно CryptoPro или его оберток для корректной работы.
У сторонней API предусмотрена Swagger-документация, но она так просто не доступна в браузере.
Во-первых, для доступа к API из браузера необходимо установить CryptoPro CSP. Этот инструмент позволяет установить на устройство клиентский сертификат .pfx.
Во-вторых, даже после установки сертификата Swagger-документация внешнего API становится доступна только в браузерах Chromium-Gost и Яндекс Браузер. Эти браузеры поддерживают отечественные криптографические алгоритмы ГОСТ, используемые в сертификатах, выпущенных через CryptoPro. И на этих браузерах должен быть установлен КриптоПро ЭЦП Browser Plugin.
Наконец, при открытии url /swagger в подходящем браузере появляется модальное окно CryptoPro CSP для выбора нужного сертификата, применяемого для доступа к ресурсу. И после выбора нужного сертификата — победа, Swagger-документация становится доступна и позволяет успешно отправлять REST запросы на сервер.
Но как же выполнять взаимодействие с API по REST из приложения на .NET? Ведь в случае классической реализации выполнения запросов через HttpClient получаем в ответе HTTP статус код 400 (Bad Request) с ошибкой:

Основным решением является подключение к запросам клиентского сертификата .pfx CryptoPro и публичного доверенного сертификата сервера .cer для организации двусторонней TLS-аутентификации.
Рассмотрим следующие варианты решения:
- для .NET приложения, запускаемом на Windows;
- для .NET приложения, запускаемом на Linux;
- с помощью NGINX.
Решение для .NET приложений в Windows
Самый простой способ подцепить сертификаты (клиентский .pfx и доверенный сертификат сервера .cer) из приложения на .NET под Windows заключается в том, чтобы добавить их в виде файлов в структуру проекта.

После чего эти файлы можно подгрузить по пути к ним и применить в REST запросах к целевой API. Для этого необходимо сконфигурировать типизированный HttpClient и добавить к обработке HTTP запросов логику применения сертификатов в HttpClientHandler.

После запуска приложения и выполнения целевого запроса вызывается криптопровайдер CryptoPro CSP и появляется всплывающее окно запроса пароля. Такое поведение проявляется при использовании сертификатов с пометкой «требовать защищенную сессию». В этом случае CryptoPro всегда запрашивает ввод пароля или подтверждение доступа, даже если вы уже указали пароль при загрузке X509Certificate2.

После указания учетных данных запросы успешно доходят до целевого API.
Но в данном случае появляется проблема в виде всплывающего окна установки контейнера с паролем, что блокирует работу приложения.
Можно воспользоваться альтернативным вариантом и предварительно установить все эти сертификаты на устройство и искать целевые сертификаты из хранилища сертификатов.
Правильнее проводить поиск сертификатов в хранилище по Thumbprint (отпечатку) — это считается наиболее надежным и безопасным способом идентификации сертификатов в хранилище. По сути, это уникальный SHA-1 хэш содержимого сертификата, который гарантирует точное совпадение и устойчивость к дубликатам.
Тогда получим следующий код:

В данном случае сертификат CryptoPro успешно достается из хранилища сертификатов Windows и применяется в запросах на сервер без всплывающего окна и запроса пароля. Это работает за счет того, что криптопровайдер CryptoPro интегрируется в инфраструктуру Windows как один из поддерживаемых провайдеров. Соответственно, сертификаты, установленные через CryptoPro, регистрируются в стандартных хранилищах Windows — например, в StoreName.My и StoreLocation.CurrentUser или LocalMachine и доступны через X509Store.
Решение для .NET приложений в Linux
Для запуска приложения в Linux соберем и запустим docker образ.

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

Ошибка возникает из-за того, что OpenSSL не может корректно прочитать сертификат, поскольку в нем используется алгоритм шифрования от CryptoPro, который не поддерживается в стандартной сборке OpenSSL. То есть в Linux такой сертификат не типизировать в X509Certificate2, как это работало для Windows «из коробки».
Установить сертификат на Linux с помощью команд OpenSSL не получится - формат PFX сертификатов не поддерживается в операционной системе Linux. Однако есть возможность конвертировать его в формат PEM с помощью OpenSSL:

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

Все это не сработает для .pfx сертификата CryptoPro по причине неизвестного алгоритма шифрования.

Одним из вариантов решения является установка модифицированной версии OpenSSL, например, gost-engine. Но я бы не рекомендовал подобный вариант, так как сторонние реализации не гарантируют поддержку и безопасность, а также могут не поддерживать актуальные сертификаты или ключи.
Правильным вариантом решения является использование сертифицированного CryptoPro CSP для Linux. И установить его в docker-образ. Скачать необходимый дистрибутив можно по ссылке. В нашем случае используется образ mcr.microsoft.com/dotnet/aspnet:8.0, который базируется на debian образе, поэтому нужно установить дистрибутив linux-amd64_deb.tgz.
CryptoPro CSP является набором инструментов по работе с криптографией, предназначенных для реализации российских алгоритмов ГОСТ, создания и проверки электронной подписи, шифрования, управления сертификатами и контейнерами закрытых ключей. Часть полезных инструментов, содержащихся в сборке linux-amd64_deb.tgz:
csptest— инструмент для тестирования функций CSP: проверка алгоритмов, ключей, сертификатов, подписей и шифрования.certmgt— утилита управления сертификатами: установка, удаление, экспорт и просмотр сертификатов из хранилищ CSP.cprodiag— диагностический инструмент для сбора информации о конфигурации CryptoPro CSP, лицензии, хранилищах и возможных ошибках.cpverify— проверка целостности и корректности установки CryptoPro CSP: сверка контрольных сумм и доступности компонентов.cryptcp— утилита для подписания, шифрования и проверки данных.cpconfig— утилита настройки компонентов CryptoPro CSP: используется для активации и просмотра лицензий, управления параметрами криптопровайдера и системными настройками.cpnginx— модифицированная версия NGINX с поддержкой ГОСТ TLS через интерфейс SSPI, встроенная в CryptoPro CSP.
Внесем изменения в Dockerfile — добавим команды по установке CryptoPro CSP. Для соблюдения правил использования необходимо указать лицензионный ключ. И с помощью инструментария CryptoPro CSP нужно установить сертификаты .pfx и .cer. Скорректированный Dockerfile:

После выполнения команды установки сертификата .pfx его содержимое импортируется в систему CryptoPro и преобразуется — разбивается на отдельные компоненты, сохраненные в формате с расширением .key:

В основном хранилище ключей keys появляется наш преобразованный .pfx контейнер в формате cert.000 с шестью файлами:
primary.key,primary2.key— основные ключи.name.key— содержит имя или идентификатор контейнера.masks.key,masks2.key— используется для маскировки/шифрования ключей.header.key— содержит метаинформацию (заголовок, версию и т. п.).
Поскольку в Linux .pfx сертификат CryptoPro не типизировать в X509Certificate2, то ни вариант с загрузкой сертификата напрямую из структуры проекта, ни вариант с получением из хранилища сертификатов X509Store не сработает — нам не удастся найти целевой .pfx сертификат и, соответственно, не получится выполнить TLS-рукопожатие. Получим следующую ошибку:

В Linux предусмотрена возможность получения сертификатов из специализированного хранилища сертификатов CryptoPro. Для этого необходимо воспользоваться официальным кроссплатформенным решением CryptoPro .NET, доступным по ссылке.
Решение включает в себя четыре NuGet-пакета, которые необходимо подключить к проекту. Это можно сделать одним из следующих способов:
Локальное подключение пакетов.
Загрузка пакетов в онлайн-репозиторий, например Nexus, Azure DevOps и другие.
В .csproj нужно добавить все эти пакеты:

Теперь с помощью библиотек CryptoPro можно получить сертификаты с типом CpX509Certificate2 из хранилища сертификатов CpX509Store. Получим следующий код:

В данном случае сертификат CryptoPro успешно достается из хранилища сертификатов CryptoPro и применяется в запросах на сервер. Вдобавок это решение работает и для Windows тоже.
Решение с помощью NGINX
Вместо интеграции ГОСТ TLS напрямую в .NET-приложение, можно вынести TLS-обвязку на уровень NGINX. Это снимает с .NET приложения (или приложения на любом другом языке программирования) необходимость подключения CryptoPro-сертификатов и реализации TLS взаимодействия, что упрощает код, повышает кроссплатформенность и масштабируемость решения.
Чтобы это работало с сертификатом CryptoPro необходимо воспользоваться cpnginx — специальной версией NGINX от CryptoPro с поддержкой ГОСТ TLS. Это решение содержится в linux-дистрибутивах, доступных на официальной странице загрузки CryptoPro CSP.
Получаем следующую схему взаимодействия с внешним API:

Таким образом:
- .NET-приложение посылает обычные HTTP-запросы на cpnginx;
- cpnginx выступает в роли клиента TLS, обрабатывая криптографию через CryptoPro CSP;
- cpnginx проксирует запрос на сервер и возвращает ответ приложению.
Для настройки CryptoPro Nginx необходимо создать внешний конфигурационный файл api-tls.conf. Этот конфиг необходимо поместить в директорию /etc/opt/cprocsp/cpnginx/conf.d/api-tls.conf. Все *.conf-файлы из этой директории автоматически подключаются к основному конфигурационному файлу cpnginx.

Для запуска cpnginx приложения в Linux соберем и запустим docker образ:

NGINX успешно запускается и принимает входящие HTTP-запросы на http://localhost:8080 (настраивается) и проксирует их на внешний API с использованием CryptoPro TLS. Все запросы и ошибки логируются в /var/log/cpnginx/access.log и /var/log/cpnginx/error.log.
В приложении .NET конфигурация HttpClient упрощается. Остается только указать адрес nginx - куда посылать запросы:

Получаем решение, которое обладает следующими преимуществами:
- Упрощение .NET-приложения — нет необходимости подключать и настраивать ГОСТ TLS в приложении. ‑TLS реализован на nginx.
- Независимость от языка программирования приложения.
- Изоляция криптографии — CryptoPro CSP используется только внутри контейнера cpnginx.
- Легкое масштабирование — cpnginx можно масштабировать независимо от самого приложения.
Как безопасно хранить сертификаты и пароли к ним
При работе с чувствительными данными, особенно такими как.pfx сертификаты с приватным ключом, крайне важно соблюдать правила безопасного хранения. На этапе разработки многие допускают ошибку — хранят сертификаты и пароли в открытом виде прямо в проекте, включая их в git‑репозиторий или конфигурационные файлы. Это недопустимо даже в условиях ограниченного доступа к репозиторию.
Правильным подходом является хранение сертификатов и секретов вне исходного кода. Кроме того, сертификаты не должны включаться в Docker‑образ напрямую. Вместо этого они монтируются во время запуска контейнера через Docker volume или persistent volume в Kubernetes. Пароли к ним считываются в runtime, не попадая в историю образа.
Для исключения чувствительных данных можно воспользоваться следующими безопасными механизмами хранения:
- Переменные окружения, определенные в CI/CD или на хост‑машине при запуске.
- Secrets‑хранилища: например, Azure Key Vault, AWS Secrets Manager, HashiCorp Vault.
- Docker Secrets / Kubernetes Secrets — для защищенной передачи секретов в контейнеры.
Такой подход позволяет обеспечить высокий уровень безопасности на всех этапах жизненного цикла приложения - от сборки до развертывания в production среде.
Заключение
Интеграция с API, использующим сертификаты CryptoPro и ГОСТ TLS, может показаться непростой задачей, особенно в кроссплатформенных проектах. В статье рассмотрено три рабочих варианта: реализацию для.NET‑приложений на Windows и на Linux, а также подход с использованием cpnginx — модифицированного NGINX с поддержкой ГОСТ TLS.
Наиболее универсальным и масштабируемым решением становится cpnginx. Он полностью изолирует криптографическую обвязку от логики приложения, устраняет зависимость от ОС, упрощает сопровождение и снижает риски, связанные с безопасностью. С его помощью.NET‑приложение может работать с API как с обычным HTTP‑сервисом, а все криптографические операции выполняются внутри защищённого контейнера.
Все решения применимы на практике. Выбор подхода зависит от ваших потребностей, ограничений, инфраструктуры и требований безопасности.