Работа с Рутокен и ГОСТ в C#: от архитектуры PKCS#11 до готового кода подписи

  • Михаил
  • 8 мин. на прочтение
  • 6
  • 15 Apr 2026
  • 15 Apr 2026

Электронная подпись (КЭП) прочно вошла в корпоративный и государственный документооборот. В России стандартом де-факто стали аппаратные токены Рутокен и криптопровайдер КриптоПро CSP. Однако при разработке программного обеспечения, которое должно подписывать документы напрямую (без участия CSP), разработчики часто упираются в один и тот же вопрос: как на C# подписать данные с использованием закрытого ключа, который хранится на Рутокене?

Ответ лежит в стандарте PKCS#11 и библиотеке rtPKCS11ECP.dll. В этой статье мы разберём:

  • что такое PKCS#11 и как rtPKCS11ECP.dll общается с токеном;
  • какие библиотеки для C# существуют и какую выбрать;
  • как написать рабочий код для подписи данных по ГОСТ Р 34.10-2012;
  • какие методы PKCS#11 используются и как ими управлять.

Статья ориентирована на практиков: после её прочтения вы сможете самостоятельно подключать Рутокен к своим .NET-приложениям.


1. Архитектура PKCS#11: как библиотека находит и общается с токеном

Прежде чем писать код, важно понять иерархию объектов PKCS#11. Это знание поможет вам правильно искать ключи и сертификаты.

Ключевые понятия PKCS#11:

  • Слот (Slot) – логическое место, куда можно вставить токен. Каждому USB-порту или считывателю соответствует свой слот.
  • Токен (Token) – само устройство (Рутокен), подключённое к слоту. Токен содержит объекты: ключи, сертификаты, данные.
  • Сессия (Session) – канал связи между приложением и токеном. Все операции выполняются внутри открытой сессии.
  • Объекты (Objects) – ключи, сертификаты, данные. Имеют класс (CKO_PRIVATE_KEY, CKO_CERTIFICATE и т.д.) и атрибуты (CKA_ID, CKA_LABEL...).
  • Атрибуты (Attributes) – свойства объекта, по которым его можно искать. Например, CKA_ID связывает закрытый ключ с его сертификатом.
  • Механизмы (Mechanisms) – алгоритмы, поддерживаемые токеном. Для ГОСТ Р 34.10-2012 это CKM_GOSTR3410_WITH_GOSTR3411_2012_256 (подпись с хешированием) и другие.

2. Где взять rtPKCS11ECP.dll

Библиотека rtPKCS11ECP.dll – это реализация PKCS#11 от компании «Актив» для токенов Рутокен. Она устанавливается вместе с драйверами Рутокен или КриптоПро CSP.

Типичные пути:

  • Windows (64 бита):
    C:\Windows\System32\rtPKCS11ECP.dll
    C:\Program Files\Crypto Pro\PKCS11\rtPKCS11ECP.dll
  • Windows (32 бита):
    C:\Windows\SysWOW64\rtPKCS11ECP.dll
  • Linux:
    /usr/lib/librtpkcs11ecp.so или /usr/lib64/librtpkcs11ecp.so

В коде C# мы будем загружать эту библиотеку не напрямую через DllImport, а через специализированные обёртки (см. следующий раздел).


3. Популярные C# библиотеки для работы с PKCS#11 и ГОСТ

Для C# существует несколько библиотек, которые упрощают вызов функций PKCS#11. Ниже приведена сравнительная таблица.

 

