Введение и мотивация

При реализации цифровых подписей в корпоративных системах безопасность является обязательным требованием.
Хранение сертификата в локальном файле PFX или P12 удобно, но открывает возможность извлечения или компрометации закрытого ключа. В отличие от этого, PKCS#11 hardware tokens (например, USB‑донглы, смарт‑карты и HSM) хранят ключи внутри защищённого от вмешательства окружения, гарантируя, что они никогда не покидают устройство.

В этой статье показано, как использовать GroupDocs.Signature for .NET совместно с Pkcs11Interop для подписи PDF‑документов аппаратными токенами. Подход сочетает удобство и соответствие требованиям: GroupDocs обрабатывает всё, что связано с упаковкой PDF (поля подписи, вычисление дайджеста, внедрение), а токен выполняет фактическое криптографическое подписание.

⚠️ Уведомление о ранней реализации
Это решение предоставляется в виде ранней реализации для использования USB‑донглов цифровой подписи PKCS#11 с GroupDocs.Signature.
Хотя оно позволяет подписывать документы аппаратными токенами, мы настоятельно рекомендуем провести дополнительное тестирование в вашей среде, чтобы убедиться, что оно удовлетворяет вашим требованиям к соответствию и безопасности.
Мы будем очень благодарны за ваш отзыв, результаты тестов и предложения по улучшению.

Задача: соединение PKCS#11 с подписью PDF

Интеграция токенов PKCS#11 в рабочие процессы подписи документов сопряжена с несколькими нетривиальными проблемами:

  1. Низкоуровневая сложность — API PKCS#11 (Cryptoki) требует управления слотами, сессиями, дескрипторами и атрибутами для поиска нужного закрытого ключа.
  2. Упаковка на уровне PDF — подпись PDF — это больше, чем подпись байтов: библиотека должна вычислять корректные дайджесты по выбранным диапазонам байтов, упаковывать подписи в контейнеры CMS/PKCS#7, включать метки времени и внедрять информацию о проверке.
  3. Вариации поставщиков — разные токены/модули могут требовать пользовательского сопоставления атрибутов или дополнительного промежуточного ПО.
  4. Соответствие и аудит — в продуктивных системах необходимы надёжное управление PIN‑кодом, контроль жизненного цикла сессий, восстановление после ошибок и журналирование.

Этот примерный проект решает перечисленные задачи, комбинируя интерфейс ICustomSignHash в GroupDocs.Signature с Pkcs11Interop для переноса подписи на токен, оставляя GroupDocs заниматься структурой PDF.

Что делает примерный проект

  • Демонстрирует подпись PDF‑документов с использованием токенов PKCS#11 (донгл, смарт‑карта, HSM).
  • Поддерживает резервный вариант через хранилище сертификатов Windows: если сертификат установлен в Windows, код может использовать его.
  • Реализует пользовательскую подпись хеша: GroupDocs вычисляет дайджест; токен лишь подписывает хеш.
  • Держит закрытый ключ на аппаратуре всё время — он никогда не экспортируется.
  • Инкапсулирует логику токена (сессия, поиск ключа, подпись) в файле Pkcs11DigitalSigner.cs.
  • Предоставляет вспомогательные функции в Helpers.cs (например, поиск сертификата в хранилище Windows).
  • Конфигурация централизована в Settings.cs.
  • Служит эталонной реализацией, которую можно адаптировать под свою среду.
Bridging PKCS#11 with PDF Signing

Настройка и предварительные требования

Предварительные требования

  • .NET 6.0 или выше (или .NET Framework 4.6.2)
  • Действительная PKCS#11‑библиотека (DLL) от поставщика вашего токена
  • Аппаратный токен (USB‑донгл, смарт‑карта или HSM) с действительным сертификатом
  • GroupDocs.Signature for .NET (триальная или лицензированная версия)
  • Библиотека Pkcs11Interop

Установка

git clone https://github.com/groupdocs-signature/esign-documents-with-pkcs11-using-groupdocs-signature-dotnet.git
cd esign-documents-with-pkcs11-using-groupdocs-signature-dotnet
dotnet restore

Откройте решение в Visual Studio или в предпочитаемой IDE, убедитесь, что все зависимости разрешены.

Структура репозитория в деталях

GroupDocs.Signature-for-.NET-PKCS11-Sample/
├── GroupDocs.Signature-for-.NET-PKCS11-Sample.csproj      # Файл проекта
├── Program.cs                                             # Точка входа и порядок использования
├── Settings.cs                                            # Конфигурация PKCS#11 / токена
├── Helpers.cs                                             # Вспомогательные функции (хранилище Windows, фильтрация сертификатов)
├── Pkcs11DigitalSigner.cs                                 # Реализует ICustomSignHash через PKCS#11
└── README.md                                              # Описание и инструкции по использованию
  • Program.cs — координирует процесс подписи; демонстрирует как поток на основе токена, так и поток через сертификат Windows.
  • Settings.cs — содержит константы/заполнители для Pkcs11LibraryPath, TokenPin и CertificateSubject.
  • Helpers.cs — содержит код для поиска сертификатов в хранилище Windows по имени субъекта (используется в резервном потоке).
  • Pkcs11DigitalSigner.cs — основная логика: загрузка модуля PKCS#11, открытие сессий, поиск объекта закрытого ключа, подпись дайджеста и возврат X509Certificate2 или реализации обратного вызова подписи.
  • README.md — предоставляет обзор, описание проблем и инструкции (которые дополняет данный блог).

Пояснение к коду и пошаговый разбор

