I'm trying to sign (visible custom signature) a pdf using iText7 and BouncyCastle in c# (NET 8.0), but after an update of iText7 I had to change some obsolete functions. Now I can detect and select the certificate, but I cannot export the private key as I need the PIN/Password for the smart card, and Windows doesn't ask for this password anymore. Here is my code:
public class X509Certificate2Signature : IExternalSignature
{
private string hashAlgorithm;
private byte[] privateKey;
private string encryptionAlgorithm;
private X509Certificate2 certificate;
private string digestAlgorithm;
public X509Certificate2Signature(X509Certificate2 certificate, string hashAlgorithm)
{
if (!certificate.HasPrivateKey)
throw new ArgumentException("No private key.");
this.certificate = certificate;
this.digestAlgorithm = this.hashAlgorithm = DigestAlgorithms.GetDigest(DigestAlgorithms.GetAllowedDigest(hashAlgorithm));
//X509Certificate2UI.(certificate);
if (certificate.GetRSAPrivateKey() is RSA)
{
encryptionAlgorithm = "RSA";
RSA rSA = certificate.GetRSAPrivateKey();
try
{
privateKey = rSA.ExportRSAPrivateKey();
}
catch
{
try
{
privateKey = rSA.ExportPkcs8PrivateKey(); //<- here is the Problem, cannot export
}
catch (Exception ex)
{
throw new Exception("Errore: " + ex.Message);
}
}
}
else if (certificate.GetDSAPrivateKey() is DSA)
{
encryptionAlgorithm = "DSA";
privateKey = certificate.GetDSAPrivateKey().ExportPkcs8PrivateKey();
}
else
throw new ArgumentException("Unknown encryption algorithm!");
}
and this is the call:
try
{
if (!Directory.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp"))
Directory.CreateDirectory(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp");
float x, y, w, h, pH, pW;
int angle = _Deg;
w = _W * 2.8f;
h = _H * 2.8f;
x = _X * 2.8f;
y = _Y * 2.8f;
pH = _pageH;
pW = _pageW;
X509Store st = new X509Store(StoreName.My, StoreLocation.CurrentUser);
st.Open(OpenFlags.ReadOnly);
X509Certificate2Collection col = st.Certificates;
X509Certificate2 myCert = null;
X509Certificate2Collection finder = col.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
X509Certificate2Collection final = new X509Certificate2Collection();
byte[] firma = new byte[] { 3, 2, 6, 64 };
foreach (X509Certificate2 xcert in finder)
{
byte[] attrs = GetExtensionValue(xcert, "2.5.29.15");
if (attrs != null)
{
bool add = false;
if (attrs.Length == firma.Length)
{
for (int i = 0; i < attrs.Length; i++)
{
if (attrs[i] == firma[i])
{
add = true;
}
else
{
add = false;
break;
}
}
}
if (add)
final.Add(xcert);
}
}
X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(final, "Certificati", "Seleziona il Certificato per Firmare il Documento", X509SelectionFlag.SingleSelection);
if (sel.Count > 0)
{
X509Certificate2Enumerator en = sel.GetEnumerator();
en.MoveNext();
myCert = en.Current;
}
st.Close();
DateTime now = DateTime.Now;
string tmp = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp" + System.IO.Path.DirectorySeparatorChar + "t0.pdf";
List<IX509Certificate> chain = new List<IX509Certificate>();
X509Chain x509chain = new X509Chain();
x509chain.Build(myCert);
foreach (X509ChainElement x509ChainElement in x509chain.ChainElements)
{
var certToAdd = DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate);
var toAdd = new X509CertificateBC(certToAdd);
chain.Add(toAdd);
}
IX509Certificate cert = new X509CertificateBC(DotNetUtilities.FromX509Certificate(myCert));
string subject = myCert.Subject;
string contact = string.Empty;
string[] fields = subject.Split(',');
Dictionary<string, string> fieldDic = new Dictionary<string, string>();
foreach (string s in fields)
{
string[] t = s.Trim().Split('=');
if (t.Length == 2)
{
fieldDic.Add(t[0].Trim(), t[1].Trim());
}
}
string name = fieldDic.Keys.Contains("CN") ? fieldDic["CN"] : string.Empty;
PdfReader reader = new PdfReader(inputPDF);
using (FileStream os = new FileStream(outputPDF, FileMode.Create))
{
PdfSigner signer = new PdfSigner(reader, os, new StampingProperties());
StampingProperties properties = new StampingProperties();
SignatureFieldAppearance sap = new SignatureFieldAppearance("SignExample");
signer.SetSignatureCreator("Example");
signer.SetReason(!string.IsNullOrEmpty(SigReason) ? SigReason.ToUpper() : string.Empty);
signer.SetContact(contact);
signer.SetLocation(!string.IsNullOrEmpty(SigLocation) ? SigLocation.ToUpper() : string.Empty);
StringBuilder sb = new StringBuilder();
sb.Append("Documento firmato digitalmente da " + name.ToUpper() + " " + now.ToString("dddd dd/MM/yyyy") + " alle " + now.ToString("HH:mm:ss"));
if (!string.IsNullOrEmpty(SigLocation) || !string.IsNullOrEmpty(SigReason))
sb.AppendLine();
if (!string.IsNullOrEmpty(SigReason))
sb.Append("Motivo della firma: " + SigReason.ToUpper());
if (!string.IsNullOrEmpty(SigLocation) && !string.IsNullOrEmpty(SigReason))
sb.Append("; ");
if (!string.IsNullOrEmpty(SigLocation))
sb.Append("Luogo Firma: " + SigLocation.ToUpper());
sap.SetContent(sb.ToString());
var assembly = typeof(PaDESSigner).GetTypeInfo().Assembly;
byte[] imgBytes = new byte[0];
using (Stream? resource = assembly.GetManifestResourceStream("pdf_signatureLogo.jpg"))
{
if (resource != null)
{
imgBytes = new byte[resource.Length];
resource.Read(imgBytes, 0, (int)resource.Length);
}
}
if (imgBytes.Length > 0)
{
iText.IO.Image.ImageData img = iText.IO.Image.ImageDataFactory.Create(imgBytes, false);
img.SetXYRatio(0.5f);
img.SetRotation(angle);
iText.Layout.Properties.BackgroundImage.Builder builder = new iText.Layout.Properties.BackgroundImage.Builder();
builder.SetImage(new iText.Kernel.Pdf.Xobject.PdfImageXObject(img));
builder.SetBackgroundRepeat(new iText.Layout.Properties.BackgroundRepeat(iText.Layout.Properties.BackgroundRepeat.BackgroundRepeatValue.NO_REPEAT));
builder.SetBackgroundBlendMode(iText.Layout.Properties.BlendMode.OVERLAY);
sap.SetBackgroundImage(builder.Build());
}
AffineTransform af = new AffineTransform();
af.Rotate((float)(Math.PI / 180) * angle);
sap.SetFixedPosition(_page, x, y, w);
signer.SetSignDate(now);
signer.SetSignatureAppearance(sap);
signer.GetDocument().GetCatalog().SetModified();
X509Certificate2Signature externalSignature = new X509Certificate2Signature(myCert, "SHA-256");
signer.SignDetached(externalSignature, chain.ToArray(), null, null, null, 0, PdfSigner.CryptoStandard.CMS);
try
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp" + System.IO.Path.DirectorySeparatorChar + "t0.pdf");
}
catch { }
try
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp" + System.IO.Path.DirectorySeparatorChar + "t2.pdf");
}
catch { }
return true;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
try
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp" + System.IO.Path.DirectorySeparatorChar + "t0.pdf");
}
catch { }
try
{
File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + System.IO.Path.DirectorySeparatorChar + "Temp" + System.IO.Path.DirectorySeparatorChar + "t2.pdf");
}
catch { }
return false;
}
How can I ask for the pin in secure mode and export/use the certificate private key?