БиблиотекаПроизводительЛицензияПоддержка ГОСТ Р 34.10-2012Уровень удобства
Pkcs11InteropСообществоApache 2.0Да (через механизмы токена)Низкоуровневый, полный контроль
RutokenPkcs11InteropАО "Актив" (Рутокен)Проприетарная / FreeДа (полная, с готовыми примерами)Высокий (расширяет Pkcs11Interop)
NCryptokiSecure ImprovementsКоммерческаяНет данныхВысокий (абстрагирует PKCS#11)

pkcs11.net

СообществоOpen SourceНет данныхНизкий
Bouncy CastleLegion of the Bouncy CastleMITДа (алгоритмы, но не PKCS#11)Не適用 (не для PKCS#11)

Рекомендация: Для работы с Рутокен и ГОСТ оптимально использовать Pkcs11Interop (универсально) или RutokenPkcs11Interop (если нужны специфические функции Рутокен). Обе библиотеки позволяют вызывать любые механизмы PKCS#11, включая CKM_GOSTR3410_WITH_GOSTR3411_2012_256.

Install-Package Pkcs11Interop
# или
Install-Package RutokenPkcs11Interop

4. Практика: подписание данных на C# с помощью Pkcs11Interop

Ниже приведён рабочий пример кода, который:

  • загружает rtPKCS11ECP.dll;
  • открывает сессию с первым найденным токеном;
  • аутентифицируется по PIN-коду;
  • находит закрытый ключ с атрибутом CKA_SIGN;
  • подписывает строку с использованием механизма CKM_GOSTR3410_WITH_GOSTR3411_2012_256;
  • выводит подпись в Base64.
class Program
{
   static void Main()
   {
       // 1. Путь к библиотеке PKCS#11
       string libPath = @"C:\Windows\System32\rtPKCS11ECP.dll";
       // 2. Создаём фабрику и загружаем библиотеку
       var factories = new Pkcs11InteropFactories();
       using (IPkcs11Library pkcs11 = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, libPath, AppType.MultiThreaded))
       {
           // 3. Получаем список слотов с токенами
           List<ISlot> slots = pkcs11.GetSlotList(SlotsType.WithTokenPresent);
           if (slots.Count == 0)
           {
               Console.WriteLine("Токен не найден.");
               return;
           }
           ISlot slot = slots[0];
           Console.WriteLine($"Используем слот: {slot.SlotId}");
           // 4. Открываем сессию (ReadWrite)
           using (ISession session = slot.OpenSession(SessionType.ReadWrite))
           {
               // 5. Вход по PIN-коду
               Console.Write("Введите PIN: ");
               string pin = Console.ReadLine();
               session.Login(CKU.CKU_USER, pin);
               Console.WriteLine("Аутентификация успешна.");
               // 6. Поиск закрытого ключа, поддерживающего подпись
               var searchAttributes = new List<IObjectAttribute>
               {
                   factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY),
                   factories.ObjectAttributeFactory.Create(CKA.CKA_SIGN, true)
               };
               List<IObjectHandle> privateKeys = session.FindAllObjects(searchAttributes);
               if (privateKeys.Count == 0)
               {
                   Console.WriteLine("Не найден закрытый ключ для подписи.");
                   return;
               }
               IObjectHandle privateKey = privateKeys[0];
               Console.WriteLine("Ключ найден.");
               // 7. Данные для подписи
               byte[] dataToSign = Encoding.UTF8.GetBytes("Документ для подписи");
               // 8. Механизм подписи с хешированием по ГОСТ 2012-256
               IMechanism mechanism = factories.MechanismFactory.Create(CKM.CKM_GOSTR3410_WITH_GOSTR3411_2012_256);
               // 9. Подписание
               byte[] signature = session.Sign(mechanism, privateKey, dataToSign);
               Console.WriteLine($"Подпись (Base64): {Convert.ToBase64String(signature)}");
               // 10. Завершение
               session.Logout();
           }
       }
   }
}

Пояснения:

Метод Sign сам вычисляет хеш по ГОСТ Р 34.11-2012, поскольку механизм CKM_GOSTR3410_WITH_GOSTR3411_2012_256 включает хеширование.

Не нужно предварительно вызывать ComputeGostHash – это делает токен или библиотека.

Если ваш токен не поддерживает этот механизм, можно использовать CKM_GOSTR3410 и передавать хеш, но это менее удобно.


5. Основные методы PKCS#11, используемые в C# (через Pkcs11Interop)

 

Метод (в обёртке)Описание
pkcs11.GetSlotList(SlotsType.WithTokenPresent)Возвращает список слотов, в которых есть токен.
slot.OpenSession(SessionType.ReadWrite)Открывает сессию для работы с токеном.
session.Login(CKU.CKU_USER, pin)Аутентификация пользователя.
session.FindAllObjects(attributes)Поиск объектов (ключей, сертификатов) по атрибутам.
session.GetAttributeValue(objectHandle, attributes)Получение значений атрибутов объекта (например, CKA_ID).
session.Sign(mechanism, privateKey, data)Выполнение операции подписи.
session.Logout()Завершение сеанса пользователя.
session.Close()Закрытие сессии (вызывается автоматически при Dispose).

6. Как проверить подпись (кратко)

Проверка подписи может быть выполнена как на том же токене (метод session.Verify), так и программно с использованием открытого ключа из сертификата. Ниже пример проверки через Bouncy Castle (без токена):

bool VerifySignature(byte[] data, byte[] signature, byte[] certDer)
{
   var parser = new X509CertificateParser();
   var cert = parser.ReadCertificate(certDer);
   var pubKey = cert.GetPublicKey() as Org.BouncyCastle.Crypto.Parameters.ECGost3410PublicKeyParameters;
   if (pubKey == null) return false;
   var signer = new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411_2012_256Digest());
   signer.Init(false, pubKey);
   signer.BlockUpdate(data, 0, data.Length);
   return signer.VerifySignature(signature);
}

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

Мы разобрали:

  • что такое PKCS#11 и как rtPKCS11ECP.dll служит мостом между C# и Рутокеном;
  • какие C# библиотеки существуют и почему Pkcs11Interop – лучший выбор для ГОСТ;
  • как написать компактный код для подписи данных с использованием механизма CKM_GOSTR3410_WITH_GOSTR3411_2012_256;
  • основные методы PKCS#11, которые понадобятся в 90% задач.

Теперь вы можете подписывать документы, отчёты и JSON-данные, работая с Рутокеном напрямую, без участия КриптоПро CSP. Это даёт больше контроля и позволяет встраивать КЭП в любые .NET-приложения.