retrieve the pkcs7 file from pdf and co-sign

2020-07-30 00:18发布

问题:

I have a PDF file signed and .p7s file. I need retrieve .p7s from PDF and co-sign the PDF (generating other .p7s). After I need put again the p7s into PDF file. I get this error when I try get p7s from PDF: ExceptionConverter: java.security.SignatureException: object not initialized for signing My code:

BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);

PdfReader pdfReader = new 
PdfReader(Files.readAllBytes(inputFile.toPath()));

AcroFields acroFields = pdfReader.getAcroFields();          
List<String> signatures = acroFields.getSignatureNames();

for (String name : signatures) {
   PdfPKCS7 pdfPkcs7 = acroFields.verifySignature(name, "BC");
   Files.write(Paths.get("~/TEST/itext/"+ name +".p7s"), 
   pdfPkcs7.getEncodedPKCS7());//ERROR HERE!
}

回答1:

In Theory

You want to co-sign the PDF (counter-sign? parallel-sign?) by extracting an embedded CMS signature container from a PDF, adding your signature to it, and re-embedding the extended container.

This is the wrong approach, PDF signing is designed to contain a single signature only per PDF signature field and the CMS signature container we talk about essentially is the value of such a field, so each embedded signature container shall contain exactly one signature.

Strictly speaking the PDF specification allows you to use arbitrary custom signing schemes, only "interoperable signatures" are limited. Thus, your signature containers with multiple signatures could be considered valid PDF, merely not "interoperably signed", i.e. you shouldn't expect any other software to interpret the signature like you do. In particular Adobe Acrobat Reader will recognize and display only one of those signatures.

(Indeed also some large scale signing solution providers have embedded multi-signature CMS containers in PDFs. E.g. currently I have to deal with signatures generated by some such a provider who embeds arbitrary CAdES-A signature containers (in particular some with multiple signers) into PDFs and calls this signature format PDF/CAdES-A, and now I have to explain to their customers that their signatures are PDF signatures all right but not interoperable ones even though that name PDF/CAdES-A sounds like some awesome standard...)

Multiple signatures in PDFs for interoperability must be applied sequentially like visualized in this sketch:

For some backgrounds and links to more information, cf. this answer.

In practice

Your code appears to be iText 5.5.x code, in particular not yet iText 7.x code. In that case you can use the code from the Digital Signatures for PDF Documents white book by Bruno Lowagie as is.

A very simple example:

public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm,
        String provider, CryptoStandard subfilter, String reason, String location)
        throws GeneralSecurityException, IOException, DocumentException {
    // Creating the reader and the stamper
    PdfReader reader = new PdfReader(src);
    FileOutputStream os = new FileOutputStream(dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    appearance.setReason(reason);
    appearance.setLocation(location);
    appearance.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
    // Creating the signature
    ExternalDigest digest = new BouncyCastleDigest();
    ExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider);
    MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, subfilter);
}

(Code sample 2.1: the “Hello World” of signing with iText)

As your use case involves signing an already signed document, you merely have to change the PdfStamper.createSignature line to

PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', true);

(notice the new boolean argument at the end). This change causes the signature to be created in append mode which leaves the former revision with the original signature as is to not invalidate that signature.