如何通过iTextSharp在PDF签名时显示证书主题与颁发者信息
Hey there! Great job getting the basic digital signature working with iTextSharp. Let's adjust your code to show certificate details like subject name and issuer directly on the signed PDF. Here's how to do it step by step:
1. Extract Clean Certificate Details First
The raw Subject and Issuer properties from your X509Certificate2 are Distinguished Name (DN) strings (like CN=John Doe, OU=IT, O=Company). We'll add a helper method to extract the common name (CN) for a cleaner, more user-friendly display:
private static string GetCommonNameFromDN(string distinguishedName) { foreach (string segment in distinguishedName.Split(',')) { string trimmedSegment = segment.Trim(); if (trimmedSegment.StartsWith("CN=", StringComparison.OrdinalIgnoreCase)) { return trimmedSegment.Substring(3); } } // Fallback to full DN if CN isn't found return distinguishedName; }
2. Customize the Signature Appearance Layer
iTextSharp's PdfSignatureAppearance has editable layers—Layer 2 is where you can add custom text and graphics. We'll use this layer to inject our certificate details.
Update your signature appearance setup section with this code:
PdfSignatureAppearance appearance = stamper.SignatureAppearance; appearance.Reason = Properties.Settings.Default.DigitalSignReason; appearance.Location = Properties.Settings.Default.DigitalSignLocation; appearance.Contact = Properties.Settings.Default.DigitalSignContact; if (Properties.Settings.Default.DigitalSignAppearance == 1) { appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(20, 10, 170, 60), 1, "Signed"); } // --- Add these lines to display certificate info --- // Get the customizable layer (Layer 2) PdfTemplate signatureLayer = appearance.GetLayer(2); // Set up a readable font for the text BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); Font textFont = new Font(baseFont, 8); // Build the certificate info text (customize this format as needed) string subjectCN = GetCommonNameFromDN(cert.Subject); string issuerCN = GetCommonNameFromDN(cert.Issuer); string signatureText = $"签名者: {subjectCN}\n颁发机构: {issuerCN}\n原因: {appearance.Reason}\n位置: {appearance.Location}"; // Add the text to the layer (adjust X/Y to fit your signature rectangle) ColumnText.ShowTextAligned( signatureLayer, Element.ALIGN_LEFT, new Phrase(signatureText, textFont), 15, // X position inside the signature box 15, // Y position inside the signature box 0 ); // Tell iText to use our custom layer instead of default text appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.CUSTOM; // --- End of custom certificate info code --- // Also, switch to SHA-256 (SHA-1 is no longer cryptographically secure) IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-256"); MakeSignature.SignDetached(appearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS);
Key Notes:
- Coordinates: The X/Y values in
ShowTextAlignedare relative to your signature rectangle (the one you defined withnew Rectangle(20, 10, 170, 60)). Tweak them to ensure your text fits neatly without overflow. - Text Flexibility: You can expand
signatureTextto include more certificate details (like validity dates) by pulling properties directly from thecertobject (e.g.,cert.NotBefore.ToString("yyyy-MM-dd")). - Security Upgrade: I replaced SHA-1 with SHA-256 because SHA-1 is now considered insecure. Most modern systems require SHA-2 or higher for valid digital signatures.
Modified Full Sign Method
Here's your complete Sign method with all changes integrated:
public static X509Certificate2 cert; //Sign with certificate selection in the windows certificate store public static void Sign(string pdfFile, string outPdfFile){ Program.WriteLog("Signing Digital Certificate"); string IssuerName = null; X509Store store = new X509Store(StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); IssuerName = Properties.Settings.Default.IssuerName; if (IssuerName.Length > 0) cert = store.Certificates.Find(X509FindType.FindByIssuerName, IssuerName, false)[0]; if (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 { Console.WriteLine("Certificate not found"); return; } } PdfReader reader = new PdfReader(pdfFile); // source pdf file FileStream os = new FileStream(outPdfFile, FileMode.Create); //the output pdf file PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0'); stamper.SetEncryption(PdfWriter.STRENGTH128BITS, "", null, PdfWriter.AllowCopy | PdfWriter.AllowPrinting); try { Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser(); Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(cert.RawData) }; // Use SHA-256 for secure signing IExternalSignature externalSignature = new X509Certificate2Signature(cert, "SHA-256"); PdfSignatureAppearance appearance = stamper.SignatureAppearance; //here set signatureAppearance at your will appearance.Reason = Properties.Settings.Default.DigitalSignReason; appearance.Location = Properties.Settings.Default.DigitalSignLocation; appearance.Contact = Properties.Settings.Default.DigitalSignContact; if (Properties.Settings.Default.DigitalSignAppearance == 1) { appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(20, 10, 170, 60), 1, "Signed"); } // Customize signature layer to show certificate details PdfTemplate signatureLayer = appearance.GetLayer(2); BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); Font textFont = new Font(baseFont, 8); string subjectCN = GetCommonNameFromDN(cert.Subject); string issuerCN = GetCommonNameFromDN(cert.Issuer); string signatureText = $"签名者: {subjectCN}\n颁发机构: {issuerCN}\n原因: {appearance.Reason}\n位置: {appearance.Location}"; ColumnText.ShowTextAligned( signatureLayer, Element.ALIGN_LEFT, new Phrase(signatureText, textFont), 15, 15, 0 ); appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.CUSTOM; MakeSignature.SignDetached(appearance, externalSignature, chain, null, null, null, 0, CryptoStandard.CMS); }catch(Exception e){ Console.WriteLine(e.Message, 1); File.Delete(outPdfFile); } finally { if (reader != null) reader.Close(); if (stamper != null) stamper.Close(); if (os != null) os.Close(); } } private static string GetCommonNameFromDN(string distinguishedName) { foreach (string segment in distinguishedName.Split(',')) { string trimmedSegment = segment.Trim(); if (trimmedSegment.StartsWith("CN=", StringComparison.OrdinalIgnoreCase)) { return trimmedSegment.Substring(3); } } return distinguishedName; }
内容的提问来源于stack exchange,提问作者Timothy Yu

