繁体   English   中英

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

[英]How to show digital PDF signature in all document's Page using iText?

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

试图给出一个快速的简历,以显示邮票我所做的是创建 PdfStamper、PdfSignatureAppearance 和一个矩形,然后调用

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

上面的第二个参数“1”是我想显示图章的页码,现在可以设为 1,因为我试图在其他页面中显示图章正在创建 PdfStamper、PdfSignatureAppearance 和一个矩形,但将其设置为第 2 页。如果它有效,我会将它放在一个循环中并不断更改页面参数。

但是为什么没有效果??? 好吧,接近尾声时,我调用了 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 想到的选项。 特别是这是一个更好的选择,至少乍一看:

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

但是,有许多缺点:

  • 同一签名的多个可视化可能对该签名的合法价值产生负面影响。

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

    签名在文档中的位置可能会影响其法律含义。 出于这个原因,签名字段永远不会引用多个注释。 如果不止一个位置与签名相关联,则含义可能变得不明确。

    ( Adob​​e Acrobat 版本 9 的数字签名外观白皮书,日期为 2008 年 5 月 5 日)

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

    如果电子签名在签名文件中具有可视化效果,这种管辖权可能类似(或者至少必须努力解释差异)。 在同一签名的多个可视化的情况下,这可能意味着只有直到第一个可视化的所有内容都被视为已签名。

    (我不是律师,所以请不要考虑这个法律咨询。)

  • 由于此类潜在的法律问题,即将推出的 PDF 2.0 标准中的签名字段将只允许有一个小部件。 因此,根据该标准,具有多个小部件的签名可能会被视为无效。

  • 现在 Adob​​e Reader 的签名面板已经包含“签名所在的页面”,参见。 此屏幕截图的最后一行:

    样本面板条目

    该面板上没有关联条目(具有正确页码)的活动签名字段可能会被彻底怀疑。

单个签名,最后一页上的单个可视化,其他页面上的非活动图像

在使用此选项的情况下,先前选项的缺点不适用或至少仅在较小程度上适用。 尤其是,如果单纯的图像通过指示它们是副本的提示与可视化略有不同,则最终的、真实的可视化可能会被视为绑定签名位置。

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

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

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

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

不过,它也有自己的缺点:

  • 在验证过程中,所有这些签名都将被验证。 这可能意味着高资源需求,甚至更糟糕的模糊结果(如果某些验证失败而某些验证成功)。
  • Adobe Reader 签名面板中充斥着条目。
  • 私钥被多次使用,这在按签名付费的情况下签名服务很昂贵,并且在 SSCD(特别是智能卡或令牌)的情况下可能需要多次输入 PIN 并且也需要相当长的时间

实施选项

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

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


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

@mkl 关于法律问题的观点非常重要。 但是,如果您仍然想使用 Itext api(版本 5.5.*)对所有页面进行签名,则您应该在preClose(HashMap<PdfName, Integer> exclusionSizes) PdfSignatureAppearance preClose(HashMap<PdfName, Integer> exclusionSizes)方法中进行一些PdfSignatureAppearance ,其中包含签名外观页面。

搜索writer.addAnnotation(sigField, pagen); PdfSignatureAppearance类中的行并替换为

for (int p = 1; p <= writer.reader.getNumberOfPages(); p++) {
   writer.addAnnotation(sigField, p);
}

它将签名的引用添加到所有页面。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM