ХабрПостыНовостиМегапосты?

Как мы встраивали ЭЦП на Rutoken в свой сборочный конвейер

Как устроен конвейер

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

При разработке программного обеспечения для Windows есть необходимость подписывать исполняемые модули (файлы EXE и DLL), а также инсталляционные пакеты MSI с использованием закрытого ключа.  

Как всё было хорошо и просто раньше

До 2025 года такие закрытые ключи в комплекте с открытыми ключами и с сертификатами открытых ключей можно было приобрести у компании GlobalSign в виде отчуждаемого крипто-контейнера PFX в формате pkcs12, доступ к содержимому которого был защищён паролем.

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

signtool.exe sign /f company-cert.pfx /p <password> /t http://timestamp.digicert.com /v "<файл, который должен быть подписан>"

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

Как все стало в 2025 году

С 2025 года компания GlobalSign более не поставляет файлы крипто-контейнеров типа compamy-cert.pfx в формате pkcs12. Вместо этого поставляется защищенный электронный носитель Rutoken производства компании Актив-Софт, выполненный в форм-факторе usb-ключа, похожего на обыкновенную флешку.  Крипто-контейнер расположен во флеш-памяти usb-ключа. При этом из всех элементов содержимого крито-контейнера экспортировать можно лишь открытый ключ и его сертификат. Закрытий ключ от ключевой пары экспортировать невозможно. 

Объекты крипто-контейнера появляются в следующей последовательности:

  1. Сначала генерируется ключевая пара на защищенном электронном носителе Rutoken. При этом закрытый ключ является неизвлекаемым.

  2. Потом создается запрос на сертификат в удостоверяющий центр. В ответ удостоверяющий центр GlobalSign выпускает сертификат открытого ключа.

  3. Сертификат открытого ключа записывается на защищенный электронный носитель. Его можно извлечь из электронного носителя так же, как и открытый ключ.

Все готово для того, чтобы подписывать закрытым ключом файлы и проверять их подпись при помощи сертификата открытого ключа.

В чем сложности работы с Rutoken?

Защищённый электронный носитель Rutoken сам по себе является средством персональной ЭЦП. По конструкции он предназначен для подписи ответственным лицом нескольких десятков документов в день. Работа Rutoken начинается с операции логина, которая требует присутствия за компьютером оператора и ввода PIN-кода. Без этого получить доступ к содержимому крипто-контейнера невозможно. Конструктивно Rutoken изначально рассчитывался на работу в присутствии человека. То же самое относится к его зарубежному аналогу eToken.

Использование Rutoken на сборочном конвейере — это использование персональной системы ЭЦП на сервере без присутствия человека. Чтобы приспособить Rutoken к использованию на сборочном конвейере, нужно автоматизировать процесс ввода PIN-кода, либо этот PIN-код вообще отключить. К счастью, у Rutoken имеется функция кэширования однажды правильно введенного PIN-кода. Для ее включения следует отправить запрос в техническую поддержку компании Актив-Софт.

После включения функции кэширования PIN-код будет храниться на сервере в зашифрованном виде в файле C:\Users\<username>\AppData\LocalLow\Aktiv Co\Rutoken\pcache.txt

Таким образом система персональной ЭЦП может стать серверной системой.

Использование Rutoken вместе с signtool

Настройка совместной работы этого устройства и утилиты фирмы Microsoft оказалась не таким уж простым занятием.  Но давайте обо всем по порядку.

Защищенный электронный носитель:

Окружение:

Этап 1 — запуск вручную

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

Внутри виртуальной машины результаты следующее:

Командная строка:

signtool sign /tr http://timestamp.globalsign.com/tsa/r6advanced1 /v /td SHA256 /debug /sha1 <SHA1> /fd SHA256 C:\test.exe

SHA1 — хэш конкретного сертификата (если их на токене несколько). Для автоматического выбора наиболее подходящего сертификата используется опция /a. 

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

Этап 2 — запуск из сборочного агента

Схема запуска signtool.exe в этом случае выглядит следующим образом:

Результаты:

При этом те же самые MSI файлы любого размера успешно подписываются при запуске signtool.exe из командной строки. 

В случае ошибок signtool.exe генерирует одно и тоже сообщение: The specified private key container was not found.

Полный текст:

