Как применить отложенную (CMS) подпись к документу PDF с помощью ITextSharp - PullRequest
0 голосов
/ 06 февраля 2019

Мне нужно добавить криптографическую подпись в документ PDF с помощью CMS.В конце концов, коммерческая служба будет использоваться для создания подписи на основе хеша из PDF-документа.В данный момент я пытаюсь создать такой документ, используя самозаверяющие сертификаты.

Для этого я использую библиотеку ITextSharp версии 5.5.13.Я создал простое консольное приложение, которое по существу выполняет следующие действия:

  1. Создает хеш и подписанный подготовленный документ PDF на основе существующего документа PDF.
  2. Создает подпись SHA256 изхэш с использованием сертификата X509
  3. Добавьте подпись к документу pdf, созданному на шаге 1

Уже работает следующее:

  1. Создайте подготовленный документ PDF
  2. Создание хеш-значения
  3. Создание подписи SHA256

Но я не могу применить подпись к подготовленному PDF-документу.

Я загружаю подготовленный pdf, но внешний вид подписи равен нулю.Мне нужно добавить криптографическую подпись в PDF-документ с помощью CMS.В конце концов, коммерческая служба будет использоваться для создания подписи на основе хеша из PDF-документа.В данный момент я пытаюсь создать такой документ, используя самозаверяющие сертификаты.

Но я не могу применить подпись к подготовленному PDF-документу.Я загружаю подготовленный PDF, но внешний вид подписи равен нулю.

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);
                }
            }
        }


    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...