title: Sign PDF with signature hash provided by api
body: <p>I am trying to sign a pdf with <code>PDFBox</code> and a third-party signature provider.</p>
  <h3>My procedure:</h3>
  <ol>
  <li>Getting the pdf from user input</li>
  <li>Creating a PDDocument with the content of the given pdf</li>
  <li>creating a PDSignature with all given properties such as organisation or location but without the signature-hash itself.</li>
  <li>adding this signature object to the PDDocument and creating a digest from the PDDocument to send to the API.</li>
  <li>Awaiting a successful answer from the API (containing the signature hash) and inserting this hash into the PDDocuments PDSignature object without altering the whole document.</li>
  </ol>
  <h3>My problem:</h3>
  <p>I can create the PDDocument, the PDSignature and the digest of it and send it to the API. I then get the correct and valid signature hash back from the API and I can add it to the PDDocument but whenever this works, the resulting pdf has an invalid signature due to &quot;being altered or manipulated&quot;(the signer, timestamp and certificate are valid). I also tried to use the <code>ExternalSigningSupport</code> from <code>PDFBox</code> but I cannot use it as I always run into an error:</p>
  <pre><code>&quot;java.lang.IllegalStateException: signature reserve byte range has been changed after addSignature(), please set the byte range that existed after addSignature()&quot;.
  </code></pre>
  <h3>My Code that works but invalidates the signature:</h3>
  <pre><code>//Pending Signature is just a DTO
  public static PendingSignature createPendingSignatureFromPdf(InputStream pdf2sign, OutputStream fos, String sigName, String sigLocation, String sigReason, String contactInfo, Date forcedDate, Long revisionId) throws IOException {
      
          File pdfFileToSigned = createTempFile(&quot;chars&quot;, &quot;&quot; + Calendar.getInstance().getTimeInMillis());
          File pdfPreparedToBeSigned = createTempFile(&quot;chars&quot;, &quot;&quot; + Calendar.getInstance().getTimeInMillis());
          File pdfHashPreparedToBeSigned = createTempFile(&quot;chars&quot;, &quot;&quot; + Calendar.getInstance().getTimeInMillis());
      
          try {
              if (fos == null) {
                  fos = new FileOutputStream(pdfPreparedToBeSigned);
              }
      
              FileUtils.copyInputStreamToFile(pdf2sign, pdfFileToSigned);
      
              PDDocument doc = PDDocument.load(pdfFileToSigned);
              PDSignature signature = new PDSignature();
      
              signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
              signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
      
              if (contactInfo != null &amp;&amp; !contactInfo.isEmpty()) {
                  signature.setContactInfo(contactInfo);
              }
              if (sigLocation != null &amp;&amp; !sigLocation.isEmpty()) {
                  signature.setLocation(sigLocation);
              }
              if (sigReason != null &amp;&amp; !sigReason.isEmpty()) {
                  signature.setReason(sigReason);
              }
              if (sigName != null &amp;&amp; !sigName.isEmpty()) {
                  signature.setName(sigName);
              }
              if (forcedDate != null) {
                  SimpleDateFormat sdf = new SimpleDateFormat(&quot;dd/MM/yyyy HH:mm:ss&quot;);
                  Calendar cal = Calendar.getInstance();
                  cal.setTime(sdf.parse(sdf.format(forcedDate)));
                  signature.setSignDate(cal);
              }
      
              final OutputStream outputStream = new FileOutputStream(pdfHashPreparedToBeSigned);
              SignatureInterface signatureInterface = new SignatureInterface() {
                  @Override
                  public byte[] sign(InputStream content) throws IOException {
                      try {
                          MessageDigest digest = MessageDigest.getInstance(&quot;SHA-256&quot;);
                          byte[] imp = digest.digest(content.toString().getBytes(StandardCharsets.UTF_16));
                          IOUtils.copy(new ByteArrayInputStream(imp), outputStream);
                          return new byte[0];
                      } catch (NoSuchAlgorithmException e) {
                          e.printStackTrace();
                          return new byte[0];
                      }
                  }
              };
      
              SignatureOptions signatureOptions = new SignatureOptions();
              signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 8);
      
              doc.addSignature(signature, signatureInterface, signatureOptions);
      
              if (revisionId != null) {
                  doc.setDocumentId(revisionId);
              } else {
                  doc.setDocumentId(0L);
              }
              doc.saveIncremental(fos);
      
              PendingSignature pendingSignature = new PendingSignature(doc, signature, IOUtils.toByteArray(new FileInputStream(pdfHashPreparedToBeSigned)));
              return pendingSignature;
          } catch (IOException | ParseException e) {
              throw new IOException(e);
          } finally {
              fos.close();
              pdf2sign.close();
              FileUtils.deleteQuietly(pdfFileToSigned);
              FileUtils.deleteQuietly(pdfPreparedToBeSigned);
              FileUtils.deleteQuietly(pdfHashPreparedToBeSigned);
          }
       }
      
      //Gets called when the api returns sucess with the signedbytes
      public static File insertHashToPdf(PendingSignature pendingSignature, final byte[] signedBytes) throws IOException {
      
          File outputDocument = createTempFile(&quot;chars&quot;, &quot;&quot; + Calendar.getInstance().getTimeInMillis());
      
          FileOutputStream fos = new FileOutputStream(outputDocument);
      
          PDDocument doc = pendingSignature.getPdfDocument();
          PDSignature pdfSignature = pendingSignature.getPdfSignature();
      
          //Produces signature that it invalid because it was altered or manipulated
          pdfSignature.setContents(signedBytes);
      
          doc.saveIncremental(fos);
      
          if (fos != null) {
              fos.close();
          }
      
          return outputDocument;
      }
  </code></pre>
  <p>Signature looks like that:</p>
  <p><a href="https://i.stack.imgur.com/wHR60.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/wHR60.png" alt="InvalidSignature" /></a></p>
  <p>Does anybody know how to insert this signature hash into the whole PDDocument without invalidating it?</p>