The following certificates were considered:

     Issued to: Infowatch Laboratory LLC

     Issued by: GlobalSign GCC R45 CodeSigning CA 2020

     Expires:   Tue Apr 25 15:23:20 2028

     SHA1 hash: <SHA1>

  After EKU filter, 1 certs were left.

 After expiry filter, 1 certs were left.

 After Hash filter, 1 certs were left.

 After Private Key filter, 1 certs were left.

 The following certificate was selected:

     Issued to: Infowatch Laboratory LLC

     Issued by: GlobalSign GCC R45 CodeSigning CA 2020

     Expires:   Tue Apr 25 15:23:20 2028

     SHA1 hash: <SHA1> 

 Done Adding Additional Store

 SignTool Error: The specified private key container was not found.

Решили поэкспериментировать

Сценарии, которые были проверены при запуске signtool из-под сервиса TeamCity Build Agent:

signtool <…> C:\test1.msi
signtool <…> C:\test2.msi

Результат:

Подпись test1.msi иногда выполнялась успешно
Подпись test2.msi всегда заканчивалась ошибкой. 

Результат: иногда подпись выполнялась успешно после 5–10 неудачных попыток.

Результат: После перезагрузки ВМ первый запуск signtool мог выполниться успешно, но в большинстве случаев завершался с ошибкой.

Результат: Последующий запуск signtool.exe, как правило, также не был успешен

Еще поэкспериментировали

Альтернативные варианты запуска из-под системной службы Windows для исключения влияния конкретной реализации TeamCity Build Agent

В рамках экспериментов также менялись права на файл, где кэшируется PIN-код C:\Users\<username>\AppData\LocalLow\Aktiv Co\Rutoken\pcache.txt

Это тоже к исчезновению ошибки не привело.

Решение проблемы, наконец, было найдено

Из цепочки вызовов, которая приводила к запуску signtool.exe, полностью исключили системную службу Windows. Сделали это так:

  1. На виртуальной машине Windows Server 2019 настроили auto logon от имени пользователя с правами администратора. https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon 

  2. Через группу Автозапуск (Start Up) для автоматически залогиненного пользователя с правами администратора запустили TeamCity Build Agent как обычное консольное приложение. Он удачно связался с сервером TeamCity и начал работать.

Результаты: после перехода на этот вариант при помощи Rutoken ЭЦП 3.0 стали успешно подписываться дистрибутивы с десятками исполняемых файлов.

Техническая реализация

TeamCity Build Agent — это не нативное консольное приложение. Сам агент написан на языке Java, представляет из себя *.jar-файл и запускает внутри Java-машины. 

Для работы агента в консольном режиме нужно дополнительно настраивать пути к файлам конфигурации, classpath и прочие параметры запуска Java-машины. Чтобы не тратить на это время, можно запустить готовый BAT-файл “c:\BuildAgent\bin\agent.bat” start.

Заключение

Всего в 2025 году на сборочном конвейере при помощи вышеуказанной методики было успешно подписано 22 тысячи файлов.

Авторы статьи

Андрей Петров

руководитель отдела DevOps

Юрий Жмеренецкий

DevOps-инженер

@InfoWatch
02.02.2026 19:49 UTC
Первоисточник

Комментарии

@shuchkin
02.02.2026 16:01 UTC
+1

подпись через токен это очень медленная операция, секретный ключ можно достать, есть инструкции в сети

03.02.2026 10:57 UTC
+2

Медленная операция - хеширование. Подпись хеша выполняется достаточно быстро.

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

@Steelycrack
02.02.2026 18:45 UTC
0

прекрасно этот закрытый ключ достается, на рутокенах никакой защиты нет.

03.02.2026 10:59 UTC
+1

Зависит от режима использования Рутокен ЭЦП.
Если ключ неизвлекаемый, сгенерирован токеном, то не выйдет ничего

@dimsoft
03.02.2026 02:59 UTC
+3

Rutoken ЭЦП 3.0, в отличии от Rutoken Lite ключ не покидает токен.

@maxp
03.02.2026 07:08 UTC
+1

Читаю как послание из другого мира...
То есть разработчики буквально методом тыка изучают как бы так запустить сборочные скрипты, чтобы они работали. Причем, даже не выясняя детально "где косяк", просто довели до состояния "ну вроде пока не падает".

03.02.2026 08:29 UTC
-1

А что ещё делать, если производитель не предоставляет необходимое ПО и подробную техническую информацию, поэтому остаётся только догадываться и пробовать методом тыка?

03.02.2026 11:18 UTC
+2

Вы кого под производителем в виду имеете? Global Sign, Актив, Microsoft, JetBrains?...
Кто какую информацию и ПО не предоставил?