How to show digital PDF signature in all document&

2019-01-12 11:02发布

问题:

I've been working on a digital signature function for some days, now that I have everything working it is time to try to print the stamp in all pages but I'm not doing great...

Trying to give a quick resume, to show the stamp what I do is creating PdfStamper, PdfSignatureAppearance and a Rectangle, then call the

 appearance.setVisibleSignature(rectangle, 1, "SIGNATURE")

The second parameter "1" above is the page number that I want to show the stamp, it is okay to be 1 by now, as I tried to do in order to show stamp in other page was creating other instances of PdfStamper, PdfSignatureAppearance and a Rectangle but set it to page 2. If it had worked I would have put it within a loop and keep changing the page parameter.

But why didn't it work??? Well, near the end I call a method of MakeSignature and among the parameters I have to pass one of the appearences I created, if I call it more than once the signature appears only on the page related to the last appearence I pass to it.

For example:

    MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
    MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);

The stamp will be showed on the first page only.

Maybe I could get some help here???

Here it goes the whole thing:

public String signPdfFirstTime(String src, String dest, PrivateKey pk, Certificate[] chain, String providerName, String conteudoBase64, X509Certificate cert, String alias) throws IOException, DocumentException, GeneralSecurityException
{
    byte[] conteudoBinario = Base64.decode(conteudoBase64);

    FileOutputStream fos = new FileOutputStream(path + File.separator + src);
    fos.write(conteudoBinario);
    fos.close();

    File f = new File(path + File.separator + src);
    FileInputStream in = new FileInputStream(f);
    PdfReader reader = new PdfReader(in);

    int qtypages = reader.getNumberOfPages(); 
    FileOutputStream os = new FileOutputStream(path + File.separator + dest);
    PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
    Rectangle rectangle = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance.setVisibleSignature(rectangle, 1, "SIGNATURE");

    //Here I build a custom message...nothing relevant
    StringBuilder stampMessage = new StringBuilder();
    stampMessage.append("...");
    stampMessage.append(alias);
    stampMessage.append(" - ");
        // customize appearance layer 2 to display text vertically
    PdfTemplate layer2 = appearance.getLayer(2);
    layer2.transform(new AffineTransform(0, 1, -1, 0, rectangle.getWidth(), 0));
    Font font = new Font();
    font.setColor(BaseColor.BLACK);
    ColumnText ct2 = new ColumnText(layer2);
    ct2.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct2.setSimpleColumn(new Phrase(stampMessage.toString(), font), 0, 0, rectangle.getHeight(), rectangle.getWidth(), 15, Element.ALIGN_LEFT);
    ct2.go();
    appearance.setCertificate(cert);

    //Here starts where I tried to make a second stamp to show in the page 2
    FileOutputStream fos2 = new FileOutputStream(path + File.separator + src);
    fos2.write(conteudoBinario);
    fos2.close();
    File f2 = new File(path + File.separator + src);
    FileInputStream in2 = new FileInputStream(f2);

    PdfReader reader2 = new PdfReader(in2);
    FileOutputStream os2 = new FileOutputStream(path + File.separator + dest);

    PdfStamper stamper2 = PdfStamper.createSignature(reader2, os2, '\0');
    // Creating the appearance
    PdfSignatureAppearance appearance2 = stamper2.getSignatureAppearance();

    Rectangle rectangle2 = new Rectangle(550, 50, 610, 500);// funciona vertical
    appearance2.setVisibleSignature(rectangle2, 3, "ASSINATURA2");

    //Cria a msg que aparece na estampa
    StringBuilder stampMessage2 = new StringBuilder();
    stampMessage2.append(" - ");

    PdfTemplate layer22 = appearance.getLayer(2);
    layer22.transform(new AffineTransform(0, 1, -1, 0, rectangle2.getWidth(), 0));
    Font font2 = new Font();
    font2.setColor(BaseColor.BLACK);
    ColumnText ct22 = new ColumnText(layer22);
    ct22.setRunDirection(PdfWriter.RUN_DIRECTION_NO_BIDI);
    ct22.setSimpleColumn(new Phrase(stampMessage2.toString(), font2), 0, 0, rectangle2.getHeight(), rectangle2.getWidth(), 15, Element.ALIGN_LEFT);
    ct22.go();
    appearance2.setCertificate(cert);

    // Creating the signature
    ExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, providerName);
    ExternalDigest digest = new BouncyCastleDigest();
    List<CrlClient> crlList = new ArrayList<CrlClient>();
    crlList.add(new CrlClientOnline());

    LtvVerification v = stamper.getLtvVerification();
    LtvVerification v2 = stamper2.getLtvVerification();

    OcspClient ocspClient = new OcspClientBouncyCastle();

    String url = CertificateUtil.getCRLURL(cert);
    CertificateFactory cf = CertificateFactory.getInstance("X.509");

    X509CRL crl = (X509CRL) cf.generateCRL(new URL(url).openStream());
    System.out.println("CRL valid until: " + crl.getNextUpdate());
    System.out.println("Certificate revoked: " + crl.isRevoked(chain[0]));

    if (crl.isRevoked(chain[0])) {

        throw new GeneralSecurityException("CERTIFICADO REVOGADO!");
    }
    else {
        MakeSignature.processCrl(cert, crlList);

        MakeSignature.signDetached(appearance2, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        MakeSignature.signDetached(appearance, digest, pks, chain, null, null, null, 0, CryptoStandard.CMS);
        os.close();
        byte[] b = this.read(f);
        return Base64.encodeBytes(b);
    }
}

