Как реализовать пакетную подпись PDF-документов

Автоматическое подписание документов электронной подписью используют там, где требуется пакетная подпись документов без участия сотрудника. Это могут быть как небольшие сайты, например по продаже билетов в театр и музей, или порталы с онлайн-обучением при отправке сертификатов о прохождении курсов, так и крупные банковские приложения, например, при генерации выписок по счетам, форм договоров или квитанций. В ЕСИА, СМЭВ, ГИС ЖКХ и других государственных информационных системах также реализована автоматическая подпись.
В этой статье реализуем автоматическое подписание для PDF-файлов и добавим штамп «Документ подписан электронной подписью».
Чтобы лучше понять, как же в PDF-файл добавляется электронная подпись, начнем статью со структуры файла. Далее упомянем архивные форматы, т.к. часто в ЭДО важно не только подписать, но и обеспечить длительное хранение. В пункте про электронные подписи покажем, как она встраивается в структуру файла и перечислим некоторые продукты с поддержкой PAdES на ГОСТ-алгоритмах. И, наконец, перейдем к основной теме статьи — пакетная подпись PDF. Если вам интересна отдельная часть, то переходите по заголовкам:
Что хранится внутри PDF-файла?
Как подготовить документ к архивному хранению?
Про электронные подписи в PDF
Пакетная подпись PDF
Что хранится внутри PDF-файла?
Если вы откроете PDF-файл обычным текстовым редактором, а не специальной программой, то увидите набор бессмысленных символов, редкие проблески читаемого текста и знакомые конструкции вроде »%PDF-» или »%%EOF». Какую-то очевидную логику и структуру в контенте проследить сходу сложно.
PDF ориентирован на отображение документа в неизменном виде на любых устройствах. Он сохраняет точное форматирование, шрифты, изображения и макет, что особенно важно для печати и презентаций. Если тот же XML чаще используют для обмена данными между приложениями и сервисами, то PDF — для представления готовых документов, которые читают и обрабатывают люди. Спецификация формата ISO 32000–2 открыта. И хоть она не маленькая (почти тысяча страниц), попробуем выделить основное.
PDF-файл состоит из следующих элементов:

