title: PDF Signature - Embed separatly signed hash
body: <p>I am trying to sign a PDF Document using two web services in two servers. But it is showing "Document has been altered or corrupt since it was signed" in Adobe Reader. Can anybody suggest what is wrong in following code.</p>
  
  <p>PROCEDURE
  1. Web service (WS) on Server A, Generate hash from PDF and sent to WS on Server B for signing.
  2. WS on Server B signs hash.
  3. WS on Server A receives signed hash and Embed in PDF document.</p>
  
  <p>CODE</p>
  
  <p>GENERATE HASH</p>
  
  <pre><code> private PDFHashData generateHash(byte[] content, string userName)
      {
          PdfReader reader = new PdfReader(content);
          MemoryStream ms = new MemoryStream();
  
          PdfStamper stamper = PdfStamper.CreateSignature(reader, ms, '\0');
          PdfSignatureAppearance appearance = stamper.SignatureAppearance;
          appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, signatureFieldName);
          appearance.SignDate = DateTime.Now;
          appearance.Reason = Reason;
          appearance.Location = Location;
          appearance.Contact = Contact;
          StringBuilder buf = new StringBuilder();
          buf.Append("Digitally signed by");
          buf.Append("\n");
          buf.Append(userName);
          buf.Append("\n");
          buf.Append("Date: " + appearance.SignDate);
          appearance.Layer2Text = buf.ToString();
          appearance.Acro6Layers = true;
          appearance.CertificationLevel = 0;
          IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
          MakeSignature.SignExternalContainer(appearance, external, 8192);
  
          byte[] hash = SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
  
          StringBuilder hex = new StringBuilder(hash.Length * 2);
          foreach (byte b in hash)
              hex.AppendFormat("{0:x2}", b);
  
          PDFHashData phData= new PDFHashData();
          phData.Hash = hex.ToString();
          phData.Content = Convert.ToBase64String(ms.ToArray());
          return phData;
      }
  </code></pre>
  
  <p>SIGN HASH</p>
  
  <pre><code>    byte[] StringToByteArray(string hex)
      {
          return Enumerable.Range(0, hex.Length)
                           .Where(x =&gt; x % 2 == 0)
                           .Select(x =&gt; Convert.ToByte(hex.Substring(x, 2), 16))
                           .ToArray();
      }
      private Stream getCertificate()
      {
  // Base 64 byte - PFX file with private key
          return new MemoryStream(Convert.FromBase64String("..................................AgIEAA=="));
      }
  
      protected void Page_Load(object sender, EventArgs e)
      {
          Stream stream = Request.InputStream;
          byte[] buffer = new byte[stream.Length];
          stream.Read(buffer, 0, buffer.Length);
          byte[] hash = StringToByteArray(Encoding.UTF8.GetString(buffer));
  
          Pkcs12Store store = new Pkcs12Store(getCertificate(), "*******".ToCharArray());
          String alias = "";
          foreach (string al in store.Aliases)
              if (store.IsKeyEntry(al) &amp;&amp; store.GetKey(al).Key.IsPrivate)
              {
                  alias = al;
                  break;
              }
          AsymmetricKeyEntry pk = store.GetKey(alias);
          X509CertificateEntry[] chain = store.GetCertificateChain(alias);
          List&lt;Org.BouncyCastle.X509.X509Certificate&gt; c = new List&lt;Org.BouncyCastle.X509.X509Certificate&gt;();
          foreach (X509CertificateEntry en in chain)
          {
              c.Add(en.Certificate);
          }
          PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA1");
          String hashAlgorithm = signature.GetHashAlgorithm();
          PdfPKCS7 sgn = new PdfPKCS7(null, c, hashAlgorithm, false);
          DateTime signingTime = DateTime.Now;
          byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
          byte[] extSignature = signature.Sign(sh);
          sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
  
          Response.Write(Convert.ToBase64String(sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS)));
      }
  </code></pre>
  
  <p>EMBED SIGNATURE TO PDF</p>
  
  <pre><code>private byte[] signPDF(byte[] content, string userName, byte[] pk)
      {
          PdfReader reader = new PdfReader(content);
          MemoryStream os = new MemoryStream();
          IExternalSignatureContainer external = new MyExternalSignatureContainer(pk);
          MakeSignature.SignDeferred(reader, signatureFieldName, os, external);
          return os.ToArray();
      }
  </code></pre>