Can't add ltv to PDF document. Error

2019-08-08 13:57发布

问题:

I'm sign document, using Itext. I have that method:

public static void sign(String src, String dest, Certificate[] chain,PrivateKey pk, String digestAlgorithm, String provider,CryptoStandard subfilter, TSAClient tsa )
{

        // 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.setVisibleSignature(new Rectangle(10, 20, 100, 200), 1, "sig");

        // Creating the signature
        ExternalDigest digest = new BouncyCastleDigest();
        ExternalSignature signature = new PrivateKeySignature(pk,
        digestAlgorithm, provider);
        MakeSignature.signDetached(appearance, digest, signature, chain, null,null, tsa, 0, subfilter);


        // ALREADY SIGNED. ADD LTB NOW.
        CrlClient crlClient = new CrlClientOnline("http://crl.mycrl.com/mycrl.crl");
        OcspClient  ocspClient=new OcspClientBouncyCastle();              
    addLtv(DEST, DEST2, ocspClient, crlClient, tsa);

    }

and I sign:

sign(SRC, String.format(DEST, 1), chain, pk, DigestAlgorithms.SHA256, provider.getName(), CryptoStandard.CMS, "For Testing", " location", tsa);

everything works. PDF is signed well.

but then, I can't add ltv. I use code from itext documentation:

public static void addLtv(String src, String dest, OcspClient ocsp, CrlClient crl,
        TSAClient tsa) throws IOException, DocumentException,
        GeneralSecurityException {

    PdfReader r = new PdfReader(src);
    FileOutputStream fos = new FileOutputStream(dest);

    PdfStamper stp = PdfStamper.createSignature(r, fos, '\0', null, true);
    LtvVerification v = stp.getLtvVerification();

    AcroFields fields = stp.getAcroFields();

    List<String> names = fields.getSignatureNames();
    String sigName = names.get(names.size() - 1);

    PdfPKCS7 pkcs7 = fields.verifySignature(sigName);

    if (pkcs7.isTsp()) {
            v.addVerification(sigName, ocsp, crl,
                LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
                LtvVerification.Level.OCSP_CRL,
                LtvVerification.CertificateInclusion.NO);
    }

    else {
        for (String name : names) {
            v.addVerification(name, ocsp, crl,
                    LtvVerification.CertificateOption.WHOLE_CHAIN,
                    LtvVerification.Level.OCSP_CRL,
                    LtvVerification.CertificateInclusion.NO);
        }
    }
    PdfSignatureAppearance sap = stp.getSignatureAppearance();
    LtvTimestamp.timestamp(sap, tsa, null);
}

EDITED: everything works, but at line LtvTimestamp.timestamp(sap, tsa, null); I have that error: Exception in thread "main" java.io.IOException: Not enough space

that's my pdf: https://www.dropbox.com/s/o05rw6ubiuslm4j/DOC_SIGNED.pdf

Exception in thread "main" java.io.IOException: Not enough space
at com.itextpdf.text.pdf.security.LtvTimestamp.timestamp(LtvTimestamp.java:103)
at ge.digital.signature.DocumentSigner.DigitalSignature.addLtv(MySignature.java:132)
at ge.digital.signature.DocumentSigner.DigitalSignature.main(MySignature.java:163)

回答1:

That IOException occurs when the space reserved in the PDF for the integration of the time stamp does not suffice. Thus, you have to change the method getTokenSizeEstimate of the TSAClient instance tsa you call your sign method with to return a larger estimate of the time stamp size.

In case of the TSAClientBouncyCastle implementation of TSAClient, e.g., you can make it return arbitrary estimates instead of the default 4096 if you use the constructor with four arguments:

public TSAClientBouncyCastle(String url, String username, String password, int tokSzEstimate)

Some background: When integrating a signature or a document time stamp into a PDF, you first prepare the PDF with a gap of a given size, then calculate the hash of everything but that gap, then sign or time stamp that hash, and finally integrate the generated signature or time stamp into that gap, e.g.

(That /ByteRange entry is part of the signed content. Thus, one cannot enlarge the gap afterwards.)

Therefore, you have to use some estimate of the size of the generated signature or time stamp before its generation.

In case of document time stamps, this estimate is provided by the TSAClient method getTokenSizeEstimate.

PS: For still more details cf. this answer, the Adobe document Digital Signatures in a PDF (from which I copied the figure above), and the PDF specification ISO 32000-1:2008 made available here by Adobe.