当前位置: 首页 > 知识库问答 >
问题:

使用pdfbox的数字签名

谢海阳
2023-03-14

我尝试使用pdfbox向我的pdf添加多个签名,我尝试使用两个签名,第二个签名总是无效。请给出建议,PDF文件可以进行多次数字签名吗?Adobe livecycle论坛表示同意。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.List;

import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.SignatureException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignedGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class SigningTest implements SignatureInterface {

    private static BouncyCastleProvider provider = new BouncyCastleProvider();

    private PrivateKey privKey;

    private Certificate[] cert;

    public SigningTest(KeyStore keystore, char[] pin) {
        try {
            Enumeration<String> aliases = keystore.aliases();
            String alias = null;
            if (aliases.hasMoreElements())
                alias = aliases.nextElement();
            else
                throw new RuntimeException("Could not find Key");
            privKey = (PrivateKey) keystore.getKey(alias, pin);
            cert = keystore.getCertificateChain(alias);
        } catch (KeyStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public byte[] sign(InputStream content) throws SignatureException,
            IOException {
        CMSProcessableInputStream input = new CMSProcessableInputStream(content);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        // CertificateChain
        List<Certificate> certList = Arrays.asList(cert);

        CertStore certStore = null;
        try {
            certStore = CertStore.getInstance("Collection",
                    new CollectionCertStoreParameters(certList), provider);
            gen.addSigner(privKey, (X509Certificate) certList.get(0),
                    CMSSignedGenerator.DIGEST_SHA256);
            gen.addCertificatesAndCRLs(certStore);
            CMSSignedData signedData = gen.generate(input, false, provider);
            return signedData.getEncoded();
        } catch (Exception e) {
            // should be handled
            e.printStackTrace();
        }
        throw new RuntimeException("Problem while preparing signature");
    }

    public static void main(String[] args) throws KeyStoreException,
            NoSuchAlgorithmException, CertificateException,
            FileNotFoundException, IOException, COSVisitorException,
            SignatureException, Exception {
        File document = new File("resources/OCD.pdf");
        PDDocument pdDocument = PDDocument.load(document);

        addSignature(pdDocument, "resources/j4l_test.p12", "test");
        addSignature(pdDocument, "resources/my.p12", "123456");

        File outputDocument = new File("resources/signed" + document.getName());
        FileInputStream fis = new FileInputStream(document);
        FileOutputStream fos = new FileOutputStream(outputDocument);
        byte[] buffer = new byte[8 * 1024];
        int c;
        while ((c = fis.read(buffer)) != -1)
        {
          fos.write(buffer, 0, c);
        }
        fis.close();
        fis = new FileInputStream(outputDocument);

        pdDocument.saveIncremental(fis, fos);
        //pdDocument.close();

    }




    static void addSignature(PDDocument  pdDocument,  String filePath,String pwd)  throws Exception{
        File ksFile = new File(filePath);
        KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
        char[] pin = pwd.toCharArray();
        keystore.load(new FileInputStream(ksFile), pin);
        SigningTest signing = new SigningTest(keystore, pin.clone());
        //signing.signPDF(document);

        // create signature dictionary
        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
        // subfilter for basic and PAdES Part 2 signatures
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("signer name");
        signature.setLocation("signer location");
        signature.setReason("reason for signature");

        // the signing date, needed for valid signature
        signature.setSignDate(Calendar.getInstance());
//      SignatureOptions signatureOptions= new SignatureOptions();
//      signatureOptions.setVisualSignature();
        // register signature dictionary and sign interface
        pdDocument.addSignature(signature, signing);

    }
}

class CMSProcessableInputStream implements CMSProcessable {

    InputStream in;

    public CMSProcessableInputStream(InputStream is) {
        in = is;
    }

    public Object getContent() {
        return null;
    }

    public void write(OutputStream out) throws IOException, CMSException {
        // read the content only one time
        byte[] buffer = new byte[8 * 1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
    }
}

共有1个答案

罗昕
2023-03-14

PDF文件可以进行多次数字签名吗

是的,可以,但是这必须发生在连续的增量更新中,而不是在同一个版本中。

是的

PDDocument pdDocument = PDDocument.load(document);
addSignature(pdDocument, "resources/j4l_test.p12", "test");
addSignature(pdDocument, "resources/my.p12", "123456");
...
pdDocument.saveIncremental(fis, fos);

但你必须这么做

PDDocument pdDocument = PDDocument.load(document);
addSignature(pdDocument, "resources/j4l_test.p12", "test");
...
pdDocument.saveIncremental(fis, fos); // fos an intermediary file

...

pdDocument = PDDocument.load(intermediaryFile);
addSignature(pdDocument, "resources/my.p12", "123456");
...
pdDocument.saveIncremental(fis, fos); // fos the final file

在这个答案和其中的链接中可以找到一些背景。

 类似资料:
  • 我想问一个问题,如果我想在多页pdf中添加数字签名,每页都有相同的印章,我是否可以在第一页只添加一次数字签名,然后其他页面只需要引用第一个印章的外观。因为使用这种方法可以减少添加邮票的时间。 我使用了mkl给出的代码,但我有一个问题。我用其他代码替换了以下代码。 原件: 现在: 原始代码在加盖印花时有效,但修改后的代码将使印花无效。我使用Adobe Acrobat Pro DC打开已签名的文档。此

  • 我是使用数字签名的新手。在其中一个项目中,我们使用Apache PdfBox来处理数字签名的pdf文件。虽然我们可以测试所有功能,但签名pdf文件的验证是我们无法破解的。我们使用BouncyCastle作为提供程序。下面是代码: 从pdf文件获取数字签名和签名内容: 数字签名验证: 以下是p7b格式证书的相关摘录-我使用BouncyCastle作为安全提供程序: 使用上面的代码,我总是得到“fal

  • 签署修改的问题。 Iam使用DSC令牌传递文档哈希和签名哈希(外部签名)。 Iam收到错误,例如:“文档自签名以来已被更改或损坏”获取文档哈希:- 外部签名代码:- 签名附加代码:- 此签名附加后,但在打开签名的PDF时会出现如下图所示的错误。 PDF文件链接:https://drive.google.com/file/d/1qRT2CVgET8Ds1fu0b5psii3j8ytPKaLH/vie

  • 我的公司已经请求一个Java web服务实现,从PDF表单中提取数据,以启动使用Apache PDFBox的客户端操作的直通处理功能。很简单。困难的是,表格是由我公司的客户代表最终客户提交的,但最终客户的签名必须经过验证。 签署这些表格的商业案例是通过非正式的电子签名(湿签名的数字表示)过程,如奥多比阅读器中带有客户签名图像的签名“邮票”,或在iPad上的触摸屏绘图。到目前为止,我无法一致地验证这

  • 请原谅我!我在爪哇很穷。 我错在哪里请指正,差在哪里请改进! 我正尝试使用PDFBox对动态创建的pdf进行数字签名,并使用以下程序: 程序中的任务: (i)创建模板PDF (ii)更新ByteRange、xref、startxref (iii)构造用于签名创建的原始文档 (iv)创建分离式信封数字签名 (v)通过连接原始文档第一部分、分离式签名和原始PDF第二部分来构造数字签名的PDF文档 观察

  • 我在这里读到了关于saveIncremental是如何工作的,我的最终结果需要类似于这里,我已经成功地基于签名字段本身创建了具有多重可视化的可见签名(与响应中的情况不同,但响应对我帮助很大)。为了详细说明标题,我目前的基本任务是在已经签名的文档上创建一个空签名字段,而不中断现有签名。但是,这里的示例不适用于saveIncremental。我在主函数的末尾添加了以下片段(改编),但没有结果: 生成的