title: Signing PDF using network HSM and PDFBox
body: <p>I am trying to follow CreateSignature example as a starting point and making changes to it to make it work with our external network based HSM system.</p>
  
  <p>The generated PDF document always complains that the "document has been altered". I am lacking the insight on what should be used to sign. </p>
  
  <p>The following is the implementation of sign() in my CreateSignatureBase.java:</p>
  
  <pre><code>@Override
      public byte[] sign(InputStream content) throws IOException {
  
          // cannot be done private (interface)
          try {
  
              // Certificate chain is acquired at initialization
              List&lt;Certificate&gt; certList = new ArrayList&lt;&gt;();
              certList.addAll(Arrays.asList(certificateChain));
              Store&lt;?&gt; certs = new JcaCertStore(certList);
              org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate.getInstance(certificateChain[0].getEncoded());
  
              CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
  
              //HSMSigner is the class that interacts with the network HSM to get the signature.
              HSMSigner signer = new HSMSigner();
              byte[] input = IOUtil.toByteArray(content);
  
              //SignedHash is a base64-encoded PKCS1 block. see HSMSigner.getSignature() below
              final String signedHash = signer.getSignature(input);
  
              ContentSigner sha1Signer = new ContentSigner() {
  
                  @Override
                  public byte[] getSignature() {
                      return Base64.getDecoder().decode(signedHash);
                  }
  
                  @Override
                  public OutputStream getOutputStream() {
                      return new ByteArrayOutputStream();
                  }
  
                  @Override
                  public AlgorithmIdentifier getAlgorithmIdentifier() {
                      return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WITHRSAENCRYPTION");
                  }
              };
  
              gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, new X509CertificateHolder(cert)));
              gen.addCertificates(certs);
              CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
              CMSSignedData cmsSignedData = gen.generate(msg, true);
  
              byte[] result = cmsSignedData.getEncoded();
  
              return result;
  
          } catch (GeneralSecurityException | CMSException | OperatorCreationException e) {
              throw new IOException(e);
          }
  
      }
  </code></pre>
  
  <p>And the following is the implementation of HSMSigner().getSignature()</p>
  
  <pre><code>public String getSignature(byte [] bytes) {
  
  
          String host = "hsmvip.corp.com";
          int port = 9000;
          SignClient signClient;
          try {
              //initialize the sign client 
              signClient = ///..;
              MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
              messageDigest.update(bytes);
              byte[] outputDigest = messageDigest.digest();
  
              // signature returned by the sign method is a base64-encoded PKCS1 block.
              String signature = signClient.sign(Base16.encodeAsString(outputDigest));
              signClient.close();
              return signature;
          } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
              return null;
          }
  
      }
  </code></pre>
  
  <p>Any help in figuring out what I am doing wrong is greatly appreciated.</p>
  
  <p>A copy of a signed pdf can be found at <a href="https://file.io/2tVvYO" rel="nofollow noreferrer">https://file.io/2tVvYO</a></p>
  
  <p>Thanks in advance!</p>