【问题标题】:Multiple Signings in pdf File using IText使用 IText 在 pdf 文件中进行多重签名
【发布时间】:2020-06-08 21:27:53
【问题描述】:

我想用多个签名签署一个 pdf,但我只能用一个签名。 我正在使用 Itext 库。

public static void sign(InputStream src,OutputStream dest, InputStream p12Stream, char[] password, String reason, String location, String imagePath) throws Exception {
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(p12Stream, password);
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
        Certificate[] chain = ks.getCertificateChain(alias);

        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = PdfStamper.createSignature(reader, dest, '\0', null, true);
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason(reason);
        appearance.setLocation(location);
        appearance.setVisibleSignature(new Rectangle(300, 600, 630, 500), 1, "sig");

        Image image = Image.getInstance(imagePath);
        appearance.setSignatureGraphic(image);
        appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);

        ExternalDigest digest = new BouncyCastleDigest();
        ExternalSignature signature = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, null);
        MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
    }

    public static void main(String[] args) throws Exception {
        sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signed.pdf"), new FileInputStream(basePath+"mycert3.p12"), "mycert3".toCharArray(), "something", "something", basePath + "signing1.png");
        sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signed.pdf"), new FileInputStream(basePath+"mycert4.p12"), "mycert4".toCharArray(), "something", "something", basePath + "signing2.png");
    }

我已经尝试使用附加模式作为 true 并删除图像签名,但它只显示为一个签名。 image showing pdf signed

【问题讨论】:

  • 首先 - 您的第一个 sign 调用使用相同的文件进行输入和输出。这行不通。此外,您想添加两个具有认证级别的签名。但只有第一个签名
  • 我知道,它的 signed.pdf 在输出,但我仍然不能在文件中制作多个签名
  • 更改后,您现在将未签名的 pdf 作为两次签名尝试的输入。对于第二次签名尝试,如果第一次尝试作为输入,您需要将输出作为输入并写入另一个文件。

标签: java pdf itext certificate sign


【解决方案1】:

你的方法有很多错误

您的main 方法

原始main代码

在您的原始代码中,您的 main 方法包含这两个调用:

sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"nonsigned.pdf"), new FileInputStream(basePath+"mycert3.p12"), "mycert3".toCharArray(), "something", "something", basePath + "signing1.png");
sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signed.pdf"), new FileInputStream(basePath+"mycert4.p12"), "mycert4".toCharArray(), "something", "something", basePath + "signing2.png");

特别是在您的第一行中,您为相同的文件名创建了FileInputStreamFileOutputStream。执行后者会截断文件,因此当您的 sign 方法中的代码尝试从前一个流中读取文件时,它找不到任何内容并引发异常。

因此,您不得将同一文件用作签名过程的输入和输出。

编辑的main代码

然后您编辑了您的 main 方法以包含这两个调用:

sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signed.pdf"), new FileInputStream(basePath+"mycert3.p12"), "mycert3".toCharArray(), "something", "something", basePath + "signing1.png");
sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signed.pdf"), new FileInputStream(basePath+"mycert4.p12"), "mycert4".toCharArray(), "something", "something", basePath + "signing2.png");

现在两个调用都读取 unsigned PDF,对其进行签名,然后将结果写入同一个输出文件。因此,第一次调用创建了一个带有单个签名的 PDF,第二个调用也创建了一个带有单个签名的 PDF,并且它的输出覆盖了第一个调用的输出。

因此,您必须使用第一个签名调用的输出作为第二个签名调用的输入。

例如这样:

sign(new FileInputStream(basePath+"nonsigned.pdf"), new FileOutputStream(basePath+"signedOnce.pdf"), new FileInputStream(basePath+"mycert3.p12"), "mycert3".toCharArray(), "something", "something", basePath + "signing1.png");
sign(new FileInputStream(basePath+"signedOnce.pdf"), new FileOutputStream(basePath+"signedTwice.pdf"), new FileInputStream(basePath+"mycert4.p12"), "mycert4".toCharArray(), "something", "something", basePath + "signing2.png");

您的sign 方法

确保没有签名调用使用相同的文件作为输入和输出,并且第二次签名调用使用第一次调用的输出作为输入,我们现在遇到了多次使用您的 sign 方法的问题. (一次性使用没问题。)

签名字段名称

在您的 sign 方法中,您已经硬编码了签名字段名称:

appearance.setVisibleSignature(new Rectangle(300, 600, 630, 500), 1, "sig");

即每个调用都尝试签署 same 签名字段。不过,根据您使用的setVisibleSignature 方法的JavaDocs:

/**
 * Sets the signature to be visible. It creates a new visible signature field.
 * @param pageRect the position and dimension of the field in the page
 * @param page the page to place the field. The fist page is 1
 * @param fieldName the field name or <CODE>null</CODE> to generate automatically a new field name
 */
public void setVisibleSignature(Rectangle pageRect, int page, String fieldName)

所以这个方法尝试创建一个可见的签名字段。由于每个字段都有不同的名称,因此两次使用相同的名称是错误的。

因此,您必须确保使用不同的签名字段名称。fieldName 参数的 JavaDoc 描述中描述了一个简单的选项:如果您使用 null,iText自动创建一个新的签名名称。因此,只需在上面的代码行中将 "sig" 替换为 null

认证签名

在您的 sign 方法中,您可以像这样设置认证级别:

appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);

这会导致两个问题:

  1. CERTIFIED_NO_CHANGES_ALLOWED 的意思是:不允许更改。第二次签名更改。因此,使用此认证级别禁止再次签名。有关允许和不允许更改已签名 PDF 的详细信息,请阅读 this answer

    因此,您必须使用不禁止创建第二个签名的认证级别。

  2. 一个文档只能包含一个认证签名和任意数量的批准签名(没有认证级别的常规签名)。

    因此,您必须确保仅为您对相关文档的第一次签名调用设置签名外观认证级别。

【讨论】:

  • 哦,我明白了,非常感谢。输出和输入的部分是有道理的,我知道我自己怎么没有得到它xd。已经更改了我的代码及其工作。顺便说一句,解释得很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
  • 1970-01-01
相关资源
最近更新 更多