如何使用ITextSharp

时间:2019-02-06 13:25:35

标签: c# .net itext

我需要使用CMS将加密签名添加到pdf文档中。 最终,商业服务将用于基于pdf文档中的哈希值创建签名。 目前,我正在尝试使用自签名证书创建此类文档。

我正在为此使用ITextSharp库5.5.13版本。 我确实创建了一个简单的控制台应用程序,该应用程序实际上执行以下操作:

  1. 基于现有的pdf文档创建哈希和签名准备好的pdf文档。
  2. 使用X509Certificate从哈希创建SHA256签名
  3. 将签名添加到步骤1中创建的pdf文档中

以下内容已经有效:

  1. 创建准备好的pdf文档
  2. 创建哈希值
  3. 创建SHA256签名

但是我无法将签名应用于准备好的pdf文档。

我正在加载准备的pdf,但SignatureAppearance为空。 我需要使用CMS将加密签名添加到pdf文档中。 最终,商业服务将用于基于pdf文档中的哈希值创建签名。 目前,我正在尝试使用自签名证书创建此类文档。

但是我无法将签名应用于准备好的pdf文档。 我正在加载准备好的pdf,但SignatureAppearance为空。

public void AddSignatureToPreparedPdf(byte[] signature)
{
    //read the prepared pdf document (This document is ready to be signed)
    using (PdfReader reader = new PdfReader(PreparedForSigningDocumentPath))
    {
        //The filestream into which the newly signed document must be written.
        using (FileStream fileStream = File.OpenWrite(SignedPdfPath))
        {
            PdfStamper stamper = new PdfStamper(reader, fileStream, '\0');

            var appearance = stamper.SignatureAppearance;
            var pdfDictionary = new PdfDictionary();
            var paddedSignature = new byte[SIGNATURE_ESTIMATED_SIZE];

            Array.Copy(signature, 0, paddedSignature, 0, signature.Length);
            pdfDictionary.Put(PdfName.CONTENTS, new PdfString(paddedSignature).SetHexWriting(true));

            Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
            exclusionSizes.Add(PdfName.CONTENTS, (SIGNATURE_ESTIMATED_SIZE * 2) + 2);

            appearance.PreClose(exclusionSizes);
            appearance.Close(pdfDictionary);
        }
    }
}

下面的示例包含一个带有所有必需代码才能运行的类。 要运行它,需要执行以下任务:更改pdf文件的路径并创建一个自签名证书,并更改代码中的路径和密码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using com.itextpdf.text.pdf.security;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Pkcs;

namespace MobileIDTestConsole
{
    class Program
    {
        static void Main(string[] args)
        {

            string unsignedDocumentPath = @"C:\temp\UnsignedDocument.pdf";
            string signedDocumentPath = @"C:\temp\SignedDocument.pdf";
            string preparedForSigningDocumentPath = @"C:\temp\SigningPreparedDocument.pdf";

            ITextTest iTextTest = new ITextTest(unsignedDocumentPath, signedDocumentPath, preparedForSigningDocumentPath);

            //Create the new sign-ready pdf, and returns the hash value
            iTextTest.PreparePdfForSigning();

            //Sign the hash value
            // >> This is eventually done by a dedicated service <<
            var signature = iTextTest.SignHash();

            //Add the newly created signature to the previously created document
            iTextTest.AddSignatureToPreparedPdf(signature);
        }
    }


    public class ITextTest
    {
        //Space allocated in the new pdf document for the signature
        private int SIGNATURE_ESTIMATED_SIZE = 8192;

        //Path to my test certificate (including the private key). Needs to be replaced with certificate from cert store
        private string CertificatePath = @"C:\project\tech\mobileidtest\ws16.pfx";
        private string CertificatePassword = "xxx";

        private readonly string UnsignedPdfPath;
        private readonly string PreparedForSigningDocumentPath;
        private readonly string SignedPdfPath;

        public string PdfHash { get; set; }

        public ITextTest(string UnsignedPdfPath, string SignedPdfPath, string preparedForSigningDocumentPath)
        {
            this.UnsignedPdfPath = UnsignedPdfPath;
            this.SignedPdfPath = SignedPdfPath;
            this.PreparedForSigningDocumentPath = preparedForSigningDocumentPath;
        }


