7

I'm new to iTextSharp (and StackOverFlow). I'm trying to sign a PDF in C# using external USB token. I try using the follow code I've digged from the internet.

Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();

//Get Sertifiacte
X509Certificate2 certClient = null;
X509Store st = new X509Store(StoreName.My, StoreLocation.CurrentUser);
st.Open(OpenFlags.MaxAllowed);
X509Certificate2Collection collection = X509Certificate2UI.SelectFromCollection(st.Certificates, "Please choose certificate:", "", X509SelectionFlag.SingleSelection);
if (collection.Count > 0){
   certClient = collection[0];
}
st.Close();
//Get Cert Chain
IList<Org.BouncyCastle.X509.X509Certificate> chain = new List<Org.BouncyCastle.X509.X509Certificate>();
X509Chain x509chain = new X509Chain();
x509chain.Build(certClient );
foreach (X509ChainElement x509ChainElement in x509chain.ChainElements){
    chain.Add(DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate));
}

PdfReader reader = new PdfReader(sourceDocument);
FileStream resStream = new FileStream(resultDocument, FileMode.Create, FileAccess.ReadWrite);

PdfStamper stamper = PdfStamper.CreateSignature(reader, resStream , '\0', null, true);

PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = reason;
appearance.Location = location;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(20, 10, 170, 60), 1, "Signed");

X509Certificate2Signature es = new X509Certificate2Signature(certClient, "SHA-1");
MakeSignature.SignDetached(appearance, es, chain, null, null, null, 0, CryptoStandard.CMS);

The problem is that I receive an exception:

System.Security.Cryptography.CryptographicException was unhandled
  Message=Invalid type specified.

  Source=mscorlib
  StackTrace:
       at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
       at System.Security.Cryptography.Utils._GetKeyParameter(SafeKeyHandle hKey, UInt32 paramID)
       at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
       at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
       at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
       at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
       at iTextSharp.text.pdf.security.X509Certificate2Signature..ctor(X509Certificate2 certificate, String hashAlgorithm)
       at WindowsFormsApplication1.PDFSignerHelper.signPdfFile(String sourceDocument, String resultDocument, X509Certificate2 certClient, String reason, String location)
  InnerException: 
Picrofo Software
  • 5,475
  • 3
  • 23
  • 37
Kaloyan Iliev
  • 147
  • 1
  • 2
  • 10
  • So, for now I will return to iTextSharp 5.2.1 and use the examples from http://itextpdf.sourceforge.net/howtosign.html#signextdic. They do the job for me. If someone find solution for iTextSHarp 5.3.3 I will be glad to see it. – Kaloyan Iliev Nov 09 '12 at 14:54

3 Answers3

10

This approach works fine for us (iTextSharp 5.3.3). We use smart-card and USB-token (vendor - www.author.kiev.ua):

            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(store.Certificates, null, null, X509SelectionFlag.SingleSelection);

            X509Certificate2 cert = sel[0];

            Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
            Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
            cp.ReadCertificate(cert.RawData)};

            IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");

            PdfReader pdfReader = new PdfReader(pathToBasePdf);

            signedPdf = new FileStream(pathToBasePdf, FileMode.Create);

            pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0');
            PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

            signatureAppearance.SignatureGraphic = Image.GetInstance(pathToSignatureImage);
            signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 250, 150), pdfReader.NumberOfPages, "Signature");
            signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;

            MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
Sasha
  • 1,958
  • 1
  • 19
  • 33
  • Is there any case certificates from plugged-in USB tokens are not listed when using this way? Because my token's certificate is not listed using this code while Acrobat *does* list it. Although Acrobat reports a "The keyset is not defined." error when trying to sign with it... – user2173353 Jan 22 '16 at 12:01
  • Never mind. It seems Acrobat had imported the certificates to my local store at some point and it was viewing them from there. The USB token can not be accessed now indeed.. Sth wrong with my middleware/driver... How do these companies sell those things? I've spent days searching for the correct driver versions... :( – user2173353 Jan 22 '16 at 13:51
  • @user2173353 in our case we didn't have any problem ) examples, drivers, support ) thanks to manufacture – Sasha May 21 '17 at 20:10
9

I have made a c# project that can sign a PDF from the windows store, SmartCard or a Pfx/P12 file Maybe it can be useful for you

using System;
using System.Windows.Forms;
using System.IO;

using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;



