0

Below are the signature details as part of visible Signature

I am using Apache PDFBox 2.0.12 to sign a PDF document. I want to enable a visual signature also.

I am able to sign and create a visual signature. The problem is how can we get the details regarding the Signature properties(Such as SignedBy, SignedDate from the Certificate itself and mentioned in the Text as explained here:

public class VisibleSignature2 extends SignatureBase { 
    private SignatureOptions signatureOptions;
    private VisibleSignatureOptions visibleSignatureOptions;

    public VisibleSignature2(PrivateKey keystore, List<Certificate> pin, VisibleSignatureOptions visibleSignatureOptions) {
        super(keystore, pin);
        this.visibleSignatureOptions = visibleSignatureOptions;
    }

    public void signPDF(InputStream inputFile, OutputStream signedFile, Rectangle2D humanRect) throws IOException {

        PDDocument doc = PDDocument.load(inputFile);
        int accessPermissions = SigUtils.getMDPPermission(doc);
        if (accessPermissions == 1) {
            throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
        }

        PDSignature signature = new PDSignature();
        PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
        PDRectangle rect = createSignatureRectangle(doc, humanRect);

        if (doc.getVersion() >= 1.5f && accessPermissions == 0) {
            SigUtils.setMDPPermission(doc, signature, 2);
        }

        if (acroForm != null && acroForm.getNeedAppearances()) {
            // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
            // with Adobe Reader
            if (acroForm.getFields().isEmpty()) {
                // we can safely delete it if there are no fields
                acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
                // note that if you've set MDP permissions, the removal of this item
                // may result in Adobe Reader claiming that the document has been changed.
                // and/or that field content won't be displayed properly.
                // ==> decide what you prefer and adjust your code accordingly.
            }
        }

        // default filter
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

        // the signing date, needed for valid signature
        signature.setSignDate(Calendar.getInstance());
        signatureOptions = new SignatureOptions();
        System.out.println("Visible Signature Reason is " + visibleSignatureOptions.hasVisibleSignatureReason());
        if (visibleSignatureOptions.isShouldSignatureBeVisible()) {
            System.out.println("Inside VisualSignature");
            signature.setReason(visibleSignatureOptions.getVisibleSignatureReason());
            signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect));
        }
        signatureOptions.setPage(0);
        doc.addSignature(signature, this, signatureOptions);
        // write incremental (only for signing purpose)
        doc.saveIncremental(signedFile);
        doc.close();
        IOUtils.closeQuietly(signatureOptions);
    }

    private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect) {
        float x = (float) humanRect.getX();
        float y = (float) humanRect.getY();
        float width = (float) humanRect.getWidth();
        float height = (float) humanRect.getHeight();
        PDPage page = doc.getPage(0);
        PDRectangle pageRect = page.getCropBox();
        PDRectangle rect = new PDRectangle();
        // signing should be at the same position regardless of page rotation.
        switch (page.getRotation()) {
            case 90:
                rect.setLowerLeftY(x);
                rect.setUpperRightY(x + width);
                rect.setLowerLeftX(y);
                rect.setUpperRightX(y + height);
                break;
            case 180:
                rect.setUpperRightX(pageRect.getWidth() - x);
                rect.setLowerLeftX(pageRect.getWidth() - x - width);
                rect.setLowerLeftY(y);
                rect.setUpperRightY(y + height);
                break;
            case 270:
                rect.setLowerLeftY(pageRect.getHeight() - x - width);
                rect.setUpperRightY(pageRect.getHeight() - x);
                rect.setLowerLeftX(pageRect.getWidth() - y - height);
                rect.setUpperRightX(pageRect.getWidth() - y);
                break;
            case 0:
            default:
                rect.setLowerLeftX(x);
                rect.setUpperRightX(x + width);
                rect.setLowerLeftY(pageRect.getHeight() - y - height);
                rect.setUpperRightY(pageRect.getHeight() - y);
                break;
        }
        return rect;
    }

    private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum, PDRectangle rect) throws IOException {
        PDDocument doc = new PDDocument();

        PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
        doc.addPage(page);
        PDAcroForm acroForm = new PDAcroForm(doc);
        doc.getDocumentCatalog().setAcroForm(acroForm);
        PDSignatureField signatureField = new PDSignatureField(acroForm);
        PDAnnotationWidget widget = signatureField.getWidgets().get(0);
        List<PDField> acroFormFields = acroForm.getFields();
        acroForm.setSignaturesExist(true);
        acroForm.setAppendOnly(true);
        acroForm.getCOSObject().setDirect(true);
        acroFormFields.add(signatureField);

        widget.setRectangle(rect);

        // from PDVisualSigBuilder.createHolderForm()
        PDStream stream = new PDStream(doc);
        PDFormXObject form = new PDFormXObject(stream);
        PDResources res = new PDResources();
        form.setResources(res);
        form.setFormType(1);
        PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
        float height = bbox.getHeight();
        Matrix initialScale = null;
        switch (srcDoc.getPage(pageNum).getRotation()) {
            case 90:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
                height = bbox.getWidth();
                break;
            case 180:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
                break;
            case 270:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
                height = bbox.getWidth();
                break;
            case 0:
            default:
                break;
        }
        form.setBBox(bbox);

        PDFont font = PDType1Font.HELVETICA_BOLD;
        // from PDVisualSigBuilder.createAppearanceDictionary()
        PDAppearanceDictionary appearance = new PDAppearanceDictionary();
        appearance.getCOSObject().setDirect(true);
        PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
        appearance.setNormalAppearance(appearanceStream);
        widget.setAppearance(appearance);

        PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream);


        if (initialScale != null) {
            cs.transform(initialScale);
        }

        // show text
        float fontSize = 10;
        float leading = fontSize * 1.5f;
        cs.beginText();
        cs.setFont(font, fontSize);
        cs.setNonStrokingColor(Color.black);
        cs.newLineAtOffset(fontSize, height - leading);
        cs.setLeading(leading);
        cs.showText("(Signature very wide line 1)");
        cs.newLine();
        cs.showText("(Signature very wide line 2)");
        cs.newLine();
        cs.showText("(Signature very wide line 3)");
        cs.endText();

        cs.close();

        cs.close();
        // no need to set annotations and /P entry
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        doc.save(baos);
        doc.close();
        return new ByteArrayInputStream(baos.toByteArray());
    }

}
mkl
  • 90,588
  • 15
  • 125
  • 265
  • First of all, your example contains a green tick in it. This might be an indication that your example visible signature uses techniques deprecated for more than a decade. Don't expect the same from current signing code. – mkl Feb 07 '19 at 11:08
  • I don't understand the question. You want to know your own signing date when signing? Or do you want to know how to get the text to appear? If so, just replace "Signature very wide line" with your own text. – Tilman Hausherr Feb 07 '19 at 11:42
  • @mkl Thanks for the info. So as of now, Currently what I am seeing, we can add any text. So just wanted to know, how can we get the details such as "SignedBy" only as it comes from the Certificate direclty. – user2119099 Feb 07 '19 at 11:53
  • @Tilman. For DocumentSigningDate we know, but for Signed By "How can we figure out the same using Cerificate" as it must match the exact details during signing. – user2119099 Feb 07 '19 at 12:00
  • `keystore.getCertificate(alias)` gives you the certificate. From that certificate you call `getSubjectX500Principal()`. Btw the current version is 2.0.13. – Tilman Hausherr Feb 07 '19 at 12:10
  • see also https://stackoverflow.com/questions/2914521/how-to-extract-cn-from-x509certificate-in-java – Tilman Hausherr Feb 07 '19 at 12:14
  • *"So just wanted to know, how can we get the details such as "SignedBy" only as it comes from the Certificate direclty"* - If it comes from the certificate directly, simply read it from the certificate. @Tilman gave some hints. – mkl Feb 07 '19 at 14:34
  • @mkl @ Tilman Thanks for the info provided. Just wanted to deep-dive more, that the green tick is deprecated only if we are using PdfBox. Currently whenever I am signing the same using iText versions. Adobe is currently showing the same. So is that some feature missing in PDFBox, regarding why Adobe is not able to understand what image needs to be populated beforehand ?? or is that thing deprecated by the Adobe itself. Because seems like its a feature missing in PDFBox. – user2119099 Feb 08 '19 at 06:04
  • *"that the green tick is deprecated only if we are using PdfBox"* - that's incorrect. With itext you only get that behavior if you set the `Acro6Layers` property to false, i.e. if you select a compatibility mode with acrobat version 5 and before. For details see [this answer](https://stackoverflow.com/a/40391641/1729265). So the green tick universally is deprecated, merely some software products keep compatibility modes for it. – mkl Feb 08 '19 at 06:22
  • And if I remember correctly (I'd have to look it up, though), itext 7 had dropped that legacy. – mkl Feb 08 '19 at 06:30
  • I have improved the example you used to include usage of the certificate for the text. Note that CreateSignatureBase.java was changed as well. – Tilman Hausherr Feb 08 '19 at 14:24
  • Thanks a lot. I am able to successfully display the signature details as text only without any confirmation over the page content(visible Signature). My only concern here is. Will it be possible to use the feature being deprecated as we also want to maintain some backward compatibility of displaying the same verification like mentioned above using PDFBox(this is to maintain backward compatibility). For all the newer releases we are using the latest versions. But for the older versions we need to keep the behaviour same. – user2119099 Feb 11 '19 at 05:45
  • *"maintain some backward compatibility"* - Acrobat 6 has been released July 2003. I.e. you want to maintain backward compatibility with a feature deprecated for 15½ years! If you try to maintain backward compatibility in general for features that old, your software must be more than 50% compatibility code. That being remarked, you can of course construct signature appearances compatible with Acrobat 5, simply replace the simple structure in the PDFBox example by a structure as described in [this Adobe document](https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PPKAppearances.pdf). – mkl Feb 11 '19 at 10:02
  • Thanks. I don't see any class like DigSigCreateStdXObj or DigSigAPCreateCompositeTextXObj in PDFBox version. By backward compatibility I mean is it possible by using Apache PDFBox ? – user2119099 Feb 11 '19 at 12:15
  • There isn't, one can still do it by creating the many structures involved, i.e. all these "n" layers. These are all PDFormXObject. But I won't write an answer for that, this is too much work and useless unless you have clients that use Acrobat 6. – Tilman Hausherr Feb 11 '19 at 13:14
  • @user2119099 *"I don't see any class like ... in PDFBox version."* - obviously not. That document is not a PDFBox documentation but relates to Acrobat. That's why I said *replace the simple structure in the PDFBox example by a **structure** as described*, the structures being the objects from "Signature appearance objects in a PDF file" and the default resources from "Signature objects in a PDF file" in that Adobe document. – mkl Feb 11 '19 at 14:19

0 Answers0