Заголовок
Началом файла является заголовок. Это текстовая строка, из пяти символов »%PDF-» и номера версии (сейчас допустимы значения от %PDF–1.0 до %PDF–2.0)
Для указания ридерам, что после заголовка идут бинарные данные, после заголовка указывается строка с комментарием не менее 4 байт, где каждый байт должен быть равен или больше 128 (0×80). Это обеспечивает корректную обработку с определением типа документа (текстовый или бинарный).
Спецификация допускает наличие произвольных данных до заголовка, они не влияют на обработчики и не учитываются при вычислении смещений (считаем их от знака процента в строке %PDF-).
Тело
Тело документа включает набор объектов, содержащих основной контент документа (изображения, текст и т.д.).
1 0 obj
% ...
endobj
2 0 obj
% ...
endobj
% ...
100 0 obj
% ...
endobj
Опишем восемь основных типов:
Булево значение (Boolean): логические значения true или false.
Число (Numeric): целое или вещественное число со знаком (опционально). Недесятичные основания или экспоненциальная запись не поддерживаются.
Примеры: 42, 3.14, -67.7, +36,6.Строка (String objects): текстовая строка длиной 0 и более байт. Строки оборачивают в круглые скобки (текстовая строка) или в треугольные (шестнадцатеричная строка). Текстовые строки могут записываться в несколько линий с переносом.
Примеры: (Example), (), <4841425220544F5254>.Имя (Name): начинается с символа / и используется для ключей. Содержит последовательность любых символов, кроме Null. Обычно не используются как текст для отображения.
Примеры: /Type, /Name, /123.654.Массив (Array): Коллекция элементов различных типов, записанных в квадратных скобках. Поддерживаются только одномерные массивы, но допускается вкладывать одни массивы в другие с любой глубиной.
Пример: [1 false (Example) 2.13].Словарь (Dictionary): пара «ключ-значение», заключенная в << и >>. Ключ должен быть представлен типом Name и быть уникальным в одном словаре. Значение может быть любого типа (в том числе другим словарем).
Пример:<< /Key /Value
/Subdictionary <<
/Item0 (Example) /Item2 true
>>
>>Поток (Stream): последовательность байт любой длины, заключенных между stream и endstream, записанных после словаря. Например, поток может содержать текст, изображения или шрифты.
Null: ключевое слово null, представляющее отсутствие значения. Если null используется как значение в словаре, то это соответствует пропуску записи.
Таблица перекрестных ссылок
Таблица перекрестных ссылок нужна для быстрого доступа к объектам в файле, обходясь без чтения всего файла. Это похоже на «карту объекта» внутри PDF. Таблица представляет собой набор строк. Может быть в виде потока (stream).
Она состоит из записей формата:
nnnnnnnnnn ggggg eol
Первые 10 символов: смещение объекта в байтах от начала документа.
Следующие 5 символов: номер поколения (или генерации).
Буква n или f: используется (n) или свободен (f).
eol — символ конца строки, должен быть представлен двумя символами.
Допускается два способа обозначения записей. Основной механизм использует связанный список: каждая свободная запись указывает на следующую, а первая запись (объект 0, головной) всегда свободна и имеет номер поколения 65 535, являясь началом списка. Последняя запись (хвост) ссылается обратно на объект 0.
Второй механизм позволяет другим свободным записям ссылаться на объект 0 с номером поколения 65 535, даже если они не входят в связанный список. Все объекты, кроме нулевого, изначально имеют номер поколения 0. При удалении объекта его запись помечается как свободная, добавляется в список, и ее номер поколения увеличивается на 1 для будущего использования. Максимальный номер поколения — 65 535; при его достижении запись больше не используется. Таблица должна содержать записи для всех номеров объектов от 0 до максимального, даже если некоторые объекты отсутствуют в файле.
xref
0 6
0000000001 65535 f
0000000018 00000 n
0000000091 00000 n
0000000000 00009 f
0000000321 00000 n
0000000543 00000 n
Пример: Таблица описывает 6 объектов, где с индексом 0 — удаленный, 1 — начинается с 18 байта, 2 — начинается с 91 байта, 3 — удаленный, 4 — начинается с 321 байта и 5 — начинается с 543 байта.
xref
0 1
0000000000 65535 f
2 1
0000011625 00000 n
13 2
0000025675 00002 n
25 1
0000026725 00000 n
32 1
0000025924 00000 n
Пример: Таблица содержит 5 объектов, где первый с индексом 0 — удаленный, с индексом 2 — начинается с 11625 байта, с индексом 13 — начинается с 25675 байта и имеет номер генерации равный 2, с индексом 25 — начинается с 264725 байта, с индексом 32 — начинается с 25924 байта.
Трейлер
Трейлер служит своеобразным «содержанием». Он помогает ридеру мгновенно найти нужные объекты и ускоряет открытие документа.
Трейлер используется, чтобы быстро находить таблицу ссылок и некоторые специальные объекты. PDF-документ читается с конца, где последняя строка должна включать маркер %%EOF. Перед ним идут две строки — startxref и смещение в байтах до начала таблицы ссылок. Перед startxref находится словарь трейлера.
Структура трейлера:
trailer
<<
key1 value1
key2 value2
...
keyN valueN
>>
startxref
Byte_offset_of_last_cross-reference_section
%%EOF
Трейлер включает ключи:
/Size: количество записей в таблице.
/Prev: ссылка на предыдущий раздел перекрестных ссылок.
/Root: ссылка на корневой объект.
/Info: опционально, ссылка на метаданные документа.
Трейлер завершается ключевым словом startxref, за которым следует смещение кросс-референс таблицы.
Пример минимального файла
Рассмотрим следующий пример файла:
%PDF-2.0
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R >>
endobj
4 0 obj
<< /Length 44 >>
stream
BT
/F1 24 Tf
100 700 Td
(HABR) Tj
ET
endstream
endobj
xref
0 5
0000000000 65535 f
0000000009 00000 n
0000000056 00000 n
0000000111 00000 n
0000000219 00000 n
trailer
<< /Size 5 /Root 1 0 R >>
startxref
478
%%EOF
Давайте разберем основные элементы этого файла:
Заголовок %PDF-2.0 указывает, что файл соответствует версии 2.0.
Объект 1 0 obj является корневым каталогом документа и содержит ссылку на объект 2 0 R, который описывает страницы.
Объект 2 0 obj — это объект страниц, который ссылается на объект 3 0 R, представляющий первую страницу.
Объект 3 0 obj описывает страницу, включая ее размеры (/MediaBox) и содержимое (/Contents 4 0 R).
Объект 4 0 obj содержит поток данных (stream), включающий команды для отрисовки текста «Potato».
Кросс-референс таблица (xref) указывает на смещения всех объектов в файле.
Трейлер содержит информацию о корневом каталоге и размере таблицы.
Как вносятся изменения в PDF-файл?
Формат PDF позволяет вносить изменения в документ без полной перезаписи файла. Этот механизм называется инкрементальным обновлением (incremental update) и представляет собой процесс добавления новых данных в конец существующего файла. Вместо удаления старых данных новые объекты просто добавляются в конец файла, создается новая таблица перекрестных ссылок и обновляется трейлер. Такой подход не только экономит время при сохранении изменений, но и делает возможным редактирование в сценариях, когда перезапись исходного файла недоступна (например, при работе с документами через HTTP или OLE-встраивание в Windows).
По спецификации при инкрементном обновлении создается новая секция с изменениями, которая включает обновленные объекты, новую таблицу перекрестных ссылок (xref) и трейлер. При этом старые объекты не удаляются, а помечаются как неактивные.