        /// <summary>
        /// Prepare a pdf document for signing
        /// * Add a signature placeholder
        /// * Create a hash which will be signed later
        /// * Save the prepared pdf document
        /// </summary>
        public void PreparePdfForSigning()
        {
            using (PdfReader reader = new PdfReader(UnsignedPdfPath))
            {
                FileStream os = File.OpenWrite(PreparedForSigningDocumentPath);
                PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
                PdfSignatureAppearance appearance = stamper.SignatureAppearance;

                appearance.SetVisibleSignature(new Rectangle(500, 150, 400, 200), 1, "SignatureFieldName1");
                appearance.SignDate = DateTime.Now;
                appearance.Reason = "My signing reason";
                appearance.Location = "Somewhere";
                appearance.Contact = "Foo Bar";
                StringBuilder buf = new StringBuilder();
                buf.Append("Digitally signed by");
                buf.Append("\n");
                buf.Append("Foo Bar");
                buf.Append("\n");
                buf.Append("Date: " + appearance.SignDate);
                appearance.Layer2Text = buf.ToString();
                appearance.Acro6Layers = true;
                appearance.CertificationLevel = 0;
                PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
                {
                    Date = new PdfDate(appearance.SignDate),
                    Name = "Foo Bar"
                };
                dic.Reason = appearance.Reason;
                dic.Location = appearance.Location;
                dic.Contact = appearance.Contact;

                appearance.CryptoDictionary = dic;
                Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
                exclusionSizes.Add(PdfName.CONTENTS, (SIGNATURE_ESTIMATED_SIZE * 2) + 2);

                IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
                MakeSignature.SignExternalContainer(appearance, external, SIGNATURE_ESTIMATED_SIZE);

                //  appearance.PreClose(exclusionSizes); >> not sure whether this is required
                HashAlgorithm sha = new SHA256CryptoServiceProvider();
                Stream s = appearance.GetRangeStream();
                int read = 0;
                byte[] buff = new byte[0x2000];
                while ((read = s.Read(buff, 0, 0x2000)) > 0)
                {
                    sha.TransformBlock(buff, 0, read, buff, 0);
                }
                sha.TransformFinalBlock(buff, 0, 0);

                StringBuilder hex = new StringBuilder(sha.Hash.Length * 2);
                foreach (byte b in sha.Hash)
                    hex.AppendFormat("{0:x2}", b);

                PdfHash = hex.ToString();
            }
        }




        /// <summary>
        /// Create a SHA256 signature from a given hash value
        /// It uses a self signed certificate
        /// Once this runs properly, this task will be performed by a secure signing service
        /// </summary>
        /// <returns>A SHA256 signature</returns>
        public byte[] SignHash()
        {
            if (string.IsNullOrEmpty(PdfHash))
                throw new Exception("no hash available");

            byte[] hash = StringToByteArray(PdfHash);
            FileStream fs = new FileStream(CertificatePath, FileMode.Open);
            Pkcs12Store store = new Pkcs12Store(fs, CertificatePassword.ToCharArray());
            String alias = "";
            foreach (string al in store.Aliases)
                if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
                {
                    alias = al;
                    break;
                }
            AsymmetricKeyEntry pk = store.GetKey(alias);
            X509CertificateEntry[] chain = store.GetCertificateChain(alias);
            List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
            foreach (X509CertificateEntry en in chain)
            {
                c.Add(en.Certificate);
            }
            PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
            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());
            return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);

        }


        /// <summary>
        /// Creates a byte array from a hex string
        /// </summary>
        /// <param name="hex"></param>
        /// <returns></returns>
        private static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                .Where(x => x % 2 == 0)
                .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                .ToArray();
        }


        /// <summary>
        /// Adds a signature to the prepared pdf document
        /// </summary>
        /// <param name="signature"></param>
        public void AddSignatureToPreparedPdf(byte[] signature)
        {
            //read the prepared pdf document (This document is ready to be signed)
            using (PdfReader reader = new PdfReader(PreparedForSigningDocumentPath))
            {
                //The filestream into which the newly signed document must be written.
                using (FileStream fileStream = File.OpenWrite(SignedPdfPath))
                {
                    PdfStamper stamper = new PdfStamper(reader, fileStream, '\0');

                    var appearance = stamper.SignatureAppearance;
                    var pdfDictionary = new PdfDictionary();
                    var paddedSignature = new byte[SIGNATURE_ESTIMATED_SIZE];

                    Array.Copy(signature, 0, paddedSignature, 0, signature.Length);
                    pdfDictionary.Put(PdfName.CONTENTS, new PdfString(paddedSignature).SetHexWriting(true));

                    Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
                    exclusionSizes.Add(PdfName.CONTENTS, (SIGNATURE_ESTIMATED_SIZE * 2) + 2);

                    appearance.PreClose(exclusionSizes);
                    appearance.Close(pdfDictionary);
                }
            }
        }


    }
}

0 个答案:

没有答案