I'm new in digital signing concept, I'm developing a winform application that allow an user to select a certificate from Windows certificate store, and use the selected certificate to sign to a pdf file, it is working perfectly when plug an usbtoken which contains the certificate and a midleware of the usbtoken installed, when I sign to a pdf file the midleware prompt me for entering pin and after enter the correct pin my application can easily sign to the pdf file. But when I try with a smartcard( TokenMe Evo – Bit4id) I can still get the certificate from windows store but when create signature by bellow line of code:
IExternalSignature externalSignature = new X509Certificate2Signature(certificate,
"SHA-1");
nothing promt me for entering pin and the bellow exception apear:
System.ArgumentException: Unknown encryption algorithm
System.Security.Cryptography.RSACng
This is how i get the certificate from windows store:
using iTextSharp.text.pdf.security;
using iTextSharp.text.pdf;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System.Security.Cryptography.X509Certificates;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Org.BouncyCastle.Tls;
using System.Text.Json;
using Serilog;
using iTextSharp.text;
private static IList<X509Certificate> chain = new List<X509Certificate>();
private static X509Certificate2 certificate = null;
X509CertificateParser cp = new X509CertificateParser();
//Get Sertifiacte
X509Store st = new X509Store(StoreName.My, StoreLocation.CurrentUser);
st.Open(OpenFlags.MaxAllowed);
X509Certificate2Collection collection =
X509Certificate2UI.SelectFromCollection(st.Certificates,
"Please select certificate:", "", X509SelectionFlag.SingleSelection);
if (collection.Count > 0)
{
certificate = collection[0];
}
if (certificate == null)
{
MessageBox.Show("No certificate selected!");
button3.Enabled = true;
return;
}
st.Close();
//Get Cert Chain
X509Chain x509Chain = new X509Chain();
x509Chain.Build(certificate);
foreach (X509ChainElement x509ChainElement in x509Chain.ChainElements)
{
chain.Add(DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate));
}
And this is how I sign to the pdf file and the exception occur:
private void signPdf(
string inputFile,
string outputPath,
string imagePath,
int pageNum,
int position,
float imageWidth,
float imageHeight)
{
try
{
string fileName = Path.GetFileName(inputFile);
ouputFile = ouputFile + "\\" + fileName;
PdfReader inputPdf = new PdfReader(inputFile);
FileStream signedPdf = new FileStream(ouputFile, FileMode.Create);
PdfStamper pdfStamper = PdfStamper.CreateSignature(inputPdf, signedPdf,
'\0');
IExternalSignature externalSignature =
new X509Certificate2Signature(certificate, "SHA-1");
PdfSignatureAppearance signatureAppearance =
pdfStamper.SignatureAppearance;
int NumberOfPages = inputPdf.NumberOfPages;
if (imagePath != "" && imagePath != null)
{
signatureAppearance.SignatureGraphic =
iTextSharp.text.Image.GetInstance(imagePath);
}
iTextSharp.text.Rectangle pageSize = inputPdf.GetPageSize(pageNum);
float x0 = 0;
float y0 = 0;
float x1 = 0;
float y1 = 0;
switch (position)
{
case 0:
x0 = 0;
y0 = pageSize.Height - imageHeight;
break;
case 1:
x0 = pageSize.Width - imageWidth;
y0 = pageSize.Height - imageHeight;
break;
case 2:
x0 = 0;
y0 = 0;
break;
case 3:
x0 = pageSize.Width - imageWidth;
y0 = 0;
break;
default:
break;
}
x1 = x0 + imageWidth;
y1 = y0 + imageHeight;
signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle(
x0,
y0,
x1,
y1),
pageNum,
GetOrganizationName(certificate));
if (renderingMode == 0)
{
signatureAppearance.SignatureRenderingMode =
PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
}
else if (renderingMode == 1)
{
signatureAppearance.SignatureRenderingMode =
PdfSignatureAppearance.RenderingMode.GRAPHIC;
}
else if (renderingMode == 2)
{
signatureAppearance.SignatureRenderingMode =
PdfSignatureAppearance.RenderingMode.DESCRIPTION;
}
MakeSignature.SignDetached(signatureAppearance, externalSignature, chain,
null, null, null, 0,CryptoStandard.CMS);
inputPdf.Close();
pdfStamper.Close();
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
}
Please show me what am I doing wrong, Thanks a lot.
I resloved this issue myself. This is because of the private key of the certificate is not in form of RSA or DSA, its is actually:
System.Security.Cryptography.RSACng
and the class X509Certificate2Signature from iTextSharp.text.pdf.security only implement two kind of certificate.PrivateKey are RSA and DSA like this:
public X509Certificate2Signature(X509Certificate2 certificate, string
hashAlgorithm)
{
if (!certificate.HasPrivateKey)
{
throw new ArgumentException("No private key.");
}
this.certificate = certificate;
this.hashAlgorithm =
DigestAlgorithms.GetDigest(
DigestAlgorithms.GetAllowedDigests(hashAlgorithm));
if (certificate.PrivateKey is RSACryptoServiceProvider)
{
encryptionAlgorithm = "RSA";
return;
}
if (certificate.PrivateKey is DSACryptoServiceProvider)
{
encryptionAlgorithm = "DSA";
return;
}
throw new ArgumentException("Unknown encryption algorithm " +
certificate.PrivateKey);
}
this is the cause of the Unknown encryption algorithm, beacuse when certificate.PrivateKey is System.Security.Cryptography.RSACng it won't set encryptionAlgorithm and throw new ArgumentException, I cannot edit the class X509Certificate2Signature of namespace iTextSharp.text.pdf.security, so I created a class named X509Certificate2Signature1 myself implement IExternalSignature interface and add additional code to manipulate if certificate.PrivateKey is System.Security.Cryptography.RSACng, and bellow is how I implement my X509Certificate2Signature1 class:
public class X509Certificate2Signature1 : IExternalSignature
{
//
// Summary:
// The certificate with the private key
private X509Certificate2 certificate;
private string hashAlgorithm;
private string encryptionAlgorithm;
//
// Summary:
// Creates a signature using a X509Certificate2. It supports
//smartcards without
// exportable private keys.
//
// Parameters:
// certificate:
// The certificate with the private key
//
// hashAlgorithm:
// The hash algorithm for the signature. As the Windows CAPI is
//used to do the signature
// the only hash guaranteed to exist is SHA-1
public X509Certificate2Signature1(X509Certificate2 certificate, string
hashAlgorithm)
{
if (!certificate.HasPrivateKey)
{
throw new ArgumentException("No private key.");
}
this.certificate = certificate;
this.hashAlgorithm =
DigestAlgorithms.GetDigest(
DigestAlgorithms.GetAllowedDigests(hashAlgorithm));
if (certificate.PrivateKey is RSACryptoServiceProvider)
{
encryptionAlgorithm = "RSA";
return;
}
else if (certificate.PrivateKey is DSACryptoServiceProvider)
{
encryptionAlgorithm = "DSA";
return;
}
else if (certificate.PrivateKey is
System.Security.Cryptography.RSACng)
{
encryptionAlgorithm = "RSA";
return;
}
else
{
encryptionAlgorithm = "RSA";
return;
}
throw new ArgumentException("Unknown encryption algorithm " +
certificate.PrivateKey);
}
public virtual byte[] Sign(byte[] message)
{
if (certificate.PrivateKey is RSACryptoServiceProvider)
{
RSACryptoServiceProvider rsa =
(RSACryptoServiceProvider)certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
}
else if (certificate.PrivateKey is
System.Security.Cryptography.RSACng)
{
System.Security.Cryptography.RSACng rSACng =
(System.Security.Cryptography.RSACng)certificate.PrivateKey;
return rSACng.SignData(message, HashAlgorithmName.SHA1,
RSASignaturePadding.Pkcs1);
}
else
{
DSACryptoServiceProvider dsa =
(DSACryptoServiceProvider)certificate.PrivateKey;
return dsa.SignData(message);
}
}
public virtual string GetHashAlgorithm()
{
return hashAlgorithm;
}
public virtual string GetEncryptionAlgorithm()
{
return encryptionAlgorithm;
}
}
And I change the line of code which cause the issue from:
externalSignature = new X509Certificate2Signature(certificate,
"SHA-1");
To:
externalSignature = new X509Certificate2Signature1(certificate,
"SHA-1");
and now my application can digital signing to pdf files perfectly!