Подписать PDF в Linux можно без Adobe Acrobat: есть свободные инструменты для создания криптографической подписи и её проверки. Важно различать два действия: нарисованная картинка с подписью — это просто изображение, а электронная подпись — криптографический объект, связанный с содержимым документа. Любое изменение уже подписанных байтов делает её недействительной. Ниже — практический маршрут: подготовка сертификата, подпись через PyHanko и JSignPdf, проверка через pdfsig и разбор ошибок валидации.
Что находится внутри подписи PDF
В PDF подпись хранится в специальном словаре. При подписании вычисляется хеш байтового диапазона файла, затем создаётся CMS/PKCS#7-структура с сертификатом и подписью, а в документ добавляется поле с этой структурой. Диапазон ByteRange показывает, какие байты покрыты подписью; поле подписи и последующие изменения в него не входят. Формат PAdES (ETSI EN 319 132) описывает профиль таких подписей и уровни с меткой времени и данными проверки.
Подпись бывает видимой — с изображением, именем и датой на странице — и невидимой. Видимая часть не влияет на криптографию: её можно нарисовать, но технически документ защищает именно криптографическая структура. Также различают обычную подпись и сертификацию документа: вторая задаёт, какие изменения разрешены после подписания. Файл может содержать несколько подписей: каждая добавляется отдельным инкрементальным обновлением и покрывает свою часть байтов. Поэтому проверять нужно все подписи, а не только первую.
Подготовка тестового сертификата
Для экспериментов подойдёт самоподписанный сертификат. Для рабочего документооборота используется сертификат, выданный доверенным удостоверяющим центром, а в России — квалифицированный сертификат; юридические требования зависят от соглашения сторон и вида документа, поэтому здесь мы ограничиваемся технической стороной.
openssl req -x509 -newkey rsa:3072 -keyout test.key -out test.crt -days 365 -nodes -subj "/CN=Test User/O=Example"
openssl pkcs12 -export -inkey test.key -in test.crt -out test.p12 -name "test"
chmod 600 test.key test.p12
openssl pkcs12 -info -in test.p12 -noout
Флаг -nodes оставляет ключ без пароля — только для тестов. Пароль на .p12 обязателен, а сам файл нельзя коммитить в репозиторий. Для реальной работы ключ лучше хранить в аппаратном токене или смарт-карте; тогда PyHanko и JSignPdf потребуют отдельного провайдера PKCS#11.
Подпись через PyHanko
PyHanko — библиотека и CLI для работы с PDF-подписями. Установите её изолированно через pipx, чтобы не смешивать зависимости с системным Python:
pipx install pyhanko
export PDF_PASS='пароль-от-p12'
pyhanko sign addsig --field Sig1 --p12 test.p12 --passphrase env:PDF_PASS \
--reason "Согласование" --location "Москва" input.pdf signed.pdf
pyhanko sign validate --trust test.crt signed.pdf
Параметр --field задаёт имя поля подписи; если поля нет, оно создастся. --passphrase env:PDF_PASS читает пароль из переменной окружения, а не из истории команд. Для метки времени добавьте --tsa с адресом доверенного TSA-сервера; для долговременной проверки — данные OCSP/CRL, которые PyHanko умеет встраивать. Без метки времени подпись остаётся действительной, но после истечения сертификата доказать момент подписания сложнее.
Вариант с JSignPdf и Okular
Если нужен графический интерфейс, JSignPdf умеет и пакетный режим. Синтаксис зависит от версии, поэтому проверьте --help; типовая команда выглядит так:
java -jar JSignPdf.jar --help
java -jar JSignPdf.jar -kstore test.p12 -kspass "$PDF_PASS" \
-kalias test -d signed/ input.pdf
Okular умеет добавлять и проверять подписи через меню работы с цифровыми подписями. Ему нужен сертификат с ключом в хранилище NSS; импорт .p12 можно сделать командой pk12util:
mkdir -p ~/.pki/nssdb
certutil -N -d sql:$HOME/.pki/nssdb --empty-password
pk12util -i test.p12 -d sql:$HOME/.pki/nssdb
После импорта сертификат появится в списке, и документ можно подписать без терминала. Для проверки чужих подписей в Okular достаточно открыть файл: программа покажет статус, но доверие к корневому сертификату всё равно нужно настроить отдельно.
Проверка подписи
Универсальный инструмент — pdfsig из пакета poppler-utils. Он показывает количество подписей, тип, имя подписавшего и результат проверки целостности:
pdfsig signed.pdf
pdfsig -dump signed.pdf
qpdf --check signed.pdf
pdfinfo signed.pdf | head -20
pdfsig проверяет, что байты документа не изменились после подписания, и проверяет сертификат. Если корневой сертификат не добавлен в хранилище NSS, статус может выглядеть как «не удалось проверить цепочку», хотя сама подпись корректна. Для самоподписанного сертификата это ожидаемо. qpdf --check полезен для проверки структуры файла, но не заменяет криптографическую валидацию.
Для проверки в скрипте удобен PyHanko: он возвращает ненулевой код при ошибке и умеет работать с доверенными корнями через --trust. Сравнивайте результаты двух инструментов: расхождение часто указывает на отсутствие промежуточного сертификата или на неверно выбранный профиль подписи.
Российская практика и ГОСТ
В российском документообороте чаще используют квалифицированную электронную подпись с сертификатом аккредитованного удостоверяющего центра и криптографией по ГОСТ Р 34.10-2012. Для PDF это означает поддержку соответствующего криптопровайдера: CryptoPro CSP и специализированный модуль или редактор, который умеет встраивать такую подпись в документ. Утилита cryptcp создаёт отделённую CMS-подпись файла, но не встраивает её в PDF; для встроенной подписи нужен инструмент с поддержкой ГОСТ и PAdES. Проверка выполняется тем же криптопровайдером либо сервисом, которому доверяет получатель. Юридические требования к формату и сертификату зависят от вида документа, поэтому техническую проверку стоит отделять от оценки юридической силы.
Типичные ошибки
- Картинка вместо подписи. Изображение не защищает документ от изменений.
- Правка после подписания. Любое изменение подписанных байтов делает подпись недействительной, даже если визуально документ тот же.
- Нет цепочки сертификатов. Проверяющий не может построить путь до доверенного корня.
- Истёк сертификат без метки времени. Подпись, сделанная в срок, но проверяемая позже, выглядит сомнительной.
- Подпись в зашифрованном PDF. Некоторые инструменты требуют сначала снять пароль или поддерживают только определённые режимы шифрования.
- Несколько подписей. Проверяйте каждую отдельно: действительность первой не гарантирует корректность последующих.
Итог
В Linux есть полный набор инструментов для PAdES-подписи и проверки PDF: PyHanko для сценариев и CI, JSignPdf и Okular для настольной работы, pdfsig и qpdf для контроля результата. Ключевой принцип — подписывать нужно криптографической структурой, а не изображением, и проверять цепочку сертификатов и метку времени. Чек-лист: защищённый ключ, корректный профиль подписи, доверенный корень у проверяющего, повторная проверка после любых правок и понятный журнал того, кто и когда подписал документ.