PDF Signing with Smart Card

Hello, do you have any documentation/code sample as to how PDF signing with a certificate on a smart card works?

Thanks

@marcveit

Thank you for contacting support.

You may use ExternalSignature object that provides X509Certificate2 for signing document. In this example, the Windows certificate store used to get the certificate for signing:

// The System.Security.dll assembly should be added into References

// Signing 1. Using SignatureField
public void Sign_With_SmartCard_1()
{
    const string dataDir = @"c:\";

    File.Copy(dataDir + "blank.pdf", dataDir + "externalSignature1.pdf", true);
    using (FileStream fs = new FileStream(dataDir + "externalSignature1.pdf", FileMode.Open, FileAccess.ReadWrite))
    {
          using (Document doc = new Document(fs))
          {
               SignatureField field1 = new SignatureField(doc.Pages[1], new Rectangle(100, 400, 10, 10));

               // Sign with certificate selection in the windows certificate store
               X509Store store = new X509Store(StoreLocation.CurrentUser);
               store.Open(OpenFlags.ReadOnly);
               // Manually chose the certificate in the store
               X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(store.Certificates, null, null, X509SelectionFlag.SingleSelection);

               Aspose.Pdf.Forms.ExternalSignature externalSignature = new Forms.ExternalSignature(sel[0])
               {
                  Authority = "Me",
                  Reason = "Reason",
                  ContactInfo = "Contact"
               };

               field1.PartialName = "sig1";
               doc.Form.Add(field1, 1);
               field1.Sign(externalSignature);
               doc.Save();
         }
    }

    using (PdfFileSignature pdfSign = new PdfFileSignature(dataDir + "externalSignature1.pdf"))
    {
         IList<string> sigNames = pdfSign.GetSignNames();
         for (int index = 0; index <= sigNames.Count - 1; index++)
         {
             if (!pdfSign.VerifySigned(sigNames[index]) || !pdfSign.VerifySignature(sigNames[index])) 
             {
                 throw new ApplicationException("Not verified");  
             }
         }
    }
}

// Signing 2. Using PdfFileSignature
public void Sign_With_SmartCard_2()
{
   const string dataDir = @"c:\";

   Document doc = new Document(dataDir + "blank.pdf");

   using (PdfFileSignature pdfSign = new PdfFileSignature())
   {
         pdfSign.BindPdf(doc);

         //Sign with certificate selection in the windows certificate store
         X509Store store = new X509Store(StoreLocation.CurrentUser);
         store.Open(OpenFlags.ReadOnly);
         //manually chose the certificate in the store
         X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(store.Certificates, null, null,  X509SelectionFlag.SingleSelection);

         Aspose.Pdf.Forms.ExternalSignature externalSignature = new Forms.ExternalSignature(sel[0]);
         pdfSign.SignatureAppearance = dataDir + "demo.png";
         pdfSign.Sign(1, "Reason", "Contact", "Location", true, new System.Drawing.Rectangle(100, 100, 200, 200), externalSignature);
         pdfSign.Save(dataDir + "externalSignature2.pdf");
   }

   using (PdfFileSignature pdfSign = new PdfFileSignature(dataDir + "externalSignature2.pdf"))
   {
        IList<string> sigNames = pdfSign.GetSignNames();
        for (int index = 0; index <= sigNames.Count - 1; index++)
        {
             if (!pdfSign.VerifySigned(sigNames[index]) || !pdfSign.VerifySignature(sigNames[index])) 
             {
                 throw new ApplicationException("Not verified");  
             }
        }
    }
}

We hope this will be helpful. Please feel free to contact us if you need any further assistance.

Thank you very much, this is very helpful.

The example was very useful, but the smart card we are using, require entering a pin code.
How should enter the pin code in the code ?

@ShayChar

You can specify the private key of the certificate as well. For example, before the above line of code, you can add the following:

sel[0].PrivateKey = "{key}";

Thanks Ali for your replay
I tried the above, but as it seems this is Key is the private key associated with a certificate and not the PIN CODE of the smart card.

Let me know if I got it wrong.

@ShayChar

Can you please share a sample code snippet with us where you are fetching a smart card that uses a PIN CODE? Also, please share sample source files in case you are using some with the code. We will further proceed to assist you accordingly.

Thanks for responding
below snippet code, which we are using . (2 lines are marked with Bold ** )

also added a video in the below link

public int SignWithSmartCardUsingPdfFileSignature(String inputFile, String outFile)
{
try
{
LastError = “”;

            if (File.Exists(outFile))
            {
                File.Delete(outFile);
            }

            Document doc = new Document(inputFile);

            using (Aspose.Pdf.Facades.PdfFileSignature pdfSign = new Aspose.Pdf.Facades.PdfFileSignature())
            {
                pdfSign.BindPdf(doc);
                // Sign with certificate selection in the windows certificate store
                X509Store store = new X509Store(StoreLocation.CurrentUser);
                store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

                X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
                X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
                //X509Certificate2Collection oneAndOnly = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection);
                **X509Certificate2Collection oneAndOnly = fcollection.Find(X509FindType.FindByThumbprint, ComSignThumbPrint1, false);**
                if (oneAndOnly.Count == 0)
                {
                    LogEventEntry($"could not locate the Certificate by serial: {ComSignSerial1}");
                    LastError = $"could not locate the Certificate by serial: {ComSignSerial1}";
                    return 0x0010;
                }
                
                Aspose.Pdf.Forms.ExternalSignature externalSignature = new Aspose.Pdf.Forms.ExternalSignature(oneAndOnly[0]);
                pdfSign.SignatureAppearance = "demo.png";
                **pdfSign.Sign(1, "Reason", "Contact", "Location", true, new System.Drawing.Rectangle(100, 100, 200, 200), externalSignature);**
                pdfSign.Save(outFile);
            }

            using (Aspose.Pdf.Facades.PdfFileSignature pdfSign = new Aspose.Pdf.Facades.PdfFileSignature(new Document(outFile)))
            {
                IList<string> sigNames = pdfSign.GetSignNames();
                for (int index = 0; index <= sigNames.Count - 1; index++)
                {
                    if (!pdfSign.VerifySigned(sigNames[index]) || !pdfSign.VerifySignature(sigNames[index]))
                    {
                        throw new ApplicationException($"{outFile} Signed but not verified");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            LogEventEntry(ex);
            LastError = ex.Message;
            return 1;
        }
        return 0x0000;
    }

@ShayChar

An investigation ticket as PDFNET-50057 has been logged in our issue tracking system for the sake of further analysis against this case. We will look into its details and keep you posted with the status of its resolution. Please be patient and spare us some time.

We are sorry for the inconvenience.