Одной из интересных и важных особенностей документов в формате 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 документе и общий подход к работе с ними. С остальными функциями можно ознакомится в документации к библиотеке или ее исходных кодах.


Комментариев нет:
Отправить комментарий