Пример: если в документ добавляется новая аннотация, она записывается как новый объект, а затем обновляется страница, к которой она относится. В xref-таблице указываются смещения этих объектов в файле.
Предположим, мы хотим добавить текстовую аннотацию. Для этого создается новый объект, описывающий аннотацию, и обновляется объект страницы, чтобы включить ссылку на эту аннотацию.
7 0 obj<>
endobj
Далее обновляется страница, к которой добавляется ссылка на аннотацию:
3 0 obj<>
endobj
Наконец, создается новая xref-таблица, которая указывает смещения новых объектов, и добавляется новый trailer:
xref
0 1
0000000000 65535 f
3 1
0000000744 00000 n
7 1
0000000631 00000 n
trailer <>
Так выглядит указание на новую таблицу xref и завершение файла:
startxref
863
%%EOF
После этих изменений файл будет содержать новую аннотацию, исходные данные останутся нетронутыми. А ридеры будут использовать самую последнюю версию объекта, указанную в последнем xref-индексе.
Как подготовить документ к архивному хранению?
PDF рассчитан на одинаковое отображение документов на всех устройствах. Но когда речь идет о долгосрочном хранении, таких характеристик недостаточно. Для архивирования требуется не только точное сохранение содержимого, но и гарантии, что документ будет доступен для чтения через десятилетия независимо от используемого программного обеспечения или оборудования. Именно для таких целей и были созданы архивные форматы PDF/A.
Стандарт для архивного хранения
PDF/A — это подмножество формата PDF, созданное для обеспечения долговременного хранения электронных документов. Он исключает функции и возможности, которые могут затруднить или сделать невозможным доступ к файлу в будущем. Впервые архивный формат был стандартизирован как ISO 19005 в 2005 году.
Цель этого формата — сохранить все данные, необходимые для отображения документа, внутри самого файла. Это включает шрифты, цветовые профили, метаданные и даже мультимедийный контент (в определенных случаях).
Основные отличия PDF/A от стандартного PDF
PDF/A налагает ряд ограничений на использование функций стандартного PDF. Некоторые особенности:
Встраивание шрифтов:
Все шрифты, используемые в документе, должны быть встроены. Это предотвращает проблемы с отображением текста, если шрифт отсутствует на устройстве пользователя.
Запрет шифрования:
Не допускает шифрование или защиту паролем, так как это затрудняет доступ к документу в будущем.
Однозначность цветов:
Все цвета в документе должны быть описаны с использованием цветовых профилей, таких как sRGB или CMYK.
Запрет внешних ссылок:
Документ не должен ссылаться на внешние ресурсы, чтобы избежать потери данных при недоступности этих ресурсов.
Метаданные:
Требует обязательного использования метаданных в формате XMP (Extensible Metadata Platform).
Поддержка только статического контента:
Запрещено использование JavaScript, мультимедиа и других интерактивных элементов.
Определенный порядок слоев и прозрачности:
Для упрощения рендеринга в будущем все визуальные элементы должны быть однозначно упорядочены.
Уровни соответствия PDF/A
PDF/A состоит из нескольких частей, каждая из которых расширяет и уточняет стандарт:
PDF/A-1: базовый уровень (основан на PDF 1.4).
Подходит для текстовых документов без сложных элементов вроде прозрачности.
PDF/A-2: поддержка новых функций (PDF 1.7).
Добавлена прозрачность, сжатие JPEG 2000 и возможность объединять файлы в один PDF/A.
PDF/A-3: поддержка вложений.
Позволяет встраивать произвольные файлы (например, XML или CSV) внутрь PDF/A.
Кроме того, PDF/A делится на уровни соответствия:
a (accessibility): требует полной доступности, включая семантические теги.
b (basic): гарантирует только визуальную целостность.
u (unicode): обеспечивает сохранение всех текстовых данных в Unicode.
Зачем использовать PDF/A?
PDF/A незаменим в тех случаях, когда важно обеспечить долгосрочное хранение документов с сохранением их юридической значимости и информационной ценности. Основные сценарии применения:
Юридические документы: Контракты, соглашения, судебные акты.
Архивы организаций: Корпоративные отчеты, научные исследования, проекты.
Государственные документы: Законы, постановления, исторические архивы.
Медицинские записи: Истории болезней, результаты анализов.
Чем проверить соответствие PDF/A?
Для проверки документов на соответствие PDF/A используются специальные инструменты. Можно пользоваться встроенным в Adobe Acrobat. Если выбирать из opensource, то одним из наиболее надежных решений является veraPDF. Это бесплатное и открытое ПО, разработанное в рамках проекта Open Preservation Foundation и поддерживаемое Европейской комиссией.
Основные возможности veraPDF:
Проверка соответствия всем частям стандарта ISO 19005.
Отчеты о нарушениях в структуре PDF/A.
Поддержка командной строки для интеграции в автоматизированные процессы.
Пример: проверка PDF/A с помощью veraPDF и формированием отчета в html формате
verapdf.bat --format html "D:\Simple PDF 2.0 file.pdf" >> "D:\Simple PDF 2.0 file.report.html"
Инструмент анализирует файл, выявляет несоответствия и предлагает рекомендации по их устранению. Если документ соответствует стандарту, будет выведено подтверждение.

