/** * Signs a document with a PAdES-LTV Timestamp. The document is closed at the end. * @param sap the signature appearance * @param tsa the timestamp generator * @param signatureName the signature name or null to have a name generated * automatically * @throws Exception */ public static void Timestamp(PdfSignatureAppearance sap, ITSAClient tsa, String signatureName) { int contentEstimated = tsa.GetTokenSizeEstimate(); sap.SetVisibleSignature(new Rectangle(0,0,0,0), 1, signatureName); PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ETSI_RFC3161); dic.Put(PdfName.TYPE, PdfName.DOCTIMESTAMP); sap.CryptoDictionary = dic; Dictionary<PdfName,int> exc = new Dictionary<PdfName,int>(); exc[PdfName.CONTENTS] = contentEstimated * 2 + 2; sap.PreClose(exc); Stream data = sap.RangeStream; IDigest messageDigest = DigestUtilities.GetDigest(tsa.GetDigestAlgorithm()); byte[] buf = new byte[4096]; int n; while ((n = data.Read(buf, 0, buf.Length)) > 0) { messageDigest.BlockUpdate(buf, 0, n); } byte[] tsImprint = new byte[messageDigest.GetDigestSize()]; messageDigest.DoFinal(tsImprint, 0); byte[] tsToken = tsa.GetTimeStampToken(tsImprint); if (contentEstimated + 2 < tsToken.Length) throw new Exception("Not enough space"); byte[] paddedSig = new byte[contentEstimated]; System.Array.Copy(tsToken, 0, paddedSig, 0, tsToken.Length); PdfDictionary dic2 = new PdfDictionary(); dic2.Put(PdfName.CONTENTS, new PdfString(paddedSig).SetHexWriting(true)); sap.Close(dic2); }
private static void SetSigCryptoFromX509(PdfSignatureAppearance sigAppearance, X509Certificate2 card, X509Certificate[] chain) { sigAppearance.SetCrypto(null, chain, null, PdfSignatureAppearance.WINCER_SIGNED); var dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1) { Date = new PdfDate(sigAppearance.SignDate), Name = PdfPKCS7.GetSubjectFields(chain[0]).GetField("CN"), Reason = sigAppearance.Reason, Location = sigAppearance.Location }; sigAppearance.CryptoDictionary = dic; const int csize = 4000; var exc = new Dictionary<PdfName, int> { { PdfName.CONTENTS, csize * 2 + 2 } }; sigAppearance.PreClose(exc); HashAlgorithm sha = new SHA1CryptoServiceProvider(); var s = sigAppearance.RangeStream; int read; var buff = new byte[8192]; while ((read = s.Read(buff, 0, 8192)) > 0) { sha.TransformBlock(buff, 0, read, buff, 0); } sha.TransformFinalBlock(buff, 0, 0); var pk = SignMsg(sha.Hash, card, false); var outc = new byte[csize]; var dic2 = new PdfDictionary(); Array.Copy(pk, 0, outc, 0, pk.Length); dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true)); sigAppearance.Close(dic2); }
/// <summary> /// Prepare the data needed for digital signature. Unfortunately /// CAPICOM's client-side implementation both hashes **AND** signs /// passed in data instead of signing data already hashed, so the /// **entire** PDF content bytes are needed. /// </summary> /// <param name="pdfIn">PDF file contents</param> /// <returns> /// Base64 encoded PDF content bytes client will sign. /// </returns> public string PreSign(byte[] pdfIn) { byte[] pdfRawContent = null; bool isOdd = true; var timeStamp = DateTime.Now; var pdfSignature = new PdfSignature( PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED ); pdfSignature.Date = new PdfDate(timeStamp); var exclusionSizes = new Dictionary<PdfName, int>(); exclusionSizes.Add(PdfName.CONTENTS, EXCLUSION_BUFFER * 2 + 2); PdfReader reader = null; int? signedFields = null; try { var cert = new WebCertificateReader().GetSigningCertificate(); do { ++DataReadCount; reader = new PdfReader(pdfIn); _acroFieldsWorker = new AcroFieldsReader(reader.AcroFields); signedFields = signedFields ?? _acroFieldsWorker.SignedFields(); _memoryStream = new MemoryStream(); var stamper = signedFields == 0 ? PdfStamper.CreateSignature(reader, _memoryStream, '\0') : PdfStamper.CreateSignature(reader, _memoryStream, '\0', null, true) ; _signatureAppearance = stamper.SignatureAppearance; InitSignatureField(stamper); pdfSignature.Reason = Reason; _signatureAppearance.Certificate = cert; _signatureAppearance.SignDate = timeStamp; _signatureAppearance.CryptoDictionary = pdfSignature; _signatureAppearance.PreClose(exclusionSizes); using (Stream sapStream = _signatureAppearance.GetRangeStream()) { using (var ms = new MemoryStream()) { sapStream.CopyTo(ms); pdfRawContent = ms.ToArray(); } // pdfRawContent = StreamHandler.ReadAllBytes(sapStream); // fix CAPICOM's broken implemetation: signature // invalid if sapStream.Length is **ODD** if ((pdfRawContent.Length % 2) == 0) { isOdd = false; } else { Reason += "\0"; } DataSize = sapStream.Length; } // sanity check if (DataReadCount > 2) throw new InvalidOperationException("DataReadCount"); } while (isOdd); } catch { throw; } finally { HttpContext.Current.Session[InstanceLookupKey] = this; if (reader != null) { reader.Dispose(); } } return Convert.ToBase64String(pdfRawContent); }