如何在新的签名字段中关联先前的签名 [英] How to associate a previous signature in a new signature field

查看:138
本文介绍了如何在新的签名字段中关联先前的签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个签名的PDF.我想在文档中显示此签名.我可以这样添加一个新的签名字段:

I have a signed PDF. I would like to show this signature in the document. I can add a new signature field, this way:

Stamper.addSignature("My Signature", 1, 20f, 10f, 100f, 100f);

但是我找不到将其与文档中已经存在的签名相关联的方法.

But I can't find a way to associate it with the signature that is already in the document.

我该如何关联它?

推荐答案

OP希望将文档内签名可视化附加到现有签名.

The OP wants to attach an in-document signature visualization to an existing signature.

首先,如果您的文档已通过认证且不允许更改

First of all, this obviously is not allowed if your documents has been certified with no changes allowed

但是,令人惊讶的是,似乎签名但未经认证的文件(我使用的示例文件)确实允许使用该文件.

Quite surprisingly, though, it does seem to be allowed for signed but uncertified documents (which the sample file is which I worked with).

实际上,除非文档已通过认证且不允许更改 ,否则始终允许您填写表单,并且(除了带有表单填充的证明文件除外) -in和数字签名),甚至可以修改注释. 此答案以获取概述.

Indeed, unless a document has been certified with no changes allowed, you always are allowed to fill in forms and (except for documents certified with form fill-in and digital signatures allowed) even to modify annotations, cf. this answer for an overview.

由于PDF签名是表单域值,而表单域的可视化是特殊的注释,允许更改签名可视化作为表单填充或至少作为注释修改.

As PDF signatures are form field values and visualizations of form fields are special annotations, changing signature visualizations is allowed as form fill-in or at least as annotation modification.

OP尝试通过添加新的签名字段来实现此目的:

The OP tried to implement this by adding a new signature field:

Stamper.addSignature("My Signature", 1, 20f, 10f, 100f, 100f);

但是,这无济于事,因为必须更改现有签名字段,而不是创建新的签名字段.

This does not help, though, because one has to change the existing signature field, not create a new one.

使用iText 5.x,可以使用通用的表单字段操作API:

Using iText 5.x this can be done using the generic form field manipulation API:

PdfReader pdfReader = new PdfReader(resource);
PdfStamper pdfStamper = new PdfStamper(pdfReader, result, '\0', true);

AcroFields acroFields = pdfStamper.getAcroFields();
for (String signatureName : acroFields.getSignatureNames())
{
    Item field = acroFields.getFieldItem(signatureName);
    field.writeToAll(PdfName.RECT, new PdfArray(new int[]{100,100,200,200}), Item.WRITE_WIDGET);
    field.markUsed(acroFields, Item.WRITE_WIDGET);

    PdfAppearance appearance = PdfAppearance.createAppearance(pdfStamper.getWriter(), 100, 100);
    appearance.setColorStroke(BaseColor.RED);
    appearance.moveTo(0, 0);
    appearance.lineTo(99, 99);
    appearance.moveTo(0, 99);
    appearance.lineTo(99, 0);
    appearance.stroke();

    PdfDictionary appDict = new PdfDictionary();
    appDict.put(PdfName.N, appearance.getIndirectReference());
    field.writeToAll(PdfName.AP, appDict, Item.WRITE_WIDGET);
}

pdfStamper.close();

( ChangeSignatureAppearance.java 方法testChangeAppearances)

此代码为每个集成的PDF签名创建新的签名外观,在本例中为100、100和100x100大小的红叉,但是您可以在其中创建任何喜欢的外观.

This code creates a new signature appearance for each integrated PDF signature, in this case a red cross located at 100, 100 and sized 100x100, but you can create any appearance you like there.

当心::此代码假定不可见签名已经与某个文档页面相关联.对于尚未与页面关联的不可见签名,必须建立关联.可能这是不允许的更改,至少它不再仅仅是表单填写了,因为表单结构也发生了变化,不仅是其条目也已更改.

Beware: this code assumes the invisible signature to be associated with some document page already. For invisible signatures which are not associated with a page yet, an association will have to be established. This probably might turn out to be a change which is not allowed, at least it is no mere form fill-in anymore because also the form structure is changed, not only its entries.

评论中显示的操作

但是我想检索标志的名称并写上它而不是红叉

But i would like retrieve the name of the sign and write it instead of a red cross

为此,您只需要稍微更改上面的代码即可:

For this your merely need to slightly change the above code:

PdfReader pdfReader = new PdfReader(resource);
PdfStamper pdfStamper = new PdfStamper(pdfReader, result, '\0', true);