namespace SignPdf
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private  SecureString GetSecurePin(string PinCode)
        {
            SecureString pwd = new SecureString();
            foreach (var c in PinCode.ToCharArray()) pwd.AppendChar(c);
            return pwd;
        }
        private  void button1_Click(object sender, EventArgs e)
        {
            //Sign from SmartCard
            //note : ProviderName and KeyContainerName can be found with the dos command : CertUtil -ScInfo
            string ProviderName = textBox2.Text;
            string KeyContainerName = textBox3.Text;
            string PinCode = textBox4.Text;
            if (PinCode != "")
            {
                //if pin code is set then no windows form will popup to ask it
                SecureString pwd = GetSecurePin(PinCode);
                CspParameters csp = new CspParameters(1,
                                                        ProviderName,
                                                        KeyContainerName,
                                                        new System.Security.AccessControl.CryptoKeySecurity(),
                                                        pwd);
                try
                {
                    RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(csp);
                    // the pin code will be cached for next access to the smart card
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Crypto error: " + ex.Message);
                    return;
                }
            }           
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2 cert = null;
            if ((ProviderName == "") || (KeyContainerName == ""))
            {
                MessageBox.Show("You must set Provider Name and Key Container Name");
                return;
            }
            foreach (X509Certificate2 cert2 in store.Certificates)
            {
                if (cert2.HasPrivateKey)
                {
                    RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert2.PrivateKey;
                    if (rsa == null) continue; // not smart card cert again
                    if (rsa.CspKeyContainerInfo.HardwareDevice) // sure - smartcard
                    {
                        if ((rsa.CspKeyContainerInfo.KeyContainerName == KeyContainerName) && (rsa.CspKeyContainerInfo.ProviderName == ProviderName))
                        {
                            //we find it
                            cert = cert2;
                            break;
                        }
                    }
                }
            }
            if (cert == null)
            {
                MessageBox.Show("Certificate not found");
                return;
            }
            SignWithThisCert(cert);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //Sign with certificate selection in the windows certificate store
            X509Store store = new X509Store(StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2 cert = null;
            //manually chose the certificate in the store
            X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(store.Certificates, null, null, X509SelectionFlag.SingleSelection);
            if (sel.Count > 0)
                cert = sel[0];
            else
            {
                MessageBox.Show("Certificate not found");
                return;
            }
            SignWithThisCert(cert);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //Sign from certificate in a pfx or a p12 file
            string PfxFileName = textBox5.Text;
            string PfxPassword = textBox6.Text;
            X509Certificate2 cert = new X509Certificate2(PfxFileName, PfxPassword);
            SignWithThisCert(cert);
        }

        private void SignWithThisCert(X509Certificate2 cert)
        {
            string SourcePdfFileName = textBox1.Text;
            string DestPdfFileName = textBox1.Text + "-Signed.pdf";
            Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
            Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(cert.RawData) };
            IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");
            PdfReader pdfReader = new PdfReader(SourcePdfFileName);
            FileStream signedPdf = new FileStream(DestPdfFileName, FileMode.Create);  //the output pdf file
            PdfStamper pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0');
            PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;
            //here set signatureAppearance at your will
            signatureAppearance.Reason = "Because I can";
            signatureAppearance.Location = "My location";
            signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
            MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
            //MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CADES);
            MessageBox.Show("Done");
        }
    
    }


}
Ram
  • 131
  • 8
jean-luc
  • 91
  • 1
  • 1
2

The same code as above but uses a certificate file instead of store to sign a PDF document on the last page.

 X509Certificate2 cert = new X509Certificate2("C:\\mycert.p12");

 Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
 Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
 cp.ReadCertificate(cert.RawData)};

 IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-1");

 PdfReader pdfReader = new PdfReader("C:\\multi-page-pdf.pdf");

 var signedPdf = new FileStream("C:\\multi-page-pdf-signed.pdf", FileMode.Create);

 var pdfStamper = PdfStamper.CreateSignature(pdfReader, signedPdf, '\0');
 PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

 signatureAppearance.SignatureGraphic = Image.GetInstance("C:\\logo.png");
 signatureAppearance.Reason = "Because I can";
 signatureAppearance.Location = "My location";
 signatureAppearance.SetVisibleSignature(new Rectangle(100, 100, 250, 150), pdfReader.NumberOfPages, "Signature");
 signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;

 MakeSignature.SignDetached(signatureAppearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);

Copy, paste, import needed libraries and go work on something else.

RokX
  • 334
  • 6
  • 16