Avatar
Michal Pflegshörl:

Ahoj,
potřebuji do PDF vložit certifikované časové razítko. Mám kód uvedený níže. Získání časového razítka metodou DejCasoveRazitko proběhne bez chyby. Program spadne na řádku

Array.Copy(pk, 0, outc, 0, pk.Length);

protože csize=4096 ale pk.Length=8775.

Co s tím?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Asn1.Cmp;
using Org.BouncyCastle.Asn1.X509;
using System.Xml;

namespace PodepsaniPDF
{
    public partial class Form_PodepsaniPDF : Form
    {
        private const String ID_TIME_STAMP_TOKEN = "1.2.840.113549.1.9.16.2.14"; // RFC 3161 id-aa-timeStampToken

        public Form_PodepsaniPDF()
        {
            InitializeComponent();
        }

        private void Form_PodepsaniPDF_Load(object sender, EventArgs e)
        {
            comboBoxCertifikat.Items.Clear();
            System.Security.Cryptography.X509Certificates.X509Store store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser);
            store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
            System.Security.Cryptography.X509Certificates.X509Certificate2Collection storecollection = (System.Security.Cryptography.X509Certificates.X509Certificate2Collection)store.Certificates;
            foreach (System.Security.Cryptography.X509Certificates.X509Certificate2 x509 in storecollection)
            {
                if(Convert.ToDateTime(x509.GetExpirationDateString()) > DateTime.Now)
                    comboBoxCertifikat.Items.Add(x509.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.SimpleName, false) + "; " + x509.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.DnsName, true) + " #" + x509.Thumbprint);
            }
            if (comboBoxCertifikat.Items.Count > 0) comboBoxCertifikat.SelectedIndex = 0;
        }

        private void button_Zdroj_Click(object sender, EventArgs e)
        {
            OpenFileDialog VyberSouboru = new OpenFileDialog();
            VyberSouboru.Title = "Výběr PDF souboru";
            VyberSouboru.Multiselect = false;
            VyberSouboru.Filter = "Soubor PDF | *.pdf";
            if (VyberSouboru.ShowDialog() == DialogResult.OK)
            {
                textBox_Zdroj.Text = VyberSouboru.FileName;
                textBox_Cil.Text = VyberSouboru.FileName.Replace(".pdf", "_Spodpisem.pdf");
            }
        }

        private void button_Cil_Click(object sender, EventArgs e)
        {
            SaveFileDialog VyberSouboru = new SaveFileDialog();
            VyberSouboru.Title = "Uložení PDF souboru";
            VyberSouboru.Filter = "Soubor PDF | *.pdf";
            VyberSouboru.FileName = textBox_Cil.Text;
            if (VyberSouboru.ShowDialog() == DialogResult.OK)
            {
                textBox_Cil.Text = VyberSouboru.FileName;
            }
        }

        private void buttonZpracovat_Click(object sender, EventArgs e)
        {
            try
            {
                File.Copy(textBox_Zdroj.Text, textBox_Cil.Text, true);
                PodepisPdfCer(textBox_Cil.Text, comboBoxCertifikat.Text.Substring(comboBoxCertifikat.Text.LastIndexOf('#') + 1));

                MessageBox.Show("Hotovo");
            }
            catch (Exception Chyba)
            {
                MessageBox.Show("Dokument se nepodařilo podepsat!\n\n" + Chyba.Message, "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void PodepisPdfCer(string SouborPDF, string OtiskCertifikatu)
        {
            //http://www.dotnetportal.cz/blogy/15/Null-Reference-Exception/5250/Digitalni-podepisovani-PDF-souboru-v-C-cast-2

            //Get certificate
            //Open the currently logged-in user certificate store
            var store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser);
            store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);

            string thumbprint = OtiskCertifikatu.Replace(" ", "").ToUpperInvariant();
            if (thumbprint[0] == 8206)
            {
                thumbprint = thumbprint.Substring(1);
            }

            //Select a certificate from the certificate store
            var certs = store.Certificates.Find(System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint, thumbprint, true);
            store.Close();

            //Verify that a certificate exists
            if (certs.Count == 0)
            {
                MessageBox.Show("Nelze najít určený certifikát v Current user certificate store!", "Sign PDF", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            //Open Pdf document
            byte[] pdfData = File.ReadAllBytes(SouborPDF);

            //Sign the PDF document
            using (MemoryStream stream = new MemoryStream())
            {
                var reader = new PdfReader(pdfData);
                var stp = PdfStamper.CreateSignature(reader, stream, '\0');
                var sap = stp.SignatureAppearance;

                //Protect certain features of the document
                stp.SetEncryption(null,
                    Guid.NewGuid().ToByteArray(), //random password
                    PdfWriter.ALLOW_PRINTING | PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_SCREENREADERS,
                    PdfWriter.ENCRYPTION_AES_256);

                //Get certificate chain
                var cp = new Org.BouncyCastle.X509.X509CertificateParser();
                var certChain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(certs[0].RawData) };

                sap.SetCrypto(null, certChain, null, PdfSignatureAppearance.WINCER_SIGNED);

                //Set signature appearance
                BaseFont helvetica = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, BaseFont.EMBEDDED);
                iTextSharp.text.Font font = new iTextSharp.text.Font(helvetica, 12, iTextSharp.text.Font.NORMAL);
                sap.Layer2Font = font;
                //sap.SetVisibleSignature(new iTextSharp.text.Rectangle(415, 100, 585, 40), 1, null);

                var dic = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
                //Set some stuff in the signature dictionary.
                dic.Date = new PdfDate(sap.SignDate);
                dic.Name = certs[0].Subject;    //Certificate name
                if (sap.Reason != null)
                {
                    dic.Reason = sap.Reason;
                }
                if (sap.Location != null)
                {
                    dic.Location = sap.Location;
                }

                //Set the crypto dictionary
                sap.CryptoDictionary = dic;

                //Set the size of the certificates and signature.
                int csize = 4096; //Size of the signature - 4K

                //Reserve some space for certs and signatures
                var reservedSpace = new Dictionary<PdfName, int>();
                reservedSpace[PdfName.CONTENTS] = csize * 2 + 2; //*2 because binary data is stored as hex strings. +2 for end of field
                sap.PreClose(reservedSpace);    //Actually reserve it

                //Build the signature
                //System.Security.Cryptography.HashAlgorithm sha = new System.Security.Cryptography.SHA1CryptoServiceProvider();
                System.Security.Cryptography.HashAlgorithm sha = new System.Security.Cryptography.SHA256CryptoServiceProvider();

                var sapStream = sap.GetRangeStream();
                int read = 0;
                byte[] buff = new byte[8192];
                while ((read = sapStream.Read(buff, 0, 8192)) > 0)
                {
                    sha.TransformBlock(buff, 0, read, buff, 0);
                }
                sha.TransformFinalBlock(buff, 0, 0);

                //Place message in a ContentInfo object. This is required to build a SignedCms object.
                System.Security.Cryptography.Pkcs.ContentInfo contentInfo = new System.Security.Cryptography.Pkcs.ContentInfo(sha.Hash);

                //Instantiate SignedCms object with the ContentInfo above.
                //Has default SubjectIdentifierType IssuerAndSerialNumber.
                System.Security.Cryptography.Pkcs.SignedCms signedCms = new System.Security.Cryptography.Pkcs.SignedCms(contentInfo, false);

                //Formulate a CmsSigner object for the signer.
                System.Security.Cryptography.Pkcs.CmsSigner cmsSigner = new System.Security.Cryptography.Pkcs.CmsSigner(certs[0]);  //First cert in the chain is the signer cert

                //Do the whole certificate chain. This way intermediate certificates get sent across as well.
                cmsSigner.IncludeOption = System.Security.Cryptography.X509Certificates.X509IncludeOption.ExcludeRoot;

                //Časové razítko z TSA
                if (!String.IsNullOrEmpty(textBox_ServerCasovehoRazitka.Text.Trim()))
                {
                    try
                    {
                        System.Security.Cryptography.AsnEncodedData timeData = new System.Security.Cryptography.Pkcs.Pkcs9AttributeObject(ID_TIME_STAMP_TOKEN,
                                                                                                                                          DejCasoveRazitko(stream.GetBuffer(), textBox_ServerCasovehoRazitka.Text)
                                                                                                                                         );
                        cmsSigner.UnsignedAttributes.Add(timeData);
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show("Chyba TSA!\n" + e.Message);
                    }
                }

                //Sign the CMS/PKCS #7 message. The second argument is needed to ask for the pin.
                signedCms.ComputeSignature(cmsSigner, false);

                //Encode the CMS/PKCS #7 message.
                byte[] pk = signedCms.Encode();

                //Put the certs and signature into the reserved buffer
                byte[] outc = new byte[csize];
                Array.Copy(pk, 0, outc, 0, pk.Length);

                //Put the reserved buffer into the reserved space
                PdfDictionary certificateDictionary = new PdfDictionary();
                certificateDictionary.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));

                //Write the signature
                sap.Close(certificateDictionary);

                //Close the stamper and save it
                stp.Close();

                reader.Close();

                byte[] signedData = stream.GetBuffer();
                File.WriteAllBytes(SouborPDF, signedData);
            }
        }

        static protected byte[] DejCasoveRazitko(byte[] PDF, string ServerTSA)
        {
            System.Security.Cryptography.SHA256Managed hashString = new System.Security.Cryptography.SHA256Managed();
            string hex = "";

            var hashValue = hashString.ComputeHash(PDF);
            foreach (byte x in hashValue)
            {
                hex += String.Format("{0:x2}", x);
            }

// VSTUPEM je hash dokumentu, pro který se razítko vyžaduje
            NotservisTSA.GetTimeStampRequest Request = new NotservisTSA.GetTimeStampRequest(new NotservisTSA.GetTimeStampRequestBody(hex, 0));

            NotservisTSA.Print2PDF_WebServiceSoap Soap = new NotservisTSA.Print2PDF_WebServiceSoapClient();

            ((NotservisTSA.Print2PDF_WebServiceSoapClient)Soap).Endpoint.Address = new System.ServiceModel.EndpointAddress(new Uri(ServerTSA + "/Default.asmx"));

// VÝSTUPEM je zakódované časové razítko (GetTimeStampResult - BASE-64 v kódované struktuře TimeStampResp, viz RFC 3161) a návratová hodnota s případným popisem chyby.
            PodepsaniPDF.NotservisTSA.GetTimeStampResponse Response = Soap.GetTimeStamp(Request);
            byte[] responseBytes = Encoding.ASCII.GetBytes(Response.Body.GetTimeStampResult);

            if(!String.IsNullOrEmpty(Response.Body.Error))
                MessageBox.Show(Response.Body.Error, "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);

            string base64String = Encoding.UTF8.GetString(responseBytes, 0, responseBytes.Length);
            return Convert.FromBase64String(base64String);
        }
    }
}
 
Odpovědět 1. září 8:09
Avatar
jan.pichl
Člen
Avatar
jan.pichl:
int csize = 8775;
 
Nahoru Odpovědět 20. září 15:57
Avatar
Odpovídá na jan.pichl
Michal Pflegshörl:

Takhle to proběhne bez chyby ale PDF hlásí, že podpis je neplatný a dokument byl od podepsání změněn nebo poškozen.

 
Nahoru Odpovědět 21. září 8:28
Avatar
Michal Pflegshörl:

Tak změna.
S

int csize = 8775;

proběhne bez chyby ale v PDF je stále uvedeno, že datum a čas jsou z počítače autora podpisu. Nejsem si jistý jestli metodě DejCasoveRazitko předávám správně HASH dokumentu.

Editováno 23. září 9:10
 
Nahoru Odpovědět 23. září 9:10
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.