Статьи

Добавление видимых электронных подписей в PDF-файлы

Я знаю, что это будет очень нишевая тема. Электронная подпись PDF-файлов далека от основного использования. Тем не менее, я напишу это по двум причинам: во-первых, я думаю, что это будет очень полезно для тех немногих, кому это действительно нужно, и, во-вторых, я думаю, что оно будет становиться все более и более распространенным по мере того, как правила eIDAS приобретают популярность, — в основном это говорит что электронные подписи признаются повсюду в Европе (сейчас это не совсем так , из-за некоторых скучных юридических деталей, но в любом случае).

Итак, каков вариант использования — во-первых, вы должны подписать PDF-файл электронной подписью в электронном виде (юридический термин — «электронная подпись», поэтому я буду использовать их взаимозаменяемо, хотя они не полностью совпадают — например, любые электронные данные, примененные к другим данным, могут рассматриваться как электронная подпись, где цифровая подпись является подписью на основе PKI).

Во-вторых, вы можете захотеть отобразить подпись на страницах, вместо того, чтобы программа чтения PDF распознала ее и показала на боковой панели. Почему это? Потому что люди привыкли видеть подписи на страницах, а некоторые могут настаивать на том, чтобы подпись была видимой (правдивая история — у меня есть комментарий, что отдельная подпись «не является НАСТОЯЩЕЙ электронной подписью, потому что ее не видно на странице»).

Теперь обратите внимание, что я написал «страницы» на «странице». Да, у электронного документа нет страниц — это поток байтов. Так что подпись только на последней странице — это нормально. Но, опять же, люди привыкли подписывать все страницы, поэтому они предпочли бы, чтобы электронная подпись была видна на всех страницах.

И это усложняет задачу: PDF удобен тем, что на последней странице есть блок цифровой подписи, но наличие нескольких таких блоков не работает. Поэтому необходимо добавить другие типы аннотаций, которые выглядят как поле для подписи и при щелчке открывают панель для подписи (точно так же, как и поле для подписи).

Я должен представить здесь DSS — замечательный набор компонентов Европейской комиссии, который можно использовать для подписи и проверки всех видов электронных подписей. Это открытый исходный код, вы можете использовать его любым удобным для вас способом. Разверните демонстрационное приложение , используйте только библиотеки, что угодно. Он включает функцию подписи из коробки — просто проверьте PAdESService или PDFBoxSignatureService . Он даже включает возможность визуализировать подпись один раз (на определенной странице).

Тем не менее, он не имеет возможности показывать «штампы» (изображения) на нескольких страницах. Вот почему я его разветвил и реализовал функциональность. Большинство моих изменений в PDFBoxSignatureService в loadAndStampDocument(..) . Если вы хотите использовать эту функциональность, вы можете просто собрать jar из моего форка и использовать его (передав соответствующие SignatureImageParameters в PAdESSErvice.sign(..) чтобы определить, как будет выглядеть подпись).

Зачем это нужно в первую очередь? Потому что, когда документ подписан, вы больше не можете его изменить, так как вы измените хеш. Тем не менее, PDF-файлы имеют постепенные обновления, которые позволяют добавлять к документу и, следовательно, иметь более новую версию, не изменяя ничего в исходной версии. Таким образом, подпись остается действительной (изначально подписанное содержимое не изменяется), но добавляется новый материал. В нашем случае этот новый материал представляет собой некоторые «аннотации», которые представляют изображение и интерактивную область, открывающую панель подписи (по крайней мере, в Adobe Reader). И хотя они добавляются до того, как будет добавлено поле для подписи, если имеется несколько подписывающих, аннотации для второго подписывающего добавляются после первой подписи.

К сожалению, PDFBox не поддерживает это из коробки. Что ж, почти так и есть — фрагмент кода, показанный ниже, выглядит хакерским, и потребовалось некоторое время, чтобы понять, что именно следует вызывать и когда, но он работает только с одним вызовом отражения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
for (PDPage page : pdDocument.getPages()) {
        // reset existing annotations (needed in order to have the stamps added)
        page.setAnnotations(null);
    }
    // reset document outline (needed in order to have the stamps added)
    pdDocument.getDocumentCatalog().setDocumentOutline(null);
    List<PDAnnotation> annotations = addStamps(pdDocument, parameters);
             
    setDocumentId(parameters, pdDocument);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (COSWriter writer = new COSWriter(baos, new RandomAccessBuffer(pdfBytes))) {
        // force-add the annotations (wouldn't be saved in incremental updates otherwise)
        annotations.forEach(ann -> addObjectToWrite(writer, ann.getCOSObject()));
                 
        // technically the same as saveIncremental but with more control
        writer.write(pdDocument);
    }
    pdDocument.close();
    pdDocument = PDDocument.load(baos.toByteArray());
    ...
}
 
private void addObjectToWrite(COSWriter writer, COSDictionary cosObject) {
    // the COSWriter does not expose the addObjectToWrite method, so we need reflection to add the annotations
    try {
        Method method = writer.getClass().getDeclaredMethod("addObjectToWrite", COSBase.class);
        method.setAccessible(true);
        method.invoke(writer, cosObject);
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

Что он делает — загружает оригинальный PDF, очищает некоторые внутренние каталоги, добавляет аннотации (изображения) на все страницы, а затем «принудительно добавляет аннотации», потому что они «не будут сохранены в инкрементных обновлениях в противном случае». Я надеюсь, что PDFBox сделает это немного проще, но пока это работает, и это не делает недействительными существующие подписи.

Я надеюсь, что этот пост знакомит вас с:

  • наличие юридически обязательных электронных подписей
  • существование утилит DSS
  • стандарт PAdES для подписи PDF
  • как разместить более одного поля для подписи в документе PDF

И я надеюсь, что эта статья со временем будет становиться все более популярной, так как все больше и больше компаний осознают, что могут использовать электронные подписи.

Опубликовано на Java Code Geeks с разрешения Божидара Божанова, партнера нашей программы JCG . См. Оригинальную статью здесь: Добавление видимых электронных подписей в PDF-файлы.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.