简体   繁体   English

pdf 文件中的多重签名使用 IText

[英]Multiple Signings in pdf File using IText

I want to sign a pdf with multiple signatures but i can only sign the pdf with one.我想用多个签名签署 pdf 但我只能用一个签署 pdf。 Im using the Itext Library.我正在使用 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");
    }

I already tried using the append mode as true and removing the image signing but it only shows as one signature.我已经尝试使用 append 模式作为 true 并删除图像签名,但它只显示为一个签名。 image showing pdf signed显示 pdf 签名的图像

There are a number of errors in your approach你的方法有很多错误

Your main method你的main方法

The Original main Code原始main代码

In your original code your main method contained these two calls:在您的原始代码中,您的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");

In particular in your first line you created a FileInputStream and a FileOutputStream for the same file name.特别是在您的第一行中,您为相同的文件名创建了FileInputStreamFileOutputStream Doing the latter truncates the file, so when the code in your sign method attempts to read the file from the former stream, it doesn't find anything and throws an exception.执行后者会截断文件,因此当您的sign方法中的代码尝试从前 stream 读取文件时,它找不到任何内容并引发异常。

Thus, you must not use the same file as both input and output of your signing procedure.因此,您不能使用相同的文件作为签名过程的输入和 output。

The Edited main Code编辑后的main代码

You then edited your main method to contain these two calls:然后,您编辑了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");

Now both calls read the unsigned PDF, sign it, and write the result to the same output file.现在两个调用都读取未签名的 PDF,对其进行签名,并将结果写入同一个 output 文件。 Thus, the first call creates a PDF with a single signature and the second call also creates a PDF with a single signature and its output overwrites the output of the first call. Thus, the first call creates a PDF with a single signature and the second call also creates a PDF with a single signature and its output overwrites the output of the first call.

Thus, you must use the output of the first signing call as input of the second signing call.因此,您必须使用第一个签名调用的 output 作为第二个签名调用的输入。

For example like this:例如像这样:

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");

Your sign Method你的sign方法

Having made sure that no signing call uses the same file both as input and output and that the second signing call uses the output of the first call as input, we now run into the issues of your sign method used multiple times.确保没有签名调用使用相同的文件作为输入和 output 并且第二个签名调用使用第一个调用的 output 作为输入,我们现在遇到了多次使用您的sign方法的问题。 (For one-time use it's ok.) (一次性使用没问题。)

Signature Field Names签名字段名称

In your sign method you have hard-coded the signature field name:在您的sign方法中,您对签名字段名称进行了硬编码:

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

Ie each call tries to sign the same signature field.即每个呼叫都试图签署相同的签名字段。 According to the JavaDocs of the setVisibleSignature method you use, though:但是,根据您使用的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)

So this method tries to create a new visible signature field.所以这个方法试图创建一个的可见签名字段。 As each field has a distinct name, using the same name twice is an error.由于每个字段都有不同的名称,因此两次使用相同的名称是错误的。

Thus, you must make sure that you use different signature field names.因此,您必须确保使用不同的签名字段名称。 A simple option for that is described in the JavaDoc description of the fieldName parameter: If you use null , iText automatically creates a new signature name.fieldName参数的 JavaDoc 描述中描述了一个简单的选项:如果您使用null , iText 会自动创建一个新的签名名称。 So, simply replace "sig" by null in your code line above.因此,只需在上面的代码行中将"sig"替换为null

Certification Signatures认证签名

In your sign method you set the certification level like this:在您的sign方法中,您可以像这样设置认证级别:

appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);

This causes two issues:这会导致两个问题:

  1. CERTIFIED_NO_CHANGES_ALLOWED means what it says: No changes allowed. CERTIFIED_NO_CHANGES_ALLOWED的意思是:不允许更改。 Signing a second time is a change.第二次签约一种改变。 So using this certification level forbids signing again.因此,使用此认证级别禁止再次签名。 For details on allowed and disallowed changes to a signed PDF read this answer .有关已签名 PDF 的允许和不允许更改的详细信息,请阅读此答案

    Thus, you must use a certification level which does not forbid creating the second signature.因此,您必须使用不禁止创建第二个签名的认证级别。

  2. A document may only contain a single certification signature and any number of approval signatures (regular signatures without a certification level).一个文件可能只包含一个认证签名和任意数量的批准签名(没有认证级别的常规签名)。

    Thus, you must make sure you set the signature appearance certification level only for your first signing call for the document in question.因此,您必须确保仅为您对相关文档的第一次签名调用设置签名外观认证级别。

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

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