java - 如何使用iText在所有文档页面中显示数字PDF签名?

标签 java itext signature

我从事数字签名功能已经有好几天了,现在一切正常,是时候尝试在所有页面上打印图章了,但是我做得并不好。

尝试快速恢复简历,以显示图章的操作,然后创建PdfStamper,PdfSignatureAppearance和Rectangle,然后调用

 appearance.setVisibleSignature(rectangle, 1, "SIGNATURE")

上面的第二个参数“1”是我要显示图章的页码,现在可以设为1,因为我试图在其他页面中显示图章的目的是创建PdfStamper,PdfSignatureAppearance和一个Rectangle,但将其设置为page2。如果可以,我将其置于循环中并继续更改page参数。

但是为什么它不起作用???好了,快要结束了,我调用了MakeSignature的方法,并且在参数中我必须传递我创建的外观之一,如果我多次调用它,则签名仅出现在与我传递给它的最后一个外观相关的页面上。

例如:
    MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
    MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);

该图章仅显示在首页上。

也许我可以在这里得到一些帮助???

整个过程都在这里进行:
public String signPdfFirstTime(String src, String dest, PrivateKey pk, Certificate[] chain, String providerName, String conteudoBase64, X509Certificate cert, String alias) throws IOException, DocumentException, GeneralSecurityException
{
    byte[] conteudoBinario = Base64.decode(conteudoBase64);

    FileOutputStream fos = new FileOutputStream(path + File.separator + src);
    fos.write(conteudoBinario);
    fos.close();

    File f = new File(path + File.separator + src);
    FileInputStream in = new FileInputStream(f);
    PdfReader reader = new PdfReader(in);

    int qtypages = reader.getNumberOfPages(); 
    FileOutputStream os = new FileOutputStream(path + File.separator + dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    Rectangle rectangle = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance.setVisibleSignature(rectangle, 1, "SIGNATURE");

    //Here I build a custom message...nothing relevant
    StringBuilder stampMessage = new StringBuilder();
    stampMessage.append("...");
    stampMessage.append(alias);
    stampMessage.append(" - ");
        // customize appearance layer 2 to display text vertically
    PdfTemplate layer2 = appearance.getLayer(2);
    layer2.transform(new AffineTransform(0, 1, -1, 0, rectangle.getWidth(), 0));
    Font font = new Font();
    font.setColor(BaseColor.BLACK);
    ColumnText ct2 = new ColumnText(layer2);
    ct2.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct2.setSimpleColumn(new Phrase(stampMessage.toString(), font), 0, 0, rectangle.getHeight(), rectangle.getWidth(), 15, Element.ALIGN_LEFT);
    ct2.go();
    appearance.setCertificate(cert);

    //Here starts where I tried to make a second stamp to show in the page 2
    FileOutputStream fos2 = new FileOutputStream(path + File.separator + src);
    fos2.write(conteudoBinario);
    fos2.close();
    File f2 = new File(path + File.separator + src);
    FileInputStream in2 = new FileInputStream(f2);

    PdfReader reader2 = new PdfReader(in2);
    FileOutputStream os2 = new FileOutputStream(path + File.separator + dest);

    PdfStamper stamper2 = PdfStamper.createSignature(reader2, os2, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance2 = stamper2.getSignatureAppearance();

    Rectangle rectangle2 = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance2.setVisibleSignature(rectangle2, 3, "ASSINATURA2");

    //Cria a msg que aparece na estampa
    StringBuilder stampMessage2 = new StringBuilder();
    stampMessage2.append(" - ");

    PdfTemplate layer22 = appearance.getLayer(2);
    layer22.transform(new AffineTransform(0, 1, -1, 0, rectangle2.getWidth(), 0));
    Font font2 = new Font();
    font2.setColor(BaseColor.BLACK);
    ColumnText ct22 = new ColumnText(layer22);
    ct22.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct22.setSimpleColumn(new Phrase(stampMessage2.toString(), font2), 0, 0, rectangle2.getHeight(), rectangle2.getWidth(), 15, Element.ALIGN_LEFT);
    ct22.go();
    appearance2.setCertificate(cert);

    // Creating the signature
    ExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, providerName);
    ExternalDigest digest = new BouncyCastleDigest();
    List<CrlClient> crlList = new ArrayList<CrlClient>();
    crlList.add(new CrlClientOnline());

    LtvVerification v = stamper.getLtvVerification();
    LtvVerification v2 = stamper2.getLtvVerification();

    OcspClient ocspClient = new OcspClientBouncyCastle();

    String url = CertificateUtil.getCRLURL(cert);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    X509CRL crl = (X509CRL) cf.generateCRL(new URL(url).openStream());
    System.out.println("CRL valid until: " + crl.getNextUpdate());
    System.out.println("Certificate revoked: " + crl.isRevoked(chain[0]));

    if (crl.isRevoked(chain[0])) {

        throw new GeneralSecurityException("CERTIFICADO REVOGADO!");
    }
    else {
        MakeSignature.processCrl(cert, crlList);

        MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        os.close();
        byte[] b = this.read(f);
        return Base64.encodeBytes(b);
    }
}

最佳答案

这实际上是对可用选项的讨论。

从根本上来说,在所有页面上打印签名印章的方式有所不同:

  • 创建具有多个可视化效果的单个签名字段,每页上一个。
  • 在单个页面上创建具有单个可视化效果的单个签名字段,例如最后一个文档页面,并在其他所有页面上创建具有相同内容的图像。
  • 每页创建一个签名字段,每个签名页在页面上都有一个可视化效果。

  • PDF签名字段的“可视化”是与该字段直接关联的小部件;特别是可以单击它们以打开签名验证对话框。与这些小部件相反,第二个选项中的“图像”只是没有这种相关动作的图像。

    单签名,多可视化

    这很可能是OP想到的选择。特别是,这至少是乍一看是更可取的选择:

    在每个页面上
  • ,签名图像处于 Activity 状态,并允许打开签名验证对话框;
  • ,但仍然只需要创建一个数字签名,这意味着
  • 在Adobe Reader的左侧签名面板上只有一个条目
  • 仅需一个签名容器进行验证(因此,没有明确的验证结果),而
  • 仅使用一次私钥,因此不需要多次输入PIN,并且在按签名付费的签名服务的情况下,只需支付一次签名事件。

  • 但是,有许多缺点:
  • 同一签名的多个可视化效果可能对该签名的法律价值产生负面影响。

    因此,Adobe几年前决定不要在其软件中创建具有多个可视化效果的签名字段,请参见。例如

    文件中签名的位置可能会影响其法律含义。为此原因,
    签名字段永远不会引用多个注解。如果一个地点与一个地点相关联
    签名,含义可能会变得模棱两可。

    (适用于Adobe Acrobat版本9的Digital Signature Appearances白皮书,日期为2008年5月5日)

    例如。在德国,关于书面签名的管辖权在垂直范围内限制了签名人合法签名的文件部分,通常不受签名下方任何书面内容的法律约束。其他法律体系中也可能存在类似的管辖权。

    对于在签名文档中具有可视化效果的电子签名,这种管辖权可能具有类似的管辖权(或者至少必须付出相当大的努力才能解释这些差异)。如果对同一签名进行多次可视化,这可能意味着只有第一次可视化之前的所有内容都被视为已签名。

    (我不是律师,所以请不要考虑此法律咨询。)
  • 由于此类潜在的法律问题,即将发布的PDF 2.0标准中的签名字段仅允许具有单个小部件。因此,根据该标准,具有多个窗口小部件的签名可能会被视为无效。
  • 现在,Adobe Reader的签名面板包含“签名所在的页面”,参见。该屏幕截图的最后一行:

    sample panel entry

    在该面板上没有相关条目(具有正确的页码)的 Activity 签名字段可能会被完全怀疑。

  • 单一签名,最后一页的单一可视化效果,其他页面上的无效图像

    在使用此选件的情况下,先前选件的缺点不适用或至少仅在较小程度上适用。特别是,如果仅图像与可视化的区别在于提示它们是副本的提示,则最终的真实可视化可能会被视为绑定签名位置。

    但是,此选项的主要缺点是,对于已签名的文档,不允许仅向内容添加图像。因此,此选项不能用于文档的第二或第三签名者,但是OP表示解决方案最终将不得不允许文档被多个人签名。

    可以考虑将这些图像添加为注释,而不是内容。对于某些类型的集成PDF签名,允许在签名后添加和删除注释。但是,如果允许添加这些注释,通常也允许在签名后再次将其删除,从而使这些签名图像非常不稳定。

    多个签名(每页一个),每个签名一个可视化

    此选项没有其他选项的缺点,因为每个可视化都对应于不同的数字签名。因此,最后一个保证签名人受整个文件的法律约束。

    但是,它确实有缺点:
  • 在验证过程中,将对所有这些签名进行验证。这可能意味着对资源的高要求,甚至导致更模糊的结果(如果某些验证失败而某些成功)。
  • Adob​​e Reader签名面板中充满了条目。
  • 私钥被多次使用,这在按签名付费的签名服务昂贵的情况下,并且在SSCD(尤其是智能卡或 token )可能需要多次输入PIN的情况下,也需要相当长的时间

  • 实施选项

    iText允许以开箱即用的方式直接实现第二个和第三个选项。

    使用iText可以实现第一个选项,但需要使用低级API和Java反射,或者对iText进行一些修补。

    考虑到每个选项的问题,我建议完全不要这样做,在内容末尾签名是最不明确的签名方式。

    关于java - 如何使用iText在所有文档页面中显示数字PDF签名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35701597/

    相关文章:

    java - onchange 使用 radioChoice 获取当前值

    java - 表在同一行

    java - spring java本地文件包含漏洞

    java - 在 Java 流中插入值

    java - 使用 Assets 字体和 utf-8 文本的 android itext

    f# - F# 模块签名文件的用例是什么?

    android - 如何获取 APK 签名签名?

    c++ - 功能不同?

    java - Apache POI Workbook 向 OutputStream 写入奇怪的字符

    java - 将 rowspan 与 itext pdf 一起使用时无法显示背景颜色