How do I sign a PDF document using a certificate from the Windows Cert Store?

匿名 (未验证) 提交于 2019-12-03 02:25:01

问题:

I need to sign a PDF document using a certificate that exists in the Windows Certificate Store. I have been digging around all day trying to figure it out, and I am so close yet so far away.

All that is missing is this: How do I get an IExternalSignature object to sign the PDF file with?

Rahul Singla has written a beautiful example of how to sign a PDF document using the new iText 5.3.0 API - as long as you can access a .pfx file sitting around on your PC somewhere.

There is a previous question on signing using a certificate from the Windows Cert Store, except it was using a version of the API where SetCrypto still exists, and the signature was apparently optional. In iText 5.3.0, the API has changed, and SetCrypto is no longer a thing.

Here's what I have so far (comments added for posterity, since this might be the most complete and recent version of how to do this on the 'net):

using iTextSharp.text.pdf; using iTextSharp.text.pdf.security; using BcX509 = Org.BouncyCastle.X509; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Crypto; using DotNetUtils = Org.BouncyCastle.Security.DotNetUtilities;  ...  // Set up the PDF IO PdfReader reader = new PdfReader(@"some\dir\SomeTemplate.pdf"); PdfStamper stamper = PdfStamper.CreateSignature(reader,     new FileStream(@"some\dir\SignedPdf.pdf", FileMode.Create), '\0'); PdfSignatureAppearance sap = stamper.SignatureAppearance;  sap.Reason = "For no apparent raisin"; sap.Location = "...";  // Acquire certificate chain var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine); certStore.Open(OpenFlags.ReadOnly);  X509CertificateCollection certCollection =     certStore.Certificates.Find(X509FindType.FindBySubjectName,     "My.Cert.Subject", true); X509Certificate cert = certCollection[0]; // iTextSharp needs this cert as a BouncyCastle X509 object; this converts it. BcX509.X509Certificate bcCert = DotNetUtils.FromX509Certificate(cert); var chain = new List<BcX509.X509Certificate> { bcCert }; certStore.Close();  // Ok, that's the certificate chain done. Now how do I get the PKS? IExternalSignature signature = null; /* ??? */  // Sign the PDF file and finish up. MakeSignature.SignDetached(sap, signature, chain, // the important stuff     null, null, null, 0, CryptoStandard.CMS); stamper.Close(); 

As you can see: I have everything but the signature, and I'm stumped as to how I should obtain it!

回答1:

X509Certificate cert = certCollection[0]; // Your code X509Certificate2 signatureCert = new X509Certificate2(cert);  var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private; 

If you have the pk, which can get as above, you create an IExternalSignature as follows:

IExternalSignature es = new PrivateKeySignature(pk, "SHA-256"); 

You may also find the following articles of use:



回答2:

Please download the book on PDF and digital signatures. You'll find a Java example on how to sign using the Windows Certificate Store in Chapter 3. As you can see, you need the Windows-MY keystore.

Now go to the repository where we've published the C# port of these examples. Look for C3_11_SignWithToken.cs.

X509Store x509Store = new X509Store("My"); x509Store.Open(OpenFlags.ReadOnly); X509Certificate2Collection certificates = x509Store.Certificates; IList<X509Certificate> chain = new List<X509Certificate>(); X509Certificate2 pk = null; if (certificates.Count > 0) {     X509Certificate2Enumerator certificatesEn = certificates.GetEnumerator();     certificatesEn.MoveNext();     pk = certificatesEn.Current;     X509Chain x509chain = new X509Chain();     x509chain.Build(pk);     foreach (X509ChainElement x509ChainElement in x509chain.ChainElements) {         chain.Add(DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate));     } } x509Store.Close(); 

If I understand correctly chain and pk are the variable you were looking for;



回答3:

public byte[] SignPdf(byte[] pdf) {     using (MemoryStream output = new MemoryStream())     {         //get certificate from path         X509Certificate2 cert1 = new X509Certificate2(@"C:\temp\certtemp.pfx", "12345", X509KeyStorageFlags.Exportable);         //get private key to sign pdf         var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert1.PrivateKey).Private;         // convert the type to be used at .SetCrypt();          Org.BouncyCastle.X509.X509Certificate bcCert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(cert1);         // get the pdf u want to sign         PdfReader pdfReader = new PdfReader(pdf);          PdfStamper stamper = PdfStamper.CreateSignature(pdfReader, output, '\0');         PdfSignatureAppearance pdfSignatureAppearance = stamper.SignatureAppearance;         //.SetCrypt(); sign the pdf         pdfSignatureAppearance.SetCrypto(pk, new Org.BouncyCastle.X509.X509Certificate[] { bcCert }, null, PdfSignatureAppearance.WINCER_SIGNED);          pdfSignatureAppearance.Reason = "Este documento está assinado digitalmente pelo Estado Portugues";         pdfSignatureAppearance.Location = " Lisboa, Portugal";         pdfSignatureAppearance.SignDate = DateTime.Now;          stamper.Close();          return output.ToArray();     } }  

I use this code to get byte[] PDF and return again a byte[] PDF already signed.

It's iTextSharp-LGPL.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!