#itext #digital-signature
#itext #цифровая подпись
Вопрос:
Я не понимаю, почему входной поток, возвращаемый getRangeStream(), отличается на каждой итерации кода в одном и том же файле. Обычно он должен возвращать «байты документа, которые можно хэшировать при использовании внешних подписей», что для меня означает, что для того же входного файла должен быть возвращен тот же массив байтов. Но это не так…
PdfReader pdfReader = new PdfReader(new FileInputStream(inPdfFile), null);
AcroFields acroFields = pdfReader.getAcroFields();
boolean hasSignature = acroFields.getSignatureNames().size() > 0;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, byteArrayOutputStream, '', null, hasSignature);
pdfStamper.setXmpMetadata(pdfReader.getMetadata());
PdfSignatureAppearance pdfSignatureAppearance = pdfStamper.getSignatureAppearance();
PdfSignature pdfSignature = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
pdfSignature.setReason("reason");
pdfSignature.setLocation("location");
pdfSignature.setContact("contact");
pdfSignatureAppearance.setCryptoDictionary(pdfSignature);
// certify the pdf, if requested
/* if (certificationLevel > 0) {
// check: at most one certification per pdf is allowed
if (pdfReader.getCertificationLevel() != PdfSignatureAppearance.NOT_CERTIFIED)
throw new Exception("Could not apply -certlevel option. At most one certification per pdf is allowed, but source pdf contained already a certification.");
pdfSignatureAppearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
}*/
pdfSignatureAppearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, new Integer(30000 * 2 2));
pdfSignatureAppearance.preClose(exc);
InputStream rangeStream = pdfSignatureAppearance.getRangeStream();
Ответ №1:
Встроенная подпись pdf не просто добавляется как есть к существующему PDF-файлу, но является значением поля формы подписи (вместе с некоторой метаинформацией). И совместимая такая подпись должна подписывать все, кроме зарезервированного для нее места в значении поля подписи.
Следовательно, всякий раз, когда вы пытаетесь подписать существующий PDF-файл, его обычно нужно подготовить: если в PDF-файле еще нет структуры формы, структура формы должна быть добавлена. Если пока нет пустого поля формы подписи, такое поле формы должно быть добавлено. И значение подписи должно быть подготовлено, в частности, с указанием времени подписи.
Всякий раз, когда вы создаете или обновляете PDF-файл, генерируется идентификатор, определяется время создания или модификации, и эти данные добавляются в созданный / обновленный PDF-файл.
Таким образом, ваш код при каждом запуске манипулирует исходным PDF-файлом, поэтому результирующие PDF-файлы имеют разные идентификаторы, время модификации и время подписи. И поскольку подпись подписывает все, кроме своего заполнителя, она также подписывает эти переменные данные.
Следовательно, хэш отличается при каждом запуске.
Для получения дополнительной информации прочитайте этот ответ и статьи, на которые ссылаются оттуда.
Комментарии:
1. Хорошо, спасибо за ваш ответ. Итак, я понимаю, что метаданные и поля формы для подписи включены в данные, которые возвращаются при вызове PdfSignatureAppearance .getRangeStream() . Если я сохраню документ в этом состоянии (подготовленный PDF для получения отдельной подписи, но без интеграции реальной отдельной подписи), как я могу получить хэш, предоставленный getRangeStream (), если я повторно открою эту сохраненную версию pdf.
2. @jokon «как я могу получить хэш, предоставленный getRangeStream (), если я повторно открою эту сохраненную версию pdf» — Смотрите Пример руководства по подписи iText C4_09_DeferredSigning , метод
emptySignature
«подписывает» без ввода контейнера подписи, оставляя заполнитель пустым, иcreateSignature
впоследствии работает с выходным PDF-файлом этого первого метода, извлекает и хэширует поток диапазона, генерирует подпись и вводит его.