AcroFields acroFields = pdfStamper.getAcroFields();
for (String signatureName : acroFields.getSignatureNames())
{
    PdfPKCS7 pkcs7 = acroFields.verifySignature(signatureName);
    X509Certificate signerCert = (X509Certificate) pkcs7.getSigningCertificate();
    String signerName = CertificateInfo.getSubjectFields(signerCert).getField("CN");

    Item field = acroFields.getFieldItem(signatureName);
    field.writeToAll(PdfName.RECT, new PdfArray(new int[]{100,100,200,200}), Item.WRITE_WIDGET);
    field.markUsed(acroFields, Item.WRITE_WIDGET);

    PdfAppearance appearance = PdfAppearance.createAppearance(pdfStamper.getWriter(), 100, 100);
    ColumnText columnText = new ColumnText(appearance);
    Chunk chunk = new Chunk();
    chunk.setSkew(0, 12);
    chunk.append("Signed by:");
    columnText.addElement(new Paragraph(chunk));
    chunk = new Chunk();
    chunk.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, BaseColor.BLACK);
    chunk.append(signerName);
    columnText.addElement(new Paragraph(chunk));
    columnText.setSimpleColumn(0, 0, 100, 100);
    columnText.go();

    PdfDictionary appDict = new PdfDictionary();
    appDict.put(PdfName.N, appearance.getIndirectReference());
    field.writeToAll(PdfName.AP, appDict, Item.WRITE_WIDGET);
}

pdfStamper.close();

( ChangeSignatureAppearance.java 方法testChangeAppearancesWithName)

对于样本文档,BouncyCastle必须注册为安全提供者.

In case of the sample document BouncyCastle has to be registered as security provider.

上面的警告仍然适用.

由于iText 7最近已经发布,因此上面的代码可以像这样移植到它:

As iText 7 recently has been released, the code above can be ported to it like this:

try (   PdfReader pdfReader = new PdfReader(resource);
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter, new StampingProperties().useAppendMode()))
{
    SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
    PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, false);

    for (String name : signatureUtil.getSignatureNames())
    {
        PdfFormField field = acroForm.getField(name);
        field.setModified();
        for (PdfWidgetAnnotation pdfWidgetAnnotation : field.getWidgets())
        {
            pdfWidgetAnnotation.setRectangle(new PdfArray(new int[]{100, 100, 200, 200}));

            PdfFormXObject form = new PdfFormXObject(new Rectangle(100, 100));
            PdfCanvas canvas = new PdfCanvas(form, pdfDocument);
            canvas.setStrokeColor(Color.RED);
            canvas.moveTo(0, 0);
            canvas.lineTo(99, 99);
            canvas.moveTo(0, 99);
            canvas.lineTo(99, 0);
            canvas.stroke();

            pdfWidgetAnnotation.setNormalAppearance(form.getPdfObject());
        }
    }
}

( ChangeSignatureAppearance.java 方法testChangeAppearances)

此代码需要iText 7工件kernelformssign.

This code requires the iText 7 artifacts kernel, forms, and sign.

与上面的iText 5代码相同的警告适用:

The same warning as in case of the iText 5 code above applies:

当心::此代码假定不可见签名已经与某个文档页面相关联.

Beware: this code assumes the invisible signature to be associated with some document page already.

带有主题名称的变体如下:

The variant with the subject's name looks like this:

try (   PdfReader pdfReader = new PdfReader(resource);
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter, new StampingProperties().useAppendMode()))
{
    SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
    PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, false);

    for (String name : signatureUtil.getSignatureNames())
    {
        PdfPKCS7 pkcs7 = signatureUtil.verifySignature(name);
        X509Certificate signerCert = (X509Certificate) pkcs7.getSigningCertificate();
        String signerName = CertificateInfo.getSubjectFields(signerCert).getField("CN");
        PdfFormField field = acroForm.getField(name);
        field.setModified();
        for (PdfWidgetAnnotation pdfWidgetAnnotation : field.getWidgets())
        {
            pdfWidgetAnnotation.setRectangle(new PdfArray(new int[]{100, 100, 200, 200}));

            PdfFormXObject form = new PdfFormXObject(new Rectangle(100, 100));
            Canvas canvas = new Canvas(form, pdfDocument);
            canvas.add(new Paragraph().setItalic().add("Signed by:"));
            canvas.add(new Paragraph().setBold().add(signerName));

            pdfWidgetAnnotation.setNormalAppearance(form.getPdfObject());
        }
    }
}

( ChangeSignatureAppearance.java 方法testChangeAppearancesWithName)

此代码还使用iText 7工件layout.此外,对于示例文档,BouncyCastle必须像上面的iText 5代码一样注册为安全提供程序.