Settings.cs

public static class Settings
{
    public const string Pkcs11LibraryPath = "<PKCS11_LIBRARY_PATH>";
    public const string TokenPin = "<TOKEN_PIN>";
    public const string CertificateSubject = "<CERT_SUBJECT>";
}

Эти параметры вынесены в отдельный класс, чтобы их было легко заменить в вашей среде развертывания.

Pkcs11DigitalSigner.cs — общий поток

public class Pkcs11DigitalSigner : ICustomSignHash
{
    public byte[] SignHash(byte[] hash)
    {
        // Этот метод вызывается GroupDocs.Signature, когда требуется подпись хеша токеном
        using (var pkcs11 = new Pkcs11(Settings.Pkcs11LibraryPath, AppType.SingleThreaded))
        {
            // Загрузка модуля, открытие сессии, вход с PIN, поиск ключа и выполнение подписи
        }
    }

    public X509Certificate2 GetCertificateFromPkcs11()
    {
        // Получает публичный сертификат из токена, чтобы можно было настроить параметры подписи
    }
}
  • SignHash — центральный метод: получает дайджест, вычисленный GroupDocs, а затем использует API PKCS#11 для его подписи.
  • GetCertificateFromPkcs11 извлекает сертификат (с открытым ключом) из токена, чтобы метаданные подписи были корректными.

Program.cs — порядок использования

class Program
{
    static void Main()
    {
        string inputFile = "sample.pdf";
        string outputFile = "signed.pdf";

        // (1) Подпись через PKCS#11
        var tokenSigner = new Pkcs11DigitalSigner();
        var cert = tokenSigner.GetCertificateFromPkcs11();

        using (var signature = new Signature(inputFile))
        {
            var options = new DigitalSignOptions(cert)
            {
                Comments = "Signed with PKCS#11 token",
                SignTime = DateTime.Now,
                CustomSignHash = tokenSigner  // привязываем подпись через токен
            };
            signature.Sign(outputFile, options);
        }

        // (2) Резервный вариант через хранилище сертификатов Windows (по желанию)
        // var storeCert = Helpers.GetCertificateFromWindowsStore(Settings.CertificateSubject);
        // using (var signature2 = new Signature(inputFile))
        // {
        //     var options2 = new DigitalSignOptions(storeCert) { ... };
        //     signature2.Sign("signed_store.pdf", options2);
        // }
    }
}

Ключевые моменты:

  • Свойство CustomSignHash объекта DigitalSignOptions устанавливается в tokenSigner, позволяя GroupDocs делегировать фактическую подпись хеша токену.
  • Закомментированный резервный поток показывает, как переключиться на сертификат из хранилища Windows, если аппаратный токен недоступен.

Сценарии использования и реальные примеры

  • Индия и USB‑донглы, выданные УЦ
    В Индии многие юридически значимые e‑подписи требуют сертификаты, хранящиеся в USB‑донглах, выданных аккредитованными удостоверяющими центрами. Этот пример позволяет приложениям (например, шлюзам документов, порталам) напрямую работать с такими донглами.
  • Корпоративные документооборотные процессы
    Для внутренних систем, таких как управление контрактами или процессы согласования, аппаратная подпись гарантирует, что неавторизованные пользователи не смогут подделать подписи.
  • Юридические / регуляторные подписи
    Правительства и регулируемые отрасли часто требуют, чтобы подписи исходили от ключей, контролируемых аппаратурой. Интеграция помогает соответствовать строгим требованиям аудита и соответствия.

Распространённые ошибки и их устранение

  • Неправильный путь к библиотеке → Путь к DLL PKCS#11 должен соответствовать модулю вашего поставщика (например, softhsm2.dll, cryptoki.dll).
  • Блокировка или ошибка PIN → Многократный ввод неверного PIN может заблокировать токен; проверьте политику поставщика.
  • Ключ не найден → Убедитесь, что указано правильное имя субъекта сертификата; токен должен содержать сертификат с соответствующим субъектом.
  • Отсутствие драйвера или промежуточного ПО → Некоторые токены требуют установки драйверов поставщика перед тем, как Pkcs11Interop сможет установить связь.
  • Проблемы с потоками → Операции PKCS#11 могут быть не потокобезопасными; используйте однопоточный контекст, если только поставщик не поддерживает многопоточность.
  • Тайм‑ауты или сброс сессий → Длительные операции могут привести к закрытию или тайм‑ауту сессий; обеспечьте корректное управление сессиями и их очистку.

Безопасность и лучшие практики

  • Никогда не храните в коде производственные секреты (PIN‑коды, пути к библиотекам); используйте безопасные конфигурации или менеджеры секретов.
  • Применяйте надёжные PIN‑коды и меняйте их согласно политике.
  • Ведите журнал операций и ошибок (не записывайте чувствительные PIN‑коды).
  • Ограничивайте количество сессий токена и сразу после подписи выполняйте выход из системы.
  • После подписи проверяйте подпись (цепочка доверия, метка времени).
  • Тестируйте в разных средах и с различными типами токенов (донгл/смарт‑карта/HSM).

Следующие шаги и ресурсы

Готовы попробовать? Склонируйте репозиторий, замените заполнители и запустите пример.
Темы, которые могут быть интересны дальше:

  • Пользовательская подпись хеша (делегирование вычисления дайджеста и подписи токену)
  • Метка времени и внедрение LTV / DSS
  • Итеративная подпись (несколько подписей в одном документе)
  • Интеграция с удалёнными HSM‑сервисами или облачными хранилищами токенов

Внешние ссылки