回答1:

This actually is a discussion of the available options...

There are fundamentally different ways to print the signature stamp in all pages:

  • Create a single signature field with multiple visualizations, one on each page.
  • Create a single signature field with a single visualization on a single page, e.g. the last document page, and furthermore create images with the same content on all the other pages.
  • Create one signature field per page, each with a single visualization on its page.

"Visualizations" of a PDF signature field are widgets directly associated with the field; in particular they can be clicked to have a signature validation dialog open up. In contrast to these widgets the "images" in the second option are mere images without such an associated action.

Single signature, multiple visualizations

This most likely is the option the OP has on his mind. In particular this is the preferable option, at least at first glance:

  • on each page the signature image is active and allows opening the signature validation dialog;
  • but still only one digital signature has to be created, which means
    • only one entry on the signature panel on the left in Adobe Reader,
    • only one signature container to verify (therefore, no unambiguous verification results), and
    • only one use of the private key, so a PIN does not need to be entered multiple times, and in case of pay-per-signature signing services only a single signing event to pay.

There are a number of disadvantages, though:

  • Multiple visualizations of the same signature may have a negative impact on the legal value of that signature.

    Adobe, therefore, decided years ago not to create signature fields with multiple visualizations in their software, cf. for example

    The location of a signature within a document can have a bearing on its legal meaning. For this reason, signature fields never refer to more than one annotation. If more than one location is associated with a signature, the meaning may become ambiguous.

    (Digital Signature Appearances whitepaper for Adobe Acrobat version 9 dated May 5, 2008)

    E.g. in Germany jurisdiction concerning written signatures vertically confines the part of the document the signer has legally signed, he generally is not legally bound by anything written below the signature. Similar jurisdiction may exist in other legal systems, too.

    In case of electronic signatures with visualizations in the signed documents, such jurisdiction might hold analogously (or one at least has to make quite an effort to explain the differences). In case of multiple visualization of the same signature this might mean that only everything up to the first visualization is considered signed.

    (I am not a lawyer, so please do not consider this legal consultation.)

  • Because of such potential legal issues signature fields in the upcoming PDF 2.0 standard will only be allowed to have a single widget. Thus, signatures with multiple widgets likely will be considered invalid according to that standard.

  • Already now the signature panel of Adobe Reader contains "the page the signature is on", cf. the last line of this screen shot:

    Active signature fields without an associated entry (with the correct page number) on that panel might be doubted outright.

Single signature, single visualization on last page, inactive images on the other pages

In case of using this option, the disadvantages of the prior option do not apply or at least only to a lesser degree. In particular if the mere images slightly differ from the visualization by a hint indicating that they are copies, the final, true visualization will likely be considered the binding signature location.

The major disadvantage of this option, though, is that adding mere images to the content is not allowed for already signed documents. Thus, this option cannot be used for the second or third signer of a document, but the OP has indicated that the solution will eventually have to allow the documents to be signed to more than one person.

One can consider adding those images as annotations, not as content; for certain types of integrated PDF signatures adding and removing annotations after signing is an allowed operation. But if adding those annotations was allowed, usually removing them again after signing is allowed, too, making those signature images quite volatile.

Multiple signatures (one per page) with a single visualization each

This option does not have the disadvantages of the other options as each visualization corresponds to a different digital signature. Thus, the final one guarantees that the signer is legally bound by the whole document.

It does have disadvantages of its own, though:

  • During verification all these signatures will be verified. This might mean high resource requirements, and even worse ambiguous results (if some verifications fail and some succeed).
  • The Adobe Reader signature panel is flooded with entries.
  • The private key is used multiple times which in case of pay-per-signatures signing services is expensive and in case of SSCDs (in particular smart cards or tokens) might require entering the PIN multiple times and also take quite some time

Implementing the options

iText allows implementing the second and third option in a fairly straight-forward manner out-of-the-box.

Implementing the first option is possible with iText but requires the use of low-level APIs and Java reflection or alternatively patching iText a bit.


Considering the problems of each option, though, I'd advice against doing this at all, one signature right at the end of the content to sign is the least ambiguous way to sign.



回答2:

@mkl points on legal issue are very much considerable. But if you still want to sign all the pages with Itext api (version 5.5.*) , they you should do a little hack in preClose(HashMap<PdfName, Integer> exclusionSizes) method of PdfSignatureAppearance class which where the signature appearance is included in the pages.

search for writer.addAnnotation(sigField, pagen); line inside PdfSignatureAppearance class and replace with

for (int p = 1; p <= writer.reader.getNumberOfPages(); p++) {
   writer.addAnnotation(sigField, p);
}

It add the reference of the signature to all the pages.