This code additionally uses the iText 7 artifact layout. Furthermore, in case of the sample document BouncyCastle has to be registered as security provider just like for the iText 5 code above.

同样,上面的警告仍然适用.

And again the warning from above still applies.

我在Adobe Acrobat Reader DC中使用无形签名的空白文档

I tested this in Adobe Acrobat Reader DC with an invisibly signed blank document BLANK-signed.pdf:

使用上面的代码操纵了文件,我得到了:

Having manipulated the file using the code above, I got:

有关未签名更改的警告是正确的,但是再次签名后,即使该警告也消失了:

The warning concerning unsigned changes is correct, but after signing again, even that warning vanishes:

具有签名者名称的变体如下:

The variant with signer name looks like this:

OP在评论中问

此方法仅在文档的第一页上盖章.如何在文档的所有页面上加盖印章?

This method only stampt it in the first page of the document. How can I stamp it in all pages in the document ?

通常,上述方法实际上不会在第一页上标记,而是在与签名相关的页面上标记.但是,由于看不见的签名通常与首页相关联,因此为什么它如此出现是可以理解的.

The methods above in general actually don't stamp on the first page but on the page associated with the signature. As invisible signatures often are associated with the first page, though, it is understandable why it appeared so.

此外,不普遍支持单个签名字段的多次出现(尽管ISO 32000-1实际上并未禁止),并且它们将被禁止即将发布的ISO 32000-2.因此,这不是最好的主意.

Furthermore, multiple appearances of a single signature field are not universally supported (while not actually being forbidden by ISO 32000-1), and they will be forbidden by the upcoming ISO 32000-2. Thus, it is not the best idea to go that way.

但是,如果无法解决,您可以在iText 7中尝试类似的方法:

If there is no way around it, though, you can try something like this in iText 7:

try (   PdfReader pdfReader = new PdfReader(resource);
        PdfWriter pdfWriter = new PdfWriter(result);
        PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter, new StampingProperties().useAppendMode()))
{
    SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
    PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, false);

    for (String name : signatureUtil.getSignatureNames())
    {
        PdfPKCS7 pkcs7 = signatureUtil.verifySignature(name);
        X509Certificate signerCert = (X509Certificate) pkcs7.getSigningCertificate();
        String signerName = CertificateInfo.getSubjectFields(signerCert).getField("CN");
        PdfFormField field = acroForm.getField(name);
        field.setModified();

        Rectangle rectangle = new Rectangle(100, 100);
        PdfFormXObject form = new PdfFormXObject(rectangle);
        Canvas canvas = new Canvas(form, pdfDocument);
        canvas.add(new Paragraph().setItalic().add("Signed by:"));
        canvas.add(new Paragraph().setBold().add(signerName));

        for (PdfWidgetAnnotation pdfWidgetAnnotation : field.getWidgets())
        {
            PdfDictionary pageObject = pdfWidgetAnnotation.getPageObject();
            PdfPage page = pdfDocument.getPage(pageObject);
            page.removeAnnotation(pdfWidgetAnnotation);

            pdfWidgetAnnotation.releaseFormFieldFromWidgetAnnotation();
        }

        for (int pageNumber = 1; pageNumber <= pdfDocument.getNumberOfPages(); pageNumber++)
        {
            PdfPage pdfPage = pdfDocument.getPage(pageNumber);
            PdfWidgetAnnotation pdfWidgetAnnotation = new PdfWidgetAnnotation(rectangle);
            pdfWidgetAnnotation.setNormalAppearance(form.getPdfObject());
            pdfWidgetAnnotation.setPage(pdfPage);
            field.addKid(pdfWidgetAnnotation);
            pdfPage.addAnnotation(pdfWidgetAnnotation);
        }
    }
}

( ChangeSignatureAppearance.java 方法testChangeAppearancesWithNameAllPages)

这首先删除签名字段的所有现有注释,然后将新注释添加到所有字段.

This first removes any existing annotations of the signature field and then adds new ones to all fields.

就像上面一样,这将发出有关未签名更改的警告,这毕竟是正确的.

Just like above this will make a warning about unsigned changes appear which is true after all.

顺便说一句,如果一个人在一页上看到带有可视化效果的签名文档,并更改上面的代码而不删除原始注释,则可以轻松地将该注释的副本添加到所有页面,而当前的Adobe Acrobat Reader可以甚至不显示警告!用于检查已签名文档中的更改的Reader代码确实很奇怪...

As an aside, if one takes a signed document with a visualization on one page and changes the code above not to remove the original annotation, one can easily add copies of that annotation to all pages, and the current Adobe Acrobat Reader does not even show a warning! The Reader code for checking for changes in a signed document is weird indeed...

这篇关于如何在新的签名字段中关联先前的签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