title: Can PDFBox sign all 'Approval Signatures' instead of always making first signature as 'Certified'?
body: <p>As Adobe article <a href="https://www.adobe.com/devnet-docs/acrobatetk/tools/DigSig/Acrobat_DigitalSignatures_in_PDF.pdf" rel="nofollow noreferrer">"Digital Signatures in a PDF"</a> stating:</p>
  
  <blockquote>
    <p>PDF defines two types of signatures: approval and certification.  The
    differences are as follows:
    <strong>Approval:</strong> There can be any number of approval signatures in a document. The field may optionally be associated with FieldMDP
    permissions. 
    <strong>Certification:</strong> There can be only one certification signature and it must be the first one in a document. The field is always associated
    with DocMDP.</p>
  </blockquote>
  
  <p>Using PDFBox examples I was able to successfully apply multiple signatures to my document: <a href="https://github.com/apache/pdfbox/blob/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java" rel="nofollow noreferrer">https://github.com/apache/pdfbox/blob/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java</a>
  In order to apply multiple signatures I was just running same code multiple times with different signature placeholders and images.</p>
  
  <p>But what I have distinguished is that, even though I am running same code it always sets first signature as Certified, and all other once as Approval.</p>
  
  <p><a href="https://i.stack.imgur.com/vifwa.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vifwa.png" alt="enter image description here"></a></p>
  
  <p>But in my case, I don't want a document to be certified, I just need all signatures to be of Apploval type including the first one. I know I can invisible first Certifying signature, but still I do not want to certify document at all. </p>
  
  <p>I was trying to find a way to setup signature, but couldn't figure it out.</p>
  
  <p>Here is my usage of example code (other classes are in the GitHub link above):</p>
  
  <pre><code>public class SignnerPDFBoxExample extends CreateSignatureBase {
  
      private SignatureOptions signatureOptions;
      private PDVisibleSignDesigner visibleSignDesigner;
      private final PDVisibleSigProperties visibleSignatureProperties = new PDVisibleSigProperties();
      private boolean lateExternalSigning = false;
  
      public static void main(String[] args) throws Exception {
  
          File ksFile = new File("keystore.jks");
          KeyStore keystore = KeyStore.getInstance("JKS");
          char[] pin = "123456".toCharArray();
          keystore.load(new FileInputStream(ksFile), pin);
  
          SignnerPDFBoxExample signer = new SignnerPDFBoxExample(keystore, pin.clone());
          String inputFilename = "Four_Signature_template.pdf";
  
          File documentFile = new File(inputFilename);
          File signedDocumentFile;
          int page = 1;
          try (FileInputStream imageStream = new FileInputStream("client_signature.jpg"))
          {
              String name = documentFile.getName();
              String substring = name.substring(0, name.lastIndexOf('.'));
              signedDocumentFile = new File(documentFile.getParent(), substring + "_signed.pdf");
              // page is 1-based here
              signer.setVisibleSignDesigner(inputFilename, 0, 0, -50, imageStream, page);
          }
          signer.setVisibleSignatureProperties("name", "location", "Signed using PDFBox", 0, page, true);
          signer.signPDF(documentFile, signedDocumentFile, null, "certifySignature");
      }
  
      public boolean isLateExternalSigning()
      {
          return lateExternalSigning;
      }
  
      /**
       * Set late external signing. Enable this if you want to activate the demo code where the
       * signature is kept and added in an extra step without using PDFBox methods. This is disabled
       * by default.
       *
       * @param lateExternalSigning
       */
      public void setLateExternalSigning(boolean lateExternalSigning)
      {
          this.lateExternalSigning = lateExternalSigning;
      }
  
      /**
       * Set visible signature designer for a new signature field.
       * 
       * @param filename
       * @param x position of the signature field
       * @param y position of the signature field
       * @param zoomPercent
       * @param imageStream
       * @param page the signature should be placed on
       * @throws IOException
       */
      public void setVisibleSignDesigner(String filename, int x, int y, int zoomPercent, 
              FileInputStream imageStream, int page) 
              throws IOException
      {
          visibleSignDesigner = new PDVisibleSignDesigner(filename, imageStream, page);
          visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent).adjustForRotation();
      }
  
      /**
       * Set visible signature designer for an existing signature field.
       * 
       * @param zoomPercent
       * @param imageStream
       * @throws IOException
       */
      public void setVisibleSignDesigner(int zoomPercent, FileInputStream imageStream) 
              throws IOException
      {
          visibleSignDesigner = new PDVisibleSignDesigner(imageStream);
          visibleSignDesigner.zoom(zoomPercent);
      }
  
      /**
       * Set visible signature properties for new signature fields.
       * 
       * @param name
       * @param location
       * @param reason
       * @param preferredSize
       * @param page
       * @param visualSignEnabled
       * @throws IOException
       */
      public void setVisibleSignatureProperties(String name, String location, String reason, int preferredSize, 
              int page, boolean visualSignEnabled) throws IOException
      {
          visibleSignatureProperties.signerName(name).signerLocation(location).signatureReason(reason).
                  preferredSize(preferredSize).page(page).visualSignEnabled(visualSignEnabled).
                  setPdVisibleSignature(visibleSignDesigner);
      }
  
      /**
       * Set visible signature properties for existing signature fields.
       * 
       * @param name
       * @param location
       * @param reason
       * @param visualSignEnabled
       * @throws IOException
       */
      public void setVisibleSignatureProperties(String name, String location, String reason,
              boolean visualSignEnabled) throws IOException
      {
          visibleSignatureProperties.signerName(name).signerLocation(location).signatureReason(reason).
                  visualSignEnabled(visualSignEnabled).setPdVisibleSignature(visibleSignDesigner);
      }
  
      /**
       * Initialize the signature creator with a keystore (pkcs12) and pin that
       * should be used for the signature.
       *
       * @param keystore is a pkcs12 keystore.
       * @param pin is the pin for the keystore / private key
       * @throws KeyStoreException if the keystore has not been initialized (loaded)
       * @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found
       * @throws UnrecoverableKeyException if the given password is wrong
       * @throws CertificateException if the certificate is not valid as signing time
       * @throws IOException if no certificate could be found
       */
      public SignnerPDFBoxExample(KeyStore keystore, char[] pin)
              throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException
      {
          super(keystore, pin);
      }
  
      /**
       * Sign pdf file and create new file that ends with "_signed.pdf".
       *
       * @param inputFile The source pdf document file.
       * @param signedFile The file to be signed.
       * @param tsaClient optional TSA client
       * @throws IOException
       */
      public void signPDF(File inputFile, File signedFile, TSAClient tsaClient) throws IOException
      {
          this.signPDF(inputFile, signedFile, tsaClient, null);
      }
  
      /**
       * Sign pdf file and create new file that ends with "_signed.pdf".
       *
       * @param inputFile The source pdf document file.
       * @param signedFile The file to be signed.
       * @param tsaClient optional TSA client
       * @param signatureFieldName optional name of an existing (unsigned) signature field
       * @throws IOException
       */
      public void signPDF(File inputFile, File signedFile, TSAClient tsaClient, String signatureFieldName) throws IOException
      {
          setTsaClient(tsaClient);
  
          if (inputFile == null || !inputFile.exists())
          {
              throw new IOException("Document for signing does not exist");
          }
  
          // creating output document and prepare the IO streams.
          FileOutputStream fos = new FileOutputStream(signedFile);
  
          try (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");
              }
              // Note that PDFBox has a bug that visual signing on certified files with permission 2
              // doesn't work properly, see PDFBOX-3699. As long as this issue is open, you may want to
              // be careful with such files.
  
              PDSignature signature;
  
              // sign a PDF with an existing empty signature, as created by the CreateEmptySignatureForm example.
              signature = findExistingSignature(doc, signatureFieldName);
  
              if (signature == null)
              {
                  // create signature dictionary
                  signature = new PDSignature();
              }
  
              // Optional: certify
              // can be done only if version is at least 1.5 and if not already set
              // doing this on a PDF/A-1b file fails validation by Adobe preflight (PDFBOX-3821)
              // PDF/A-1b requires PDF version 1.4 max, so don't increase the version on such files.
              if (doc.getVersion() &gt;= 1.5f &amp;&amp; accessPermissions == 0)
              {
                  SigUtils.setMDPPermission(doc, signature, 2);
              }
  
              PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
              if (acroForm != null &amp;&amp; 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.
                      // ==&gt; decide what you prefer and adjust your code accordingly.
                  }
                  else
                  {
                      System.out.println("/NeedAppearances is set, signature may be ignored by Adobe Reader");
                  }
              }
  
              // default filter
              signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
  
              // subfilter for basic and PAdES Part 2 signatures
              signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
  
              if (visibleSignatureProperties != null)
              {
                  // this builds the signature structures in a separate document
                  visibleSignatureProperties.buildSignature();
  
                  signature.setName(visibleSignatureProperties.getSignerName());
                  signature.setLocation(visibleSignatureProperties.getSignerLocation());
                  signature.setReason(visibleSignatureProperties.getSignatureReason());
              }
  
              // the signing date, needed for valid signature
              signature.setSignDate(Calendar.getInstance());
  
              // do not set SignatureInterface instance, if external signing used
              SignatureInterface signatureInterface = isExternalSigning() ? null : this;
  
              // register signature dictionary and sign interface
              if (visibleSignatureProperties != null &amp;&amp; visibleSignatureProperties.isVisualSignEnabled())
              {
                  signatureOptions = new SignatureOptions();
                  signatureOptions.setVisualSignature(visibleSignatureProperties.getVisibleSignature());
                  signatureOptions.setPage(visibleSignatureProperties.getPage() - 1);
                  doc.addSignature(signature, signatureInterface, signatureOptions);
              }
              else
              {
                  doc.addSignature(signature, signatureInterface);
              }
  
              if (isExternalSigning())
              {
                  System.out.println("Signing externally " + signedFile.getName());
                  ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);
                  // invoke external signature service
                  byte[] cmsSignature = sign(externalSigning.getContent());
  
                  // Explanation of late external signing (off by default):
                  // If you want to add the signature in a separate step, then set an empty byte array
                  // and call signature.getByteRange() and remember the offset signature.getByteRange()[1]+1.
                  // you can write the ascii hex signature at a later time even if you don't have this
                  // PDDocument object anymore, with classic java file random access methods.
                  // If you can't remember the offset value from ByteRange because your context has changed,
                  // then open the file with PDFBox, find the field with findExistingSignature() or
                  // PODDocument.getLastSignatureDictionary() and get the ByteRange from there.
                  // Close the file and then write the signature as explained earlier in this comment.
                  if (isLateExternalSigning())
                  {
                      // this saves the file with a 0 signature
                      externalSigning.setSignature(new byte[0]);
  
                      // remember the offset (add 1 because of "&lt;")
                      int offset = signature.getByteRange()[1] + 1;
  
                      // now write the signature at the correct offset without any PDFBox methods
                      try (RandomAccessFile raf = new RandomAccessFile(signedFile, "rw"))
                      {
                          raf.seek(offset);
                          raf.write(Hex.getBytes(cmsSignature));
                      }
                  }
                  else
                  {
                      // set signature bytes received from the service and save the file
                      externalSigning.setSignature(cmsSignature);
                  }
              }
              else
              {
                  // write incremental (only for signing purpose)
                  doc.saveIncremental(fos);
              }
          }
  
          // Do not close signatureOptions before saving, because some COSStream objects within
          // are transferred to the signed document.
          // Do not allow signatureOptions get out of scope before saving, because then the COSDocument
          // in signature options might by closed by gc, which would close COSStream objects prematurely.
          // See https://issues.apache.org/jira/browse/PDFBOX-3743
          IOUtils.closeQuietly(signatureOptions);
      }
  
      // Find an existing signature (assumed to be empty). You will usually not need this.
      private PDSignature findExistingSignature(PDDocument doc, String sigFieldName)
      {
          PDSignature signature = null;
          PDSignatureField signatureField;
          PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
          if (acroForm != null)
          {
              signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
              if (signatureField != null)
              {
                  // retrieve signature dictionary
                  signature = signatureField.getSignature();
                  if (signature == null)
                  {
                      signature = new PDSignature();
                      // after solving PDFBOX-3524
                      // signatureField.setValue(signature)
                      // until then:
                      signatureField.getCOSObject().setItem(COSName.V, signature);
                  }
                  else
                  {
                      throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
                  }
              }
          }
          return signature;
      }
  
      /**
       * This will print the usage for this program.
       */
      private static void usage()
      {
          System.err.println("Usage: java " + CreateVisibleSignature.class.getName()
                  + " &lt;pkcs12-keystore-file&gt; &lt;pin&gt; &lt;input-pdf&gt; &lt;sign-image&gt;\n" + "" +
                             "options:\n" +
                             "  -tsa &lt;url&gt;    sign timestamp using the given TSA server\n"+
                             "  -e            sign using external signature creation scenario");
      }
  
  
  }
  </code></pre>