Одной из интересных и важных особенностей документов в формате PDF, является возможность подписи документа с помощью цифрового сертификата. Цифровая подпись, как и рукописная на бумаге, позволяет идентифицировать человека, подписавшего документ. Но, в отличие от рукописной, такую подпись сложнее подделать. Кроме того, она позволяет определить, был ли документ изменен после подписания.
Используемая мной для работы с документами в формате PDF библиотека Foxit Quick PDF Library (бывшая Debenu Quick PDF Library) имеет два варианта реализации подписи документа с помощью цифрового сертификата.
Первый вариант очень простой. Он создает невидимую подпись и реализуется с помощью вызова одной функции:
Загрузку сертификата из файла формата PKCS#12 используя метод SetSignProcessPFXFromFile можно заменить на выбор сертификата из хранилища сертификатов Windows по его SHA1-хешу:
Используемая мной для работы с документами в формате PDF библиотека Foxit Quick PDF Library (бывшая Debenu Quick PDF Library) имеет два варианта реализации подписи документа с помощью цифрового сертификата.
Первый вариант очень простой. Он создает невидимую подпись и реализуется с помощью вызова одной функции:
procedure SignFile(const sPdfFileName, sPdfPassword, sPfxFileName, sPfxPassword: String); var pdf: TDebenuPDFLibrary; iSignResult: Integer; begin pdf := TDebenuPDFLibrary.Create; try // Подписываем документ сертификатом в формате PKCS#12 iSignResult := pdf.SignFile(sPdfFileName, // наименование документа для подписи sPdfFileName, // пароль документа 'Test signature field', // именованное поле для подписи // наименование подписанного документа ChangeFileExt(sPdfFileName, '-signed.pdf'), sPfxFileName, // файл сертификата sPfxPassword, // пароль сертификата 'Тестирование подписи', // причина подписания 'Belarus', // местонахождение 'https://it-blackcat.blogspot.com'); // контактная информация // Проверяем результат выполнения функции подписи WriteSignResult(iSignResult); finally pdf.Free; end; end; procedure WriteSignResult(const iSignResult: Integer); begin case iSignResult of 1 : WriteLn('The file was signed successfully'); 2 : WriteLn('Input PDF not found'); 3 : WriteLn('Input PDF cannot be read'); 4 : WriteLn('Input PDF password incorrect'); 5 : WriteLn('Certificate file not found'); 6 : WriteLn('Certificate file is invalid'); 7 : WriteLn('Incorrect certificate password'); 8 : WriteLn('Unknown certificate format'); 9 : WriteLn('No private key found in certificate file'); 10: WriteLn('Could not write output file'); 11: WriteLn('Could not apply signature'); 12: WriteLn('The signature field name was blank'); 13: WriteLn('The input file cannot be signed because the "NeedAppearances" ' + 'flag is set to true'); 14: WriteLn('Certificate not found in store'); 15: WriteLn('The input file cannot be signed due to an xref table issue'); 16: WriteLn('Could not apply timestamp to signature'); else WriteLn('Unknown result code: ' + iSignResult.ToString); end; end;Исходный код приведенных процедур содержит подробные комментарии, поэтому нет смысла его описывать Добавлю только, что для подписи используется файл формата PKCS#12 (PFX-файл), который содержит закрытый ключ и сертификат X.509. Если взглянуть на свойства добавленной в документ подписи, то мы найдем там параметры, которые задали из программы и информацию о том, что этот документ после подписания не изменялся: Второй вариант немного сложнее и требует написания нескольких строк кода. Но он позволяет добавить в документ видимую цифровую подпись:
procedure SignFile(const sPdfFileName, sPdfPassword, sPfxFileName, sPfxPassword: String); var pdf: TDebenuPDFLibrary; iSignProcessID: Integer; begin pdf := TDebenuPDFLibrary.Create; try // Создаем новый процесс цифровой подписи iSignProcessID := pdf.NewSignProcessFromFile(sPdfFileName, sPdfPassword); if iSignProcessID > 0 then try // Используем для подписи файл формата PKCS#12 с закрытым ключом и сертификатом X.509 pdf.SetSignProcessPFXFromFile(iSignProcessID, sPfxFileName, sPfxPassword); // Задаем информацию для подписи pdf.SetSignProcessInfo(iSignProcessID, 'Тестирование подписи', // причина подписания 'Belarus', // местонахождение 'https://it-blackcat.blogspot.com'); // контактная информация // Задаем именованное поле для подписи pdf.SetSignProcessField(iSignProcessID, 'Test signature field'); // Если поле с указанным именем не найдено, то на первой странице будет создано // новое невидимое поле нулевой ширины и высоты, которое необходимо настроить // Задаем номер страницы, на которой будет размещено поле для подписи pdf.SetSignProcessFieldPage(iSignProcessID, 1); // Задаем расположение и размер поля для подписи pdf.SetSignProcessFieldBounds(iSignProcessID, 95, 815, 85, 20); // Задаем полю для подписи файл с изображением pdf.SetSignProcessFieldImageFromFile(iSignProcessID, 'Bill-Gates.jpg', 0); // Завершаем процесс подписи и записываем подписанный документ в файл pdf.EndSignProcessToFile(iSignProcessID, ChangeFileExt(sPdfFileName, '-signed.pdf')); // Проверяем результат выполнения процесса подписи WriteSignResult(pdf.GetSignProcessResult(iSignProcessID)); finally // Удаляем процесс подписи из памяти pdf.ReleaseSignProcess(iSignProcessID); end else WriteLn('Input PDF not found') finally pdf.Free; end; end;Все вызываемые в этой процедуре методы являются функциями, но их результат можно игнорировать. Например, результат вызова метода EndSignProcessToFile всегда будет нулевым. Чтобы проверить результат выполнения процесса подписи необходимо воспользоваться методом GetSignProcessResult. В свойствах подписи, добавленной в документ вторым способом, мы видим, что именованное поле для подписи "Test signature field" видимое и находится на первой странице, а после надписи "Подпись:" находится изображение, загруженное методом SetSignProcessFieldImageFromFile. Если кликнуть мышкой по изображению подписи можно посмотреть ее свойства (как на первом скриншоте).
Загрузку сертификата из файла формата PKCS#12 используя метод SetSignProcessPFXFromFile можно заменить на выбор сертификата из хранилища сертификатов Windows по его SHA1-хешу:
... // pdf.SetSignProcessPFXFromFile(iSignProcessID, sPfxFileName, sPfxPassword); // Выбираем сертификат из хранилища сертификатов Windows pdf.SetSignProcessCertFromStore(iSignProcessID, '3E8EF1F4E64543F308D9DB13B2C01E9CE54F8E3F', 0); // Задаем информацию для подписи pdf.SetSignProcessInfo(iSignProcessID, ...Я рассмотрел базовый набор функций библиотеки Foxit Quick PDF Library для работы с цифровой подписью в PDF документе и общий подход к работе с ними. С остальными функциями можно ознакомится в документации к библиотеке или ее исходных кодах.
Комментариев нет:
Отправить комментарий