1 Reply Latest reply on Sep 5, 2016 5:49 AM by Bernd Alheit

    C# - Certifikované časové razítko do PDF

    Flexam

      Ahoj všem,

      potřeboval bych poradit s vložením certifikovaného časového razítka do PDF.

      Níže uvedený zdrojový kód, který spadne na řádku Array.Copy(pk, 0, outc, 0, pk.Length); protože outc.Length = 4096, ale pk.Length = 7541. Metoda DejCasoveRazitko funguje bez chyby.

       

       

              private const String ID_TIME_STAMP_TOKEN = "1.2.840.113549.1.9.16.2.14"; // RFC 3161 id-aa-timeStampToken

       

              public void PodepisPdfCer(string SouborPDF, string OtiskCertifikatu)

              {

                  //http://www.dotnetportal.cz/blogy/15/Null-Reference-Exception/5250/Digitalni-podepisovani-P DF-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.X509 Certificates.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.FindBy Thumbprint, 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.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"));

       

                  PodepsaniPDF.NotservisTSA.GetTimeStampResponse Response = Soap.GetTimeStamp(Request);

                 

                  // 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.

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

              }