Какие регламенты по использованию PDF/A есть в России?
Федеральная налоговая служба (ФНС) Российской Федерации утвердила использование формата PDF/A-3 для подачи договорной документации в электронном виде. Данное решение зафиксировано в Приказе ФНС России от 24.03.2022 № ЕД-7–26/236@, который начал действовать спустя 30 дней после официальной публикации.
Формат PDF/A-3 был принят для обеспечения долгосрочного хранения электронных документов и их совместимости с различными программными платформами. PDF/A-3 позволяет объединить визуальное отображение документа (для удобства восприятия человеком) и структурированные данные в формате XML (для автоматизированной обработки). Это важно для договоров, соглашений, протоколов разногласий и других документов, требующих высокой точности и юридической значимости.
Ключевые характеристики формата:
Единый файл-контейнер: содержится как визуальная часть (для чтения), так и вложенный XML-файл (для автоматической обработки).
Электронная подпись: Документы заверяются электронной подписью, соответствующей требованиям Федерального закона № 63-ФЗ «Об электронной подписи».
Универсальность: Формат поддерживает различные типы договорных документов, включая договоры, протоколы разногласий и дополнительные соглашения.
Про электронные подписи в PDF
Мы уже познакомились с базовой структурой файла и с инкрементальным обновлением, теперь рассмотрим как это использовать при добавлении подписи внутрь документа.
А зачем вообще использовать электронную подпись? ЭП обеспечивает:
Проверку целостности документа — гарантия, что документ не изменялся после подписания.
Подтверждение личность подписавшего с использованием инфраструктуры открытых ключей (PKI).
Контроль изменений и ограничение разрешений после подписания.
Электронная подпись интегрирована в PDF таким образом, что не требует отдельного файла. Подписанный так документ можно открывать для чтения в любом доступном ПО.
PAdES (PDF Advanced Electronic Signatures) — это стандарт для создания и проверки электронных подписей в документах формата PDF. Он был разработан на основе спецификаций ETSI (European Telecommunications Standards Institute) и обеспечивает высокий уровень безопасности и юридической значимости подписей. PAdES поддерживает различные уровни подписей, включая долгосрочные, что позволяет гарантировать долговечность и достоверность подписанных документов даже спустя годы.
Типы подписей в PDF
Стандарт поддерживает два типа электронных подписей с использованием сертификатов :
Заверяющая подпись или подпись для утверждения (Approval Signature):
Позволяет многократное подписание.
Может использоваться для заполнения форм.
Сертифицирующая подпись (Certification Signature):
Должна быть одна в документе и при этом ставится первой.
Ограничивает дальнейшие изменения (например, разрешая только аннотации или добавление подписей).
Может быть как видимой, так и без отображения на странице.
Основная структура подписей в PDF
Электронная подпись в PDF хранится в специальной структуре, называемой словарем подписей (Signature Dictionary). Эта структура содержит метаданные подписи и ссылки на связанные данные:
Ключ /Contents: хранит значение подписи — закодированный объект PKCS#7, включающий хеш документа, зашифрованный закрытым ключом подписанта.
Ключ /ByteRange: указывает, какие байты документа были включены в хеш при создании подписи. Это массив из четырех чисел, определяющий диапазоны данных (до и после области, зарезервированной для подписи).
Ключ /Filter: указывает, какой обработчик подписей использовался.
Ключ /SubFilter: уточняет формат и алгоритм подписи.
Пример структуры подписей:
/Type /Sig
/Filter /Adobe.PPKLite - Используемый метод подписи
/SubFilter /adbe.pkcs7.detached - Формат подписи
/ByteRange [0 840 960 240] - Диапазон для хеширования
/Contents <...> Поле для самой подписи (резервируем место)
/Reason (Approval of contract) - Причина подписи (опционально)
/M (D:20250128120000Z) - Дата подписи
/ContactInfo (support@example.com)
Процесс создания подписи
Добавление электронной подписи состоит из нескольких этапов:
Новая секция обновления добавляется в документ (то самое инкрементальное обновление).
Добавляем изображение в документ для XObject Form связанного с подписью (если требуется визуализация).
Резервируем пространство и выполняем промежуточное сохранение документа перед подписанием
Резервируем пространство в поле Contents. Значение этого поля не может быть меньше значения подписи, поэтому выделяем с запасом (неиспользуемая часть будет заполнена нулями).
Так как мы не знаем, где окажется Contents после сохранения, то нам потребуется также зарезервировать пространство для ByteRange.
Массив /ByteRange состоит из четырех чисел. Первое число в каждой паре — это смещение (от начала, начиная с 0) начала потока байтов, которые должны быть включены в хэш. Второе число — это длина этого потока. Две пары определяют две последовательности байтов, которые определяют, что должно быть хэшировано.
Перед подписанием ByteRange имеет вид: /ByteRange [ 0 0 0 0]
3. После сохранения нужно определить начало и конец содержимого Contents (значением между < >) и обновить значения для ByteRange.
/ByteRange [ 0 3291 7935 4218]
Получим подписываемый контент с использованием имеющихся значений ByteRange.
Вызываем подпись полученного контента с помощью обработчика (отдельного модуля).
Сохраняем полученную подпись в поле Contents.
Пример готовой структуры Contents: /Contents <3082025B06092A864886F70D010702A082024C30820248...>

