如何在新签名字段中关联先前的签名

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在新签名字段中关联先前的签名相关的知识,希望对你有一定的参考价值。

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

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

但我找不到将其与文档中已有的签名相关联的方法。

我该如何联想呢?

答案

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

首先,如果您的文件经过认证且不允许更改,则显然不允许这样做

但是,令人惊讶的是,似乎允许签名但未经证明的文档(我与之合作的示例文件)。

实际上,除非文件经过认证且不允许更改,否则您始终可以填写表格(除了允许表格填写和数字签名认证的文件),甚至修改注释,参见this answer概述。

由于PDF签名是表单字段值,表单字段的可视化是特殊注释,因此允许更改签名可视化作为表单填写或至少作为注释修改。

在iText 5中完成

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

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

但这并没有帮助,因为必须更改现有的签名字段,而不是创建新的签名字段。

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

PdfReader pdfReader = new PdfReader(resource);
PdfStamper pdfStamper = new PdfStamper(pdfReader, result, '', 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,但您可以创建任何您喜欢的外观。

注意:此代码假定隐形签名已与某些文档页面关联。对于尚未与页面关联的不可见签名,必须建立关联。这可能会变成一个不允许的变化,至少它不仅仅是形式填充,因为表单结构也被更改,而不仅仅是其条目。


OP在评论中表示

但我想检索标志的名称并写下它而不是红十字

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

PdfReader pdfReader = new PdfReader(resource);
PdfStamper pdfStamper = new PdfStamper(pdfReader, result, '', 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必须注册为安全提供者。

而上述警告仍然适用。

在iText 7中完成

由于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())
    {
        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

适用于上述iText 5代码的相同警告:

注意:此代码假定隐形签名已与某些文档页面关联。


主题名称的变体如下所示:

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代码一样注册为安全提供者。

上述警告仍然适用。

在Adobe Acrobat Reader DC中查看它

我在Adobe Acrobat Reader DC中使用无形签名的空白文档BLANK-signed.pdf对此进行了测试:

Screen shot of original blank signed PDF

使用上面的代码操作文件后,我得到:

Screen shot of blank signed PDF with added appearance

有关未签名更改的警告是正

以上是关于如何在新签名字段中关联先前的签名的主要内容,如果未能解决你的问题,请参考以下文章

如何在phpmyadmin中关联同一张表的两个字段?

在PDF上创建签名字段iTextSharp。用户签名后如何自动保存PDF

银联在线支付对接流程以及签名算法

ECSHOP 银联电子支付(ChinaPay)插件 掉用JAVA签名

应用程序在主要活动中关闭onBackPressed

Xcode 10 签名错误:Frameworks/libswiftsimd.dylib:代码对象根本没有签名