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

Itext 如何在新签名字段中关联以前的签名,itext,sign,Itext,Sign,我有一个签名的PDF。我想在文件上签名。我可以通过以下方式添加新的签名字段: Stamper.addSignature("My Signature", 1, 20f, 10f, 100f, 100f); 但我找不到一种方法将其与文档中已有的签名相关联 如何将其关联?OP希望将文档内签名可视化附加到现有签名 首先,如果您的文档经过认证且不允许更改,那么这显然是不允许的 但是,非常令人惊讶的是,它似乎允许签署但未经认证的文档(我使用的示例文件) 事实上,除非文档已通过认证且不允许任何更改,否则您始

我有一个签名的PDF。我想在文件上签名。我可以通过以下方式添加新的签名字段:

Stamper.addSignature("My Signature", 1, 20f, 10f, 100f, 100f);
但我找不到一种方法将其与文档中已有的签名相关联


如何将其关联?

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

首先,如果您的文档经过认证且不允许更改,那么这显然是不允许的

但是,非常令人惊讶的是,它似乎允许签署但未经认证的文档(我使用的示例文件)

事实上,除非文档已通过认证且不允许任何更改,否则您始终可以填写表单,甚至可以修改注释(除了通过表单填写和数字签名认证的文档),请参见概述

由于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, '\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();
(方法
testchange

这段代码为每个集成的PDF签名创建一个新的签名外观,在本例中是一个位于100、100、大小为100x100的红十字,但是您可以在那里创建任何您喜欢的外观

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


OP在评论中指出

但我想取回标志的名称,并写下它,而不是一个红十字

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

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();
(方法<代码>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());
        }
    }
}
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());
        }
    }
}
(方法
testchange

此代码需要iText 7工件
内核
表单
,以及
符号

与上述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())
    {
        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());
        }
    }
}
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());
        }
    }
}
(方法<代码>testChangeAppearancesWithName)

此代码还使用iText 7工件
布局
。此外,对于示例文档,BouncyCastle必须注册为安全提供者,就像上面的iText 5代码一样

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

在AdobeAcrobatReaderDC中查看它 我在Adobe Acrobat Reader DC中用一个不可见签名的空白文档对此进行了测试:

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

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


签名者名称的变体如下所示:

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());
        }
    }
}
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());
        }
    }
}

附录:签名的多次出现 OP在评论中问道

此方法仅在文档的第一页上盖章。如何在文档的所有页面上盖章

通常,上述方法实际上并不在第一页上盖章,而是在与签名相关联的页面上盖章。由于看不见的签名通常与第一页相关,所以它出现的原因是可以理解的

此外,单个签名字段的多个外观不受普遍支持(虽然ISO 32000-1实际上并未禁止),并且它们将被即将推出的ISO 32000-2禁止。因此,这样做不是最好的办法

但是,如果无法解决此问题,您可以在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);
        }
    }
}
try(PdfReader PdfReader=新的PdfReader(资源);
PdfWriter PdfWriter=新PdfWriter(结果);
PdfDocument PdfDocument=新的PdfDocument(pdfReader、pdfWriter、new StampingProperties().UseAndendMode())
{
SignatureUtil SignatureUtil=新的SignatureUtil(PDF文件);
PdfAcroForm acroForm=PdfAcroForm.getAcroForm(pdfDocument,false);
for(字符串名称:signatureUtil.getSignatureNames())
{
PdfPKCS7 pkcs7=signatureUtil.verifySignature(名称);
X509Certificate signerCert=(X509Certificate)pkcs7.getSigningCertificate();
字符串signerName=CertificateInfo.getSubjectFields(signerCert.getField(“CN”);
PdfFormField=acroForm.getField(名称);
setModified()字段;
矩形=新矩形(100100);
PdfFormXObject form=新的PdfFormXObject(矩形);
Canvas Canvas=新画布(表单、PDF文档);
canvas.add(新段落().setItalic().add(“签名人:”);
canvas.add(新段落().setBold().add(签名名称));
对于(PdfWidgetAnnotation PdfWidgetAnnotation:field.getWidgets())
{
PdfDictionary pageObject=pdfWidgetAnnotation.getPageObject();
PdfPage page=pdfDocument.getPage(pageObject);
页面移除注释(pdfWidgetAnnotation);
pdfWidgetAnnotation.releaseFormFieldFromWidgetAnnotation();
}
用于(int pageN)