Продукты и сервисы с поддержкой PAdES на ГОСТах
Выбор подходящего инструмента для работы с электронной подписью может быть непростой задачей: важно учитывать не только функциональность, но и соответствие стандартам, удобство использования и интеграцию с другими системами. Перечислим некоторые популярные решения.
Продукты
КриптоАРМ — это решение для работы с электронной подписью и шифрованием данных, включая подписание PDF-документов. Продукт поддерживает различные форматы подписей, включая CAdES и PAdES по ГОСТам. Можно подписывать документы встроенной, присоединенной или отсоединенной подписью на выбор. Есть сертификация pdf документа, можно разметить несколько областей для подписания, конвертировать в архивный формат. Есть поддержка разных ОС (Windows, Linux, macOS). В режиме пакетной подписи может подписать файлы любого формата. Реализована визуализация электронной подписи — добавление изображения штампа подписи. Для серверного использования, под автоматизацию, есть отдельное исполнение — КриптоАРМ Server.
КриптоПро PDF — это модуль для Adobe Acrobat и Reader, предназначенный для создания и проверки электронных подписей на ГОСТ-алгоритмах. Гибкая настройка внешнего вида штампа. Есть пакетная подпись (только в Adobe Acrobat Standard или Adobe Acrobat Pro). Работает только на Windows.
ContentReader PDF — это специализированное приложение для просмотра и работы с PDF-документами, включая функции подписания и проверки электронных подписей, что позволяет использовать его для работы с юридически значимыми документами. Из интересных дополнительных функций: возможность извлечь текст из отсканированных документов.
CryptExpert — это программное обеспечение для создания и проверки электронных подписей. В CryptExpert можно подписывать PDF-файлы как встроенной подписью, так и в виде отдельного файла.
Окуляр ГОСТ — приложение создано на основе свободного приложения Okular. Дополнено поддержкой ЭП (использует КриптоПро). Работает на Windows и Linux.
Сервисы
sign.kloud.one — это сервис для проверки электронных подписей, в том числе и стандарта PAdES.Для PDF-документов после проверки доступна возможность скачать копию файла с визуальным штампом о сведениях подписи. Интересная опция: возможность улучшить стандарт подписи. Исходный код открыт и доступен по ссылке.
КриптоПро SVS 2.0 — это программно аппаратный комплекс для проверки сертификатов и электронных подписей. Решение позволяет проверять подписи, созданные с использованием различных стандартов, таких как CAdES, XAdES и PAdES, а также поддерживает проверку сертификатов на соответствие требованиям регулятора.
Миг24 — это сервис для формирования разных видов подписей (открепленная подпись, прикрепленная подпись, встроенная). Решение поддерживает стандарты ГОСТ. Есть возможность визуализировать подпись.
Пакетная подпись PDF
После знакомства со структурой файла и основами PAdES переходим к практической части — настройке автоматической подписи документов на сервере.
Чтобы не подписывать некую сову, предположим, что мы — бюро кредитных историй (это не так, все совпадения случайны). На Госуслугах начала работать услуга по самозапрету кредитования. Наша задача — отправить клиенту решение в читаемом виде и подписанное УКЭП. Собравшись и обсудив задачу с командой, принято решение — не отправлять PDF с картинкой про подпись, а использовать PAdES.
Т.к. для подписания нам в любом случае потребуется сертификат, необходимо его получить. И нам, как БКИ, стоит обратиться с этим в УЦ Центрального Банка. Сертификат нам выдали, но в сведениях о субъекте там нет ни имени руководителя, ни хотя бы сотрудника. Ошибка? Нет! Это так называемый обезличенный сертификат, который и нужен для автоматического подписания на сервере.
Что такое обезличенная электронная подпись?
Обезличенная ЭП — это цифровая подпись, созданная на основе сертификата, в котором отсутствуют персональные данные владельца. Такой формат используется юридическими лицами и индивидуальными предпринимателями (для ИП в сертификате могут быть указаны их данные). По своей сути, эта подпись выполняет роль организационной печати, упрощая автоматизацию документооборота в цифровых системах. Важное отличие — сертификат обезличенной подписи не привязан к конкретному сотруднику. Его выдают для информационных систем, в которых автоматизирован процесс подписи и шифрования.
В России обезличенные сертификаты для информационных систем выдаются несколькими государственными структурами:
ФНС России выпускает квалифицированные сертификаты операторам информационных систем.
Центробанк РФ выпускает такие сертификаты для организаций, работающих в рамках ч. 1 ст. 76.1 Закона о Центральном банке.
Федеральное казначейство выдает сертификаты для органов государственной власти.
Примеры кода по автоматизации подписи PDF
Следующий шаг нашей команды за аналитиками и разработкой. Они подбирают подходящее решение для пакетной подписи PDF. Из условий: модуль можно использовать в node.js и упаковать всё решение в микросервис, а подпись конечно должна быть реализована на ГОСТ-алгоритмах. В качестве подходящего варианта для работы со структурой файла, решили остановится на модуле с открытым кодом — trusted-pdf (из состава КриптоАРМ Server, о нем упоминали ранее в списке продуктов).
Модуль выбран, сертификат есть, переходим к примерам кода. В качестве исходного документа возьмем пример файла из репозитория от PDF Association.
Для простоты тут приведем псевдокод реализации (если будет интересно, напишите, выложим полные примеры):
Добавление невидимой подписи
import * as fs from "node:fs";
import * as pdf from "@trusted-pdf/pdf";
import * as pdfSign from "@trusted-pdf/sign";
const buf = fs.readFileSync(path/Simple PDF 2.0 file.pdf);
const doc = new pdf.Document();
doc.read(buf);
const sign = new pdfSign.Sign(doc);
sign.addSignatures({
name: "signature 1",
page: 1,
});
doc.save();
Добавление подписи со штампом
import * as fs from "node:fs";
import * as pdf from "@trusted-pdf/pdf";
import * as pdfSign from "@trusted-pdf/sign";
const buf = fs.readFileSync(path/to/input.pdf);
const doc = new pdf.Document();
doc.read(buf);
const sign = new pdfSign.Sign(doc);
sign.addSignatures({
name: "signature 1",
page: 1,
rectangle: {
x: 20,
y: 750,
height: 150,
width: 50,
},
stream: Buffer.from("0.5 g\n0 0 150 50 re\nf"),
});
doc.save();
Подписание внешним модулем
import * as fs from "node:fs";
import * as pdf from "@trusted-pdf/pdf";
import * as pdfSign from "@trusted-pdf/sign";
const buf = fs.readFileSync(path/to/input.pdf);
const doc = new pdf.Document();
doc.read(buf);
const sign = new pdfSign.Sign(doc);
const sig = sign.getSignature("signature 1");
await sig.sign({
contentLength: 1300,
onUpdate: async () => {
sig.dict
.get("V", PdfDictionary)
.set("Name", doc.createLiteral("Иванов И.И."));
},
onSign: async (content) => {
// Подписание Content и формирование блока подписи в формате CMS
// Выполняется в отдельном модуле
return new Uint8Array(cms);
},
});
doc.save();
Результат проверим в некоторых упомянутых ранее продуктах и сервисах:


Вывод
Формат PDF — это не просто удобный способ представления документов для чтения или печати, но и хороший инструмент для интеграции с системами электронного документооборота, архивного хранения и электронной подписи.
Мы рассмотрели основные элементы PDF-файла, требования архивного стандарта и возможности использования ГОСТ-алгоритмов для подписи документов (с примерами продуктов и сервисов). Надеемся, что представленные материалы помогут вам глубже понять формат и успешно применять его в ваших проектах.
P.S. Если какую-то тему стоит раскрыть подробно отдельно, пишите в комментариях.