Прочитать о сертификатах (Code signing) можно тут.
На Хабре уже рассказывалось о бесплатной (уже давно не бесплатной) возможности подписать свои открытые проекты: https://habr.com/ru/articles/242267/.
Компания SignPath GmbH предлагает бесплатную сертификацию проектов с открытым исходным кодом (https://about.signpath.io/product/open-source). Есть возможность подписывать как исполняемые файлы Windows, так и контейнеры Docker. Сборка проекта должна выполняться с помощью GitHub Actions. Вся документация доступна по адресу https://about.signpath.io/documentation/getting-started.

Я прошёл этот путь и вот как это было.
Проект
О своём проекте я уже немного рассказывал на Хабре.
Весь код написан на Python с использованием PySide6 и другими пакетами. Для сборки под Windows используется PyInstaller и Nuitka (с Nuitka работает быстрее, но иногда бывают странные ошибки, поэтому пока не отказался от PyInstaller), а инсталляционный пакет собирается с помощью Inno Setup. Весь процесс сборки настроен на GitHub Actions.
При запуске инсталлятора или приложения Windows выдает всем знакомое предупреждение:

Чтобы избавится от него, исполняемый файл должен быть подписан доверенным сертификатом разработчика. Большинство сертификационных центров требуют оплату, но есть вариант получить подпись бесплатно.
Регистрация
Для того чтобы получить бесплатный сертификат необходимо отправить запрос на почту oss-support@signpath.org. Я написал буквально пару предложений с описанием и ссылкой на проект.
Через пару дней пришла форма в файле Excel. Её нужно заполнить и отправить обратно. Самым интересным был пункт Репутация:
Опишите, как мы можем проверить, что ваш проект используется и заслуживает доверия.
Предоставьте источники и ссылки, включая
* Обзоры в СМИ
* Статьи в блогах
* Статьи Википедии на других языках (см. ниже для английского), Softpedia и т. д.
* Данные об использовании, такие как количество загрузок, форков или зависимостей
* Аналитика, такая как GitHub Insights
* Доказательство права собственности на товарный знак, если вы используете зарегистрированное название
Если доступно, мы также рассмотрим статью в Википедии на английском языке и OpenHub, а также данные сообщества, такие как GitHub Insights.
Спустя 2 недели проект был зарегистрирован о чем пришло два письма с уведомлениями о привязке почты и регистрации организации. Получив подтверждение регистрации не спешите подтверждать, видимо какие-то проблемы с синхрониpацией, мне удалось попасть в аккаунт только часов через 8 причём через Google аккаунт моей почты.
Никакой идентификации личности, проверки банковских карт не проводится.
SignPath предоставляет два сертификата: тестовый и сертификат выпуска (release sertificate). Тестовый сертификат (само-подписанный и не является доверенным) предназначен для настройки инфраструктуры и дальнейшего использования для тестовых сборок. Сертификат выпуска при регистрации не активен - имеет статус CSR PENDING. Для его активации необходимо выполнить настройку сборки проекта с тестовым сертификатом и дождаться "завершения внутреннего аудита безопасности" (в чем он заключается осталось загадкой).
Настройка GitHub Actions
Подписывать артефакты проекта можно как вручную загружая через сайт (доступно только для тестового сертификата), так и интегрировав процесс подписания в CI, например GitHub Actions или AppVeyor.
В этом разделе для большей наглядности я приведу пример для простого гипотетического приложения, собирающегося скриптом build.bat и состоящего из единственного исполняемого exe-файла. Конфигурация процесса сборки, перед добавлением шагов подписи, будет выглядеть следующим образом:
on: # Запускать процесс сборки при каждом выполнении git push
push:
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build
run: build.bat # Запуск скрипта сборки
- name: Upload artifact
id: build # Этот ID будет использоваться на шаге подписания
uses: actions/upload-artifact@v4
with:
name: my_program
path: my_program.exe # Путь и имя файла - результат сборки
Для интеграции с GitHub Actions необходимо получить API токен для CI. Для этого на странице Users and Groups необходимо выбрать пользователя CI builds и сгенерировать токен. Полученный токен добавляем в секреты репозитория GitHub как SIGNPATH_API_TOKEN
(Settings - Secrets and variables - Actions - Repository secrets).
Так же для настройки сборки на сайте SignPath необходимо найти Organization ID (на странице организации), Project slug (на странице проекта, по умолчанию совпадает с названием проекта) и Signing policy (на странице проекта, по умолчанию test-signing и release-signing).
Отправить файл на сертификацию можно только из артефактов сборки (в примере выполняется на шаге Upload artifact). Артефакты GitHub загружаются в zip-архиве, а SignPath, по умолчанию, работает только с исполняемыми файлами. Поэтому необходимо добавить конфигурацию описывающую артефакт. В приведенном примере артефакт состоит из zip-архива, содержащего единственный exe-файл. Чтобы добавить конфигурацию артефакта необходимо на странице проекта в разделе Artifact Configurations нажать кнопку Add. На открывшейся странице ввести название новой конфигурации (в примере это test-zip), у поля Artifact configuration нажать кнопку Customize или выбрать Zip archive + sign nested files и ввести следующую конфигурацию:
<?xml version="1.0" encoding="utf-8" ?>
<artifact-configuration xmlns="http://signpath.io/artifact-configuration/v1">
<!-- Define that the uploaded artifact is a ZIP file -->
<zip-file>
<pe-file-set>
<!-- Inside this uploaded ZIP file sign three PE files with Authenticode
(PE = Portable Executable, e.g., .exe or .dll file) -->
<include path="*.exe" />
<for-each>
<authenticode-sign />
</for-each>
</pe-file-set>
</zip-file>
</artifact-configuration>

SignPath позволяет подписывать и внутренности MSI-пакетов, достаточно описать структуру пакета в конфигурации артефакта.
После этого нужно добавить шаг подписания в процесс сборки со следующим действием (action) https://github.com/SignPath/github-action-submit-signing-request:
- name: Signing
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '3b087850-3844-4d25-8407-cf81e731f8d1'
project-slug: 'open-numismat'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: 'test-zip'
github-artifact-id: '${{steps.upload.outputs.artifact-id}}'
output-artifact-directory: . # Результат заменит исходный файл
wait-for-completion: true
Этот шаг должен выполняться после шага с выгрузкой артефакта - Upload artifact в нашем примере.
Это действие отправляет артефакт в SignPath где он подписывается и результат загружается обратно. Есть ограничение на загрузку артефактов на проверку - 4Гб в месяц. Обратите внимание, что в SignPath отправляется zip-архив, а когда результат скачивается обратно он распаковывается автоматически по указанному в output-artifact-directory
пути.
После шага подписи полученный подписанный файл вновь загружаем в артефакты:
- name: Upload signed
uses: actions/upload-artifact@v4
with:
name: my_program-signed
path: my_program.exe
Далее следующими шагами артефакты можно загрузить в релиз.
Получившаяся минимальная конфигурация процесса сборки будет выглядеть так:
on: # Запускать процесс сборки при каждом выполнении git push
push:
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build
run: build.bat # Запуск скрипта сборки
- name: Upload artifact
id: build # Этот ID используется на шаге Signing
uses: actions/upload-artifact@v4
with:
name: my_program
path: my_program.exe # Путь и имя файла - результат сборки
- name: Signing
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '3b087850-3844-4d25-8407-cf81e731f8d1'
project-slug: 'open-numismat'
signing-policy-slug: 'test-signing'
artifact-configuration-slug: 'test-zip'
github-artifact-id: '${{steps.upload.outputs.artifact-id}}'
output-artifact-directory: . # Результат заменит исходный файл
wait-for-completion: true
- name: Upload signed
uses: actions/upload-artifact@v4
with:
name: my_program-signed
path: my_program.exe
Теперь можно запустить тестовую сборку (в данном случае запустится сразу после выполнения git push). В логе сборки процесс подписания тестовым сертификатом выглядит так:
Submitting the signing request to SignPath CI connector...
SignPath signing request has been successfully submitted
The signing request id is 29add6f6-d0c7-48c7-963d-3e1f7a8853c9
You can view the signing request here: https://app.signpath.io/Web/3b087850-3844-4d25-8407-cf81e731f8d1/SigningRequests/29add6f6-d0c7-48c7-963d-3e1f7a8853c9
Checking the signing request status...
The signing request status is InProgress, which is not a final status; after a delay, we will check again...
Next check in a few seconds
Signing request status is Completed
Signed artifact url https://app.signpath.io/API/v1/3b087850-3844-4d25-8407-cf81e731f8d1/SigningRequests/29add6f6-d0c7-48c7-963d-3e1f7a8853c9/SignedArtifact
The signed artifact is being downloaded from SignPath and will be saved to D:\a\open-numismat\open-numismat\dist\OpenNumismat
Going to download signed artifact
The signed artifact has been successfully downloaded from SignPath and extracted to D:\a\open-numismat\open-numismat\dist\OpenNumismat
Поскольку я использую Nuitka и PyInstaller и упаковываю результат в zip-архив для портативной версии и в инсталлятор с помощью Inno Setup, то мой вариант несколько сложнее. Перед упаковкой Inno Setup отправляю основной exe-файл на сертификацию, после получения упаковываю в инсталлятор и отправляю получившийся инсталлятор на сертификацию еще раз.
Ошибки с которыми я столкнулся
Error: Could not authorize against SignPath API. Trusted build system cannot be used in conjunction with an interactive user.
или
Error: Only runners in the groups "GitHub Actions" are allowed.
Использован SignPath токен обычного пользователя (interactive user), а не пользователя CI.
Другая ошибка:
There is no signing policy with slug '<…>' in the project with slug '<…>'.
Неверные значения slug - вместо значения Signing policy указал Sertificate slug.
Error: The signing request is not completed. The final status is "Failed"
В случае этой ошибки искать причину нужно на сайте SignPath в разделе Signing Requests.
Сертификат выпуска
После того как все заработало с тестовым сертификатом я написал в поддержку, что у меня все работает и как бы мне получить доверенный сертификат выпуска. Через пару дней получил не очень внятный ответ: "Есть ли у вас в настоящее время на рассмотрении CSR? Если да, дайте нам ответ, чтобы мы могли заказать сертификат продукта". Написал подтверждение, что у меня уже есть сертификат выпуска в состоянии "CSR PENDING". На это получил ответ, что в течении недели я получу свой сертификат выпуска. И через пару часов получил письмо о том, что мой сертификат готов. Не могу сказать, что в этой процедуре было лишним, но в конечном итоге я увидел свой сертификат выпуска на сайте.

После этого обновил значение поле signing-policy-slug
в шаге Signing конфигурации процесса сборки и запустил сборку на GitHub.
Подпись сертификатом выпуска требует ручного подтверждения. Поэтому процесс сборки приостановится на шаге Signing, а в логе сборки появятся следующие строки:
Submitting the signing request to SignPath CI connector...
SignPath signing request has been successfully submitted
The signing request id is 220a33ea-9fc9-4b53-b3b4-2bc176b4e5e2
You can view the signing request here: https://app.signpath.io/Web/3b087850-3844-4d25-8407-cf81e731f8d1/SigningRequests/220a33ea-9fc9-4b53-b3b4-2bc176b4e5e2
Checking the signing request status...
The signing request status is InProgress, which is not a final status; after a delay, we will check again...
Next check in a few seconds
The signing request status is WaitingForApproval, which is not a final status; after a delay, we will check again...
Next check in a few seconds
The signing request status is WaitingForApproval, which is not a final status; after a delay, we will check again...
Next check in a minute
Для подтверждения необходимо перейти по ссылке из лога сборки или на странице Signing Requests найти ваш запрос со статусом Waiting for approval, щелкнуть по его ID:

нажать кнопку Approve. Через несколько секунд статус изменится на Completed:

После этого процесс сборки продолжится и в логе отобразится следующая строка:
Signing request status is Completed
Дальше процесс завершается и полученные подписанные артефакты загружаются в релиз проекта. После скачивания видно, что все что было указано подписано доверенной цифровой подписью:

Запуск приложения теперь происходит более приятным образом, без предупреждений о неизвестном издателе.
Получившаяся конфигурация процесса сборки доступна в репозитории проекта: https://github.com/OpenNumismat/open-numismat/blob/master/.github/workflows/release.yml.
Заключительные штрихи
В требованиях для открытых проектов (https://signpath.org/terms) указано, что на сайте программы необходимо указывать Code signing policy со следующей информацией:
Free code signing provided by SignPath.io, certificate by SignPath Foundation
Что и было сделано.
При активации сертификата выпуска была опубликована ссылка на мой проект на сайте SignPath https://signpath.org/projects - здесь я в приятной компании😉
Надеюсь эта статья поможет и вам сделать мир свободных приложений более доступным и безопасным для широкого круга пользователей.