Search code examples
javapdfitextcertificatedigital-signature

Java PDF digital signature using iText visible, but not printable


I'm using iText's methods to sign a PDF with digital certificate, generating a signature visible in the document with PdfSignatureAppearance, but I'd like the visible signature not to come out in print. I saw that there is something similar in the PdfAnnotation class, where you can add a flag for this. Is there any way to do this with the digital signature? My code:

PdfStamper stp = null;
try {
    PdfReader reader = new PdfReader(pdfInputFileName);

    stp = PdfStamper.createSignature(reader, fout, '\0');
    PdfSignatureAppearance sap = stp.getSignatureAppearance();

    sap.setCrypto(privateKey, certificateChain, null, PdfSignatureAppearance.WINCER_SIGNED);

    sap.setReason(reason);
    sap.setLocation(location);
    sap.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
    sap.setVisibleSignature(new Rectangle(30, 830, 170, 770), 1, null);

    stp.close();

} catch (DocumentException | IOException e) {

    logger.error("An unknown error accoured while signing the PDF file: " + e.getMessage());
}

This is the link to a PDF signed by this code, when I print it, the signature stamp always comes out in the print: https://s3.amazonaws.com/gxzadminlocal/anexo_28276.pdf


Solution

  • Current iText 5 versions automatically set the PRINT flag if the signature field does not yet exist and they have to create it. But if you fill an existing signature field, that flag is untouched. Thus, you might want to add an empty signature field without that PRINT flag in a first step and in a second step sign using that field, e.g. with the following code:

    Preparing the PDF

    You can prepare a PDF with an empty signature field without setting the PRINT flag like this:

    try (   InputStream resource = SOURCE_STREAM;
            OutputStream os = INTERMEDIATE_OUTPUT_STREAM) {
        PdfReader reader = new PdfReader(resource);
        PdfStamper stamper = new PdfStamper(reader, os);
        PdfFormField field = PdfFormField.createSignature(stamper.getWriter());
        field.setFieldName("Signature");
        field.setWidget(new Rectangle(30, 830, 170, 770), PdfAnnotation.HIGHLIGHT_NONE);
        stamper.addAnnotation(field, 1);
        stamper.close();
    }
    

    (CreateSignature test signWidgetNoPrint pass 1)

    In particular you don't do

    field.setFlags(PdfAnnotation.FLAGS_PRINT);
    

    here!

    Signing the prepared PDF

    Having created that intermediate PDF, you can sign it like this:

    try (   InputStream resource = INTERMEDIATE_INPUT_STREAM;
            OutputStream os = RESULT_STREAM) {
        PdfReader reader = new PdfReader(resource);
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setReason("reason");
        appearance.setLocation("location");
        appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
        appearance.setVisibleSignature("Signature");
    
        ExternalSignature pks = new PrivateKeySignature(pk, "SHA512", "BC");
        ExternalDigest digest = new BouncyCastleDigest();
        MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
    }
    

    (CreateSignature test signWidgetNoPrint pass 2)

    assuming you have prepared your private key in pk and your certificate chain in chain; and assuming you have registered Bouncy Castle as security provider.

    In the result PDF the signature visualization appears on screen but not in print.