Как устроен конвейер
Сборочный конвейер большой софтверной компании обычно состоит из множества виртуальных машин, управляемых оркестраторами сборки. В качестве последних часто используются 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-ключа. При этом из всех элементов содержимого крито-контейнера экспортировать можно лишь открытый ключ и его сертификат. Закрытий ключ от ключевой пары экспортировать невозможно.
Объекты крипто-контейнера появляются в следующей последовательности:
Сначала генерируется ключевая пара на защищенном электронном носителе Rutoken. При этом закрытый ключ является неизвлекаемым.
Потом создается запрос на сертификат в удостоверяющий центр. В ответ удостоверяющий центр GlobalSign выпускает сертификат открытого ключа.
Сертификат открытого ключа записывается на защищенный электронный носитель. Его можно извлечь из электронного носителя так же, как и открытый ключ.
Все готово для того, чтобы подписывать закрытым ключом файлы и проверять их подпись при помощи сертификата открытого ключа.
В чем сложности работы с 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 оказалась не таким уж простым занятием. Но давайте обо всем по порядку.
Защищенный электронный носитель:
Модель: Rutoken ECP 3.0 (3220) (Рутокен ЭЦП 3.0)
Версия прошивки: 65.04.30.02 (03)
Версия драйверов: 4.21.0.0
Окружение:
Токен подключен к хосту ESXi, который управляется через vSphere и проброшен в виртуальную машину.
Операционная система на виртуальной машине: Windows Server 2019 Standard.
Этап 1 — запуск вручную
Сначала мы проверили, как работает весь механизм при запуске из командной строки на целевой виртуальной машине.
Внутри виртуальной машины результаты следующее:
Токен виден в системе
Настроено кеширование PIN-кода
exe/msi-файлы успешно подписываются c помощью signtool из командной строки под учётной записью локального администратора.
Командная строка:
signtool sign /tr http://timestamp.globalsign.com/tsa/r6advanced1 /v /td SHA256 /debug /sha1 <SHA1> /fd SHA256 C:\test.exe
SHA1 — хэш конкретного сертификата (если их на токене несколько). Для автоматического выбора наиболее подходящего сертификата используется опция /a.
Проблемы отсутствуют, подпись файлов любого размера выполняется успешно.
Этап 2 — запуск из сборочного агента
Виртуальная машина с установленной внутрь операционной системой Windows Server 2019 регистрируется в качестве сборочного агента на сервере Teamcity.
В виртуальную машину устанавливается Teamcity Build Agent, который регистрируется как системная служба Windows, запускаемая под учётной записью локального администратора.
Запуск
signtoolпереносится в отдельный build step в сборочной конфигурации на TeamCity.
Схема запуска signtool.exe в этом случае выглядит следующим образом:
Сервер TeamCity инициирует запуск сборки
Служба Teamcity Build Agent на виртуальной машине под управлением Wndows 2019 запускает cmd-файл, который вызывает signtool.exe
Результаты:
Подпись exe- и dll-файлов выполняется успешно. Максимальный размер подписываемых файлов —до 5MB.
Подпись MSI-файлов успешно выполняется лишь примерно в 20% случаев. Типовой размер MSI- файлов — от 70 MB до 700 MB.
При этом те же самые 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.msisigntool <…> C:\test2.msi
Результат:
Подпись test1.msi иногда выполнялась успешно
Подпись test2.msi всегда заканчивалась ошибкой.
Запуск signtool в цикле с паузами между итерациями длительностью несколько минут
Результат: иногда подпись выполнялась успешно после 5–10 неудачных попыток.
Перезагрузка виртуальной машины.
Результат: После перезагрузки ВМ первый запуск signtool мог выполниться успешно, но в большинстве случаев завершался с ошибкой.
Длительная (несколько часов) пауза после неудачной попытки
Результат: Последующий запуск signtool.exe, как правило, также не был успешен
Еще поэкспериментировали
Альтернативные варианты запуска из-под системной службы Windows для исключения влияния конкретной реализации TeamCity Build Agent
Запустили signtool.exe с помощью PsExec. Получили аналогичные проблемы. Появились ошибки при подписи MSI-файлов.
Написали собственный простенький вариант системной службы Windows на WinApi c вариациями запуска signtool.exe c помощью
CreateProcess / CreateProcessAsUser / WTSQueryUserToken+ / etc. Получили аналогичные проблемы. Появились ошибки при подписи MSI-файлов.Манипуляции с параметрами запуска системной службы Windows ни на что не повлияли (Local System
/interactive/etc.)
В рамках экспериментов также менялись права на файл, где кэшируется PIN-код C:\Users\<username>\AppData\LocalLow\Aktiv Co\Rutoken\pcache.txt
Это тоже к исчезновению ошибки не привело.
Решение проблемы, наконец, было найдено
Из цепочки вызовов, которая приводила к запуску signtool.exe, полностью исключили системную службу Windows. Сделали это так:
На виртуальной машине Windows Server 2019 настроили auto logon от имени пользователя с правами администратора. https://learn.microsoft.com/en-us/troubleshoot/windows-server/user-profiles-and-logon/turn-on-automatic-logon
Через группу Автозапуск (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-инженер
Комментарии (8)

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

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

maxp
02.02.2026 16:01Читаю как послание из другого мира...
То есть разработчики буквально методом тыка изучают как бы так запустить сборочные скрипты, чтобы они работали. Причем, даже не выясняя детально "где косяк", просто довели до состояния "ну вроде пока не падает".
nixtonixto
02.02.2026 16:01А что ещё делать, если производитель не предоставляет необходимое ПО и подробную техническую информацию, поэтому остаётся только догадываться и пробовать методом тыка?

vov_i
02.02.2026 16:01Вы кого под производителем в виду имеете? Global Sign, Актив, Microsoft, JetBrains?...
Кто какую информацию и ПО не предоставил?
shuchkin
подпись через токен это очень медленная операция, секретный ключ можно достать, есть инструкции в сети
vov_i
Медленная операция - хеширование. Подпись хеша выполняется достаточно быстро.
Секретный ключ можно достать из контейнера криптопровайдера, если токен используется в режиме пассивного ключевого носителя, но не сгенерированный на борту токена неизвлекаемый ключ