Введение
Git поддерживает несколько механизмов цифровой подписи объектов репозитория, включая OpenPGP (GnuPG), SSH и X.509. Наиболее распространённым механизмом является OpenPGP, реализуемый с использованием GnuPG (GNU Privacy Guard). Именно этот вариант рассматривается в настоящем документе.
GnuPG не имеет встроенной поддержки интерфейса PKCS#11. Для использования аппаратных токенов, поддерживающих данный интерфейс, необходимо применять промежуточный программный компонент, обеспечивающий взаимодействие между GnuPG и PKCS#11. В рассматриваемом сценарии таким компонентом является gnupg-pkcs11-scd, который взаимодействует с библиотекой librtpkcs11ecp.
Следует учитывать, что GnuPG не поддерживает российские криптографические алгоритмы ГОСТ Р 34.10 и ГОСТ Р 34.11 в качестве алгоритмов OpenPGP. По этой причине необходимо использовать алгоритмы, одновременно поддерживаемые GnuPG и библиотекой librtpkcs11ecp, например RSA или ECDSA. В рассматриваемом примере используется ключ RSA длиной 2048 бит.
Перед выполнением приведённых ниже действий токен Рутокен ЭЦП 3.0 был предварительно отформатирован. Генерация ключевой пары и сертификата выполнялась средствами OpenSC и OpenSSL. Закрытый ключ создаётся непосредственно на токене и не покидает его на протяжении всего жизненного цикла.
Архитектура решения
В рассматриваемом сценарии взаимодействие компонентов осуществляется по следующей схеме:
│
▼
GnuPG (gpg)
│
▼
gpg-agent
│
▼
gnupg-pkcs11-scd
│
▼
PKCS#11
│
▼
librtpkcs11ecp
│
▼
Рутокен ЭЦП 3.0
Git использует GnuPG для создания цифровой подписи. GnuPG взаимодействует с gpg-agent, который вместо стандартного scdaemon использует демон gnupg-pkcs11-scd. Последний обеспечивает доступ к закрытому ключу, расположенному на токене, посредством библиотеки librtpkcs11ecp, реализующей интерфейс PKCS#11.
Используемый стек
Компонент | Версия |
Ubuntu | 24.04 LTS |
Git | 2.43.0 |
GnuPG | 2.4.4 |
OpenSC | 0.25.0~rc1-1ubuntu0.2 |
OpenSSL | 3.0.13 |
librtpkcs11ecp | 2.18.4.0 |
Предварительные требования
Перед началом работы должны быть выполнены следующие условия:
- подключён токен Рутокен ЭЦП 3.0;
- токен инициализирован и при необходимости отформатирован;
- известен пользовательский PIN-код;
- установлена библиотека librtpkcs11ecp;
- установлен пакет OpenSC;
- пользователь обладает правами доступа к USB-устройству.
Установка необходимых пакетов
Обновите список пакетов:
sudo apt update
Установите необходимые пакеты:
sudo apt install git gpg gnupg-pkcs11-scd opensc libengine-pkcs11-openssl -y
После этого необходимо установить библиотеку librtpkcs11ecp. Актуальный пакет следует загрузить с официального сайта Рутокен и установить стандартными средствами пакетного менеджера.
После установки рекомендуется убедиться в расположении библиотеки:
find /usr/lib -name "librtpkcs11ecp.so"
или
dpkg -L librtpkcs11ecp | grep '\.so'
В приведённых далее примерах используется путь
/usr/lib/librtpkcs11ecp.so
Если библиотека установлена в другом каталоге, соответствующие пути необходимо скорректировать.
Настройка OpenSSL
Для выполнения криптографических операций с использованием закрытого ключа, размещённого на токене, необходимо временно настроить OpenSSL на использование PKCS#11.
Создайте в рабочем каталоге файл openssl.cnf следующего содержания:
cat > openssl.cnf <<'EOF' openssl_conf = openssl_init [openssl_init] engines = engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 dynamic_path = /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so MODULE_PATH = /usr/lib/librtpkcs11ecp.so default_algorithms = ALL EOF
Путь к библиотеке pkcs11.so может отличаться в зависимости от используемого дистрибутива Linux и установленной версии пакета libengine-pkcs11-openssl.
Созданный файл используется только при выполнении команд OpenSSL и не изменяет глобальную конфигурацию системы.
Создание ключевой пары
Сгенерируйте на токене ключевую пару RSA длиной 2048 бит:
pkcs11-tool \ --module /usr/lib/librtpkcs11ecp.so \ --keypairgen \ --key-type rsa:2048 \ -l \ --id 3132
Во время выполнения команды потребуется ввести пользовательский PIN-код.
При успешном выполнении будет получен вывод, аналогичный следующему:
Using slot 0 with a present token (0x0) Logging in to "Rutoken ECP <no label>". WARNING: user PIN to be changed Please enter User PIN: Key pair generated: Private Key Object; RSA label: ID: 3132 Usage: decrypt, sign Access: sensitive, always sensitive, never extractable, local Public Key Object; RSA 2048 bits label: ID: 3132 Usage: encrypt, verify Access: local
Создание самоподписанного сертификата
Создайте самоподписанный сертификат, используя закрытый ключ, расположенный на токене:
OPENSSL_CONF=$(pwd)/openssl.cnf \ openssl req \ -engine pkcs11 \ -x509 \ -new \ -key 0:3132 \ -keyform engine \ -out cert.crt \ -subj "/C=RU/ST=Moscow/L=Moscow/O=Aktiv/OU=dev/CN=testuser/emailAddress=testuser@mail.com"
После успешного выполнения команды в рабочем каталоге будет создан файл:
cert.crt
Запись сертификата на токен
После создания сертификата его необходимо записать на токен, чтобы закрытый ключ, открытый ключ и сертификат находились на одном носителе.
Выполните команду:
pkcs11-tool \ --module /usr/lib/librtpkcs11ecp.so \ -l \ -y cert \ -w cert.crt \ --id 3132
Во время выполнения команды потребуется ввести пользовательский PIN-код.
При успешном выполнении будет получен вывод, аналогичный следующему:
Using slot 0 with a present token (0x0) Logging in to "Rutoken ECP <no label>". WARNING: user PIN to be changed Please enter User PIN: Created certificate: Certificate Object; type = X.509 cert label: subject: DN: C=RU, ST=Moscow, L=Moscow, O=Aktiv, OU=dev, CN=testuser, emailAddress=testuser@mail.com serial: 1108129EF7B2EC6058E7399A3B9EE31237D3B117 ID: 3132
После выполнения данной операции на токене будут размещены:
- закрытый ключ;
- открытый ключ;
- сертификат X.509.
Проверить содержимое токена можно командой:
pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -O
В выводе должны присутствовать объекты типа Private Key, Public Key и Certificate с одинаковым идентификатором (ID).
Настройка GnuPG
Для обеспечения работы GnuPG с токенами, поддерживающими PKCS#11, необходимо настроить использование демона gnupg-pkcs11-scd, который выступает промежуточным звеном между GnuPG и библиотекой PKCS#11.
Если каталог конфигурации отсутствует, создайте его:
mkdir -p ~/.gnupg
При необходимости установите рекомендуемые права доступа:
chmod 700 ~/.gnupg
Настройте gpg-agent на использование gnupg-pkcs11-scd вместо стандартного scdaemon:
echo "scdaemon-program /usr/bin/gnupg-pkcs11-scd" > ~/.gnupg/gpg-agent.conf
Создайте файл конфигурации gnupg-pkcs11-scd:
cat > ~/.gnupg/gnupg-pkcs11-scd.conf <<'EOF' providers rutoken provider-rutoken-library /usr/lib/librtpkcs11ecp.so EOF
Если библиотека librtpkcs11ecp.so расположена по другому пути, необходимо указать фактическое расположение.
После изменения конфигурации перезапустите gpg-agent:
gpgconf --kill gpg-agent
Команда завершает работу процесса gpg-agent. При следующем обращении к GnuPG агент будет автоматически запущен с новой конфигурацией.
Проверка обнаружения токена
Проверьте, что GnuPG обнаруживает токен:
gpg --card-status
Несмотря на использование PKCS#11, демон gnupg-pkcs11-scd предоставляет GnuPG виртуальный интерфейс OpenPGP-карты. По этой причине вывод команды соответствует формату, используемому для OpenPGP-карт.
Пример вывода:
Reader ...........: [none] Application ID ...: D27600012401115031311743D2571111 Application type .: OpenPGP Version ..........: 11.50 Manufacturer .....: ? Serial number ....: 1743D257 Name of cardholder: [not set] Language prefs ...: [not set] Salutation .......: URL of public key : [not set] Login data .......: [not set] Signature PIN ....: forced Key attributes ...: rsa48 rsa48 rsa48 Max. PIN lengths .: 0 0 0 PIN retry counter : 0 0 0 Signature counter : 0 Signature key ....: [none] Encryption key....: [none] Authentication key: [none] General key info..: [none]
Такой вывод является ожидаемым.
Если команда завершается ошибкой или не отображает сведения о виртуальной карте, рекомендуется проверить:
- корректность установки библиотеки librtpkcs11ecp;
- правильность пути, указанного в gnupg-pkcs11-scd.conf;
- успешное завершение команды gpgconf --kill gpg-agent;
- подключение токена к системе.
Регистрация ключа в GnuPG
После того как GnuPG получил доступ к токену, необходимо зарегистрировать существующий ключ в локальном хранилище OpenPGP.
Запустите команду:
gpg --expert --full-generate-key
Несмотря на название команды, в данном случае новый ключ создаваться не будет. Она используется для регистрации существующего ключа, расположенного на аппаратном носителе.
После запуска программы выполните следующие действия.
- Выберите пункт Existing key from card независимо от его номера в меню. Нумерация пунктов может отличаться между версиями GnuPG.
- После обнаружения токена GnuPG предложит выбрать поддерживаемые алгоритмы хэширования. Выберите необходимые алгоритмы, затем завершите выбор нажатием клавиши Q.
- Укажите срок действия ключа. Значение 0 означает бессрочный срок действия. После истечения заданного срока ключ не удаляется, а получает статус expired.
- Подтвердите выбранные параметры, ответив Y.
- Укажите имя пользователя, адрес электронной почты и, при необходимости, комментарий.
- После проверки введённых данных подтвердите их, выбрав O (Okay).
После завершения процедуры будет создан локальный объект OpenPGP, связанный с закрытым ключом, размещённым на токене.
Пример вывода:
gpg: revocation certificate stored as '~/.gnupg/openpgp-revocs.d/F3F07ACE83CE8EB92301295353266002A0C1C56B.rev' public and secret key created and signed. pub rsa2048 2026-03-27 [SCEAR] F3F07ACE83CE8EB92301295353266002A0C1C56B uid User Name <test@email.com>
Проверка зарегистрированного ключа
Убедитесь, что ключ успешно зарегистрирован в локальном хранилище GnuPG:
gpg --list-secret-keys --keyid-format=long
При успешной регистрации вывод будет выглядеть примерно следующим образом:
sec> rsa2048/53266002A0C1C56B 2026-03-26 [SCEAR] F3F07ACE83CE8EB92301295353266002A0C1C56B Card serial no. = 3131 1743D257 uid [ultimate] User Name <test@email.com>
Следует обратить внимание на несколько особенностей вывода.
Строка sec> означает, что GnuPG располагает ссылкой на секретный ключ, однако сам закрытый ключ хранится на внешнем устройстве и не может быть экспортирован в локальное хранилище.
Строка Card serial no. = 3131 1743D257 подтверждает, что зарегистрированный ключ связан с аппаратным токеном.
Если вместо sec> отображается sec, это означает, что используется локальный секретный ключ, а не ключ, расположенный на смарт-карте.
Настройка Git
После регистрации ключа необходимо настроить Git на его использование при создании цифровых подписей.
Назначьте зарегистрированный ключ ключом подписи по умолчанию:
git config --global user.signingkey F3F07ACE83CE8EB92301295353266002A0C1C56B
Вместо указанного отпечатка необходимо использовать идентификатор собственного ключа.
Включите обязательную подпись всех создаваемых коммитов:
git config --global commit.gpgsign true
При необходимости явно укажите программу подписи:
git config --global gpg.program gpg
Хотя Git обычно автоматически использует программу gpg, явное указание данного параметра делает конфигурацию более очевидной и исключает неоднозначность при наличии нескольких реализаций GnuPG.
Проверить текущую конфигурацию можно командой:
git config --global --list
Проверка независимости подписи от данных автора:
Для демонстрации того, что цифровая подпись определяется закрытым ключом, а не параметрами автора коммита, можно временно изменить глобальные настройки Git.
Например:
git config --global user.name "My Name" git config --global user.email "no@email.com"
После завершения проверки рекомендуется восстановить исходные значения параметров.
Создание подписанного коммита
Создайте подписанный коммит:
git commit -S -m "test signed commit"
Если параметр commit.gpgsign=true уже установлен, использование ключа -S не является обязательным, поскольку Git автоматически подписывает каждый создаваемый коммит.
Во время выполнения команды GnuPG запросит доступ к закрытому ключу на токене. При необходимости потребуется ввести пользовательский PIN-код.
После успешного завершения операции цифровая подпись будет сформирована непосредственно на токене, а закрытый ключ не покинет аппаратное устройство.
Проверка подписи коммита
Для просмотра информации о цифровых подписях выполните команду:
git log --show-signature
Для просмотра только последнего коммита можно использовать:
git log --show-signature -1
Пример вывода:
commit 487a2127d90c9ccac3ceb331a365cb8e9fd3b344 (HEAD -> master) gpg: WARNING: unsafe permissions on homedir '~/.gnupg' gpg: Signature made Thu 26 Mar 2026 09:47:49 PM MSK gpg: using RSA key F3F07ACE83CE8EB92301295353266002A0C1C56B gpg: Good signature from "User Name <test@email.com>" [ultimate] Author: My Name <no@email.com> Date: Thu Mar 26 21:47:48 2026 +0300 test signed commit
Строка gpg: Good signature from "User Name <test@email.com>" означает, что подпись успешно проверена и соответствует зарегистрированному открытому ключу.
Следует обратить внимание, что автор коммита Author: My Name <no@email.com> не совпадает с владельцем цифровой подписи.
Это является ожидаемым поведением.
Поля Author и Committer определяют автора изменений и пользователя, выполнившего коммит, тогда как цифровая подпись подтверждает только то, каким закрытым ключом был подписан данный объект Git.
Именно поэтому изменение параметров user.name и user.email не влияет на владельца цифровой подписи.
Дополнительная проверка подписи
Проверить подпись конкретного коммита можно командой:
git verify-commit <хэш_коммита>
При успешной проверке GnuPG выведет информацию о владельце подписи и использованном ключе.
Аналогичным образом можно проверить подпись аннотированного тега:
git verify-tag <имя_тега>
Работа при отключённом токене
Если токен отсутствует или недоступен, попытка создания подписанного коммита завершится ошибкой. GnuPG сообщит об отсутствии устройства либо запросит подключение токена с соответствующим серийным номером.
После подключения устройства операция может быть повторена без изменения конфигурации Git или GnuPG. Подписание станет возможным после успешного обнаружения токена и ввода корректного пользовательского PIN-кода.
Закрытый ключ при этом продолжит храниться исключительно на аппаратном носителе и никогда не будет скопирован в память компьютера.
Устранение типичных проблем
Предупреждение unsafe permissions on homedir
Сообщение:
gpg: WARNING: unsafe permissions on homedir '~/.gnupg'
указывает на слишком широкие права доступа к каталогу ~/.gnupg.
Для исправления рекомендуется выполнить:
chmod 700 ~/.gnupg
find ~/.gnupg -type f -exec chmod 600 {} \;
После изменения прав предупреждение перестанет отображаться.
GnuPG не обнаруживает токен
Если команда:
gpg --card-status
не выводит информацию о виртуальной OpenPGP-карте, необходимо проверить:
- подключение токена;
- корректность установки библиотеки librtpkcs11ecp;
- правильность пути, указанного в ~/.gnupg/gnupg-pkcs11-scd.conf;
- успешное выполнение команды gpgconf --kill gpg-agent;
- наличие объектов на токене командой:
pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -O
Git не может подписать коммит
Если при выполнении команды git commit отображается сообщение:
gpg failed to sign the data fatal: failed to write commit object
последовательно проверьте:
- подключён ли токен;
- корректен ли введённый PIN-код;
- отображается ли токен командой gpg --card-status;
- зарегистрирован ли ключ (gpg --list-secret-keys);
- правильно ли указан параметр user.signingkey;
- доступен ли модуль librtpkcs11ecp.
Проверка содержимого токена
При возникновении проблем рекомендуется убедиться, что на токене присутствуют все необходимые объекты:
pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -O
В выводе должны присутствовать:
- закрытый ключ (Private Key);
- открытый ключ (Public Key);
- сертификат (Certificate).
Все три объекта должны иметь одинаковый идентификатор (ID).
Заключение
В результате выполнения описанных действий Git будет использовать GnuPG для создания цифровых подписей, а все криптографические операции с закрытым ключом будут выполняться непосредственно на токене Рутокен ЭЦП 3.0 через интерфейс PKCS#11. Такая схема обеспечивает невозможность извлечения закрытого ключа из памяти токена и позволяет использовать аппаратный носитель для безопасной подписи Git-коммитов, тегов и других объектов репозитория, поддерживающих подписи OpenPGP.
Рассмотренный сценарий протестирован на Ubuntu 24.04 с использованием GnuPG 2.4.4, OpenSC 0.25 и библиотеки librtpkcs11ecp версии 2.18.4.0. При использовании других дистрибутивов Linux или более новых версий программного обеспечения отдельные пути к библиотекам и пакетам могут отличаться, однако общий порядок настройки остаётся аналогичным.