title: How do I sign a PDF with a Smart Card in a web context using iText?
body: <p>Read through the following references:</p>
  
  <ul>
  <li><a href="http://pages.itextpdf.com/ebook-digital-signatures-for-pdf.html" rel="nofollow noreferrer">iText Digital signature white paper</a>, and <a href="http://sourceforge.net/p/itextsharp/code/HEAD/tree/tutorial/signatures/" rel="nofollow noreferrer">C# examples</a>. (specifically <a href="http://sourceforge.net/p/itextsharp/code/HEAD/tree/tutorial/signatures/chapter4/" rel="nofollow noreferrer">chapter 4</a>) For those interested, another <a href="https://security.stackexchange.com/questions/35121/a-standard-way-to-manually-add-a-digital-signature-to-a-pdf-file/35131">great and concise summary</a> of the PDF signing process.  </li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa375732(v=vs.85).aspx" rel="nofollow noreferrer">CAPICOM documentation</a>.</li>
  <li>Online examples / questions here and on iText mailing list archives, such as <a href="https://stackoverflow.com/questions/26869058/invalid-pdf-digital-signature-in-winrt-app">here</a> and <a href="http://itext.2136553.n4.nabble.com/Sign-and-PDF-with-SmartCard-and-web-browser-only-tp4319344p4660327.html" rel="nofollow noreferrer">here</a>.</li>
  </ul>
  
  <p>Hashing code:</p>
  
  <pre><code>BouncyCastle.X509Certificate[] chain = Utils.GetSignerCertChain();
  reader = Utils.GetReader();
  MemoryStream stream = new MemoryStream();
  using (var stamper = PdfStamper.CreateSignature(reader, stream, '\0'))
  {
      PdfSignatureAppearance sap = stamper.SignatureAppearance;
      sap.SetVisibleSignature(
          new Rectangle(36, 740, 144, 770),
          reader.NumberOfPages,
          "SignatureField"
      );
      sap.Certificate = chain[0];
      sap.SignDate = DateTime.Now;
      sap.Reason = "testing web context signatures";
  
      PdfSignature pdfSignature = new PdfSignature(
          PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED
      );
      pdfSignature.Date = new PdfDate(sap.SignDate);
      pdfSignature.Reason = sap.Reason;
      sap.CryptoDictionary = pdfSignature;
  
      Dictionary&lt;PdfName, int&gt; exclusionSizes = new Dictionary&lt;PdfName, int&gt;();
      exclusionSizes.Add(PdfName.CONTENTS, SIG_BUFFER * 2 + 2);
      sap.PreClose(exclusionSizes);
  
      Stream sapStream = sap.GetRangeStream();
      byte[] hash = DigestAlgorithms.Digest(
          sapStream,
          DigestAlgorithms.SHA256
      );
  
  // is this needed?
      PdfPKCS7 sgn = new PdfPKCS7(
          null, chain, DigestAlgorithms.SHA256, true
      );
      byte[] preSigned = sgn.getAuthenticatedAttributeBytes(
          hash, sap.SignDate, null, null, CryptoStandard.CMS
      );
  
      var hashedValue = Convert.ToBase64String(preSigned);
  }
  </code></pre>
  
  <p>Just a simple test - a dummy Pdf document is created on initial page request, hash is calculated, and put in a hidden input field Base64 encoded. (the <code>hashedValue</code> above)</p>
  
  <p>Then use CAPICOM on client-side to POST the form and get user's signed response:</p>
  
  <pre><code>PdfSignatureAppearance sap = (PdfSignatureAppearance)TempData[TEMPDATA_SAP];
  PdfPKCS7 sgn = (PdfPKCS7)TempData[TEMPDATA_PKCS7];
  stream = (MemoryStream)TempData[TEMPDATA_STREAM];
  byte[] hash = (byte[])TempData[TEMPDATA_HASH];
  
  byte[] originalText = (Encoding.Unicode.GetBytes(hashValue));
  // Oid algorithm verified on client side
  ContentInfo content = new ContentInfo(new Oid("RSA"), originalText);
  
  SignedCms cms = new SignedCms(content, true);
  cms.Decode(Convert.FromBase64String(signedValue));
  // CheckSignature does not throw exception
  cms.CheckSignature(true);
  var encodedSignature = cms.Encode();
  
  /* tried this too, but no effect on result
  sgn.SetExternalDigest(
      Convert.FromBase64String(signedValue),
      null,
      "RSA"
  );
  byte[] encodedSignature = sgn.GetEncodedPKCS7(
      hash, sap.SignDate, null, null, null, CryptoStandard.CMS
  );
  */
  byte[] paddedSignature = new byte[SIG_BUFFER];
  Array.Copy(encodedSignature, 0, paddedSignature, 0, encodedSignature.Length);
  var pdfDictionary = new PdfDictionary();
  pdfDictionary.Put(
      PdfName.CONTENTS,
      new PdfString(paddedSignature).SetHexWriting(true)
  );
  sap.Close(pdfDictionary);
  </code></pre>
  
  <p>So right now I'm not sure if I'm messing up hashing part, signature part, or both. In signature code snippet above and in client code (not shown) I'm calling what I think is signature verification code, but that may be wrong too, since this is a first for me. Get the infamous "<strong><em>Document has been altered or corrupted since it was signed</em></strong>" invalid signature message when opening the PDF. </p>
  
  <p>Client-side code (not authored by me) <a href="https://github.com/Glamdring/js-signer" rel="nofollow noreferrer">can be found here</a>. Source has a variable naming error, which was corrected. For reference, CAPICOM documentation says signed response <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa387722%28v=vs.85%29.aspx" rel="nofollow noreferrer">is in PKCS#7 format</a>.</p>
  
  <p><strong>EDIT 2015-03-12</strong>:</p>
  
  <p>After some nice pointers from @mkl and more research, it seems CAPICOM is <em>practicably</em> unusable in this scenario. Although not documented clearly, (what else is new?) according to <a href="http://www.codeproject.com/Articles/9691/Using-CAPICOM-in-NET-for-Digital-Signatures-with-A" rel="nofollow noreferrer">here</a> and <a href="https://www.eldos.com/forum/read.php?TID=2242&amp;MID=12294#message12294" rel="nofollow noreferrer">here</a>, CAPICOM expects a utf16 string (<code>Encoding.Unicode</code> in .NET) as input to create a digital signature. From there it either pads or truncates (depending which source in previous sentence in correct) <em>whatever data it receives if the length is an odd number</em>. I.e. signature creation will <strong>ALWAYS FAIL</strong> if the <code>Stream</code> returned by <a href="http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfSignatureAppearance.html#getRangeStream%28%29" rel="nofollow noreferrer">PdfSignatureAppearance.GetRangeStream()</a> has a length that is an odd number. Maybe I should create an <em>I'm lucky</em> option: sign if ranged stream length is even, and throw an <code>InvalidOperationException</code> if odd. (sad attempt at humor)</p>
  
  <p><s>For reference, here's the test project</s>.</p>
  
  <p><strong>EDIT 2015-03-25</strong>:</p>
  
  <p>To close the loop on this, <a href="https://github.com/kuujinbo/kuujinbo.StackOverflow.iTextSharp.MVC.PdfSigner" rel="nofollow noreferrer"><strong>here's a link to a VS 2013 ASP.NET MVC project</strong></a>. May not the be best way, but it <strong>does</strong> provide a <strong>fully working solution</strong> to the problem. Because of CAPICOM's strange and inflexible signing implementation, as described above, knew a possible solution would potentially require a second pass and a way to inject an extra byte if the return value of <a href="http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfSignatureAppearance.html#getRangeStream%28%29" rel="nofollow noreferrer">PdfSignatureAppearance.GetRangeStream()</a> (again, <code>Stream.Length</code>) is an odd number. I was going to try the long and hard way by padding the PDF content, but luckily a co-worker found it was much easier to pad <code>PdfSignatureAppearance.Reason</code>. Requiring a second pass to do something with iText[Sharp], is not unprecedented - e.g. <a href="http://kuujinbo.info/iTextInAction2Ed/index.aspx?ch=Chapter06&amp;ex=TwoPasses" rel="nofollow noreferrer">adding page x of y for a document page header/footer</a>.</p>