I want to return an ltv enabled pdf after signing a document. I have posted a similar question before and after suggestions from advanced developers like @mkl, I made some modification to my code yet I can't get the desired result. I've been trying for more than a week and I'm very fustrated. Please, if anyone is kind enough to edit the code or offer suggestions that might help me, I will be very grateful. Thanks very much in advance.
All the methods are located in a static class "SignMyPDF" Here is the method I have use to sign my pdf
public static byte[] Sign(byte[] document, X509Certificate2 certificate, ITSAClient tsaClient)
{
byte[] signedDocument = null;
IExternalSignature signature = new X509Certificate2Signature(certificate, "SHA-1");
Org.BouncyCastle.X509.X509CertificateParser cp = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { cp.ReadCertificate(certificate.RawData) };
PdfReader reader = new PdfReader(document);
MemoryStream ms = new MemoryStream();
PdfStamper st = PdfStamper.CreateSignature(reader, ms, '\0');
PdfSignatureAppearance sap = st.SignatureAppearance;
sap.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;
sap.SignatureCreator = "NAME";
sap.Reason = "REASON";
sap.Contact = "CONTACT";
sap.Location = "LOCATION";
sap.SignDate = DateTime.Now;
RectangleF rectangle = new RectangleF(400.98139f, 54.88828f, 530, 84.88828f);
sap.Layer2Font = iTextSharp.text.FontFactory.GetFont(BaseFont.TIMES_ROMAN, BaseFont.CP1257, 7f);
sap.Layer2Font.Color = iTextSharp.text.BaseColor.RED;
sap.Layer2Text = string.Format("Signed for testing: {0}", DateTime.Now.ToString("dd.MM.yyyy."));
sap.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
sap.SetVisibleSignature(new iTextSharp.text.Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height), 1, null);
IOcspClient ocspClient = new OcspClientBouncyCastle();
ICrlClient crlClient = new CrlClientOnline();
List<ICrlClient> crlList = new List<ICrlClient>();
string[] crls = GetCrlDistributionPoints(certificate);
crlClient = new CrlClientOnline(crls);
crlList.Add(crlClient);
MakeSignature.SignDetached(sap, signature, chain, crlList, ocspClient, tsaClient, 0, CryptoStandard.CMS);
// ADD ltv to document
AddLtv(signedDocument, @"d:\test.pdf", ocspClient, crlClient, tsaClient);
st.Close();
ms.Flush();
signedDocument = ms.ToArray();
ms.Close();
reader.Close();
return signedDocument;
}
Method to make document ltv enabled
public static void AddLtv(byte[] doc, string dest, IOcspClient ocsp, ICrlClient crl, ITSAClient tsa)
{
PdfReader r = new PdfReader(doc);
FileStream fos = new FileStream(dest, FileMode.Create);
PdfStamper stp = PdfStamper.CreateSignature(r, fos, '\0', null, true);
LtvVerification v = stp.LtvVerification;
AcroFields fields = stp.AcroFields;
List<string> names = fields.GetSignatureNames();
string sigName = names[names.Count - 1];
PdfPKCS7 pkcs7 = fields.VerifySignature(sigName);
if (pkcs7.IsTsp)
{
v.AddVerification(sigName, ocsp, crl,
LtvVerification.CertificateOption.SIGNING_CERTIFICATE,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
else
{
foreach (string name in names)
{
v.AddVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
PdfSignatureAppearance sap = stp.SignatureAppearance;
LtvTimestamp.Timestamp(sap, tsa, null);
}
These other method are helpers for collecting the crls from a certificate object
public static string[] GetCrlDistributionPoints(this X509Certificate2 certificate)
{
X509Extension ext = certificate.Extensions.Cast<X509Extension>().FirstOrDefault(
e => e.Oid.Value == "2.5.29.31");
if (ext == null || ext.RawData == null || ext.RawData.Length < 11)
return EmptyStrings;
int prev = -2;
List<string> items = new List<string>();
while (prev != -1 && ext.RawData.Length > prev + 1)
{
int next = IndexOf(ext.RawData, 0x86, prev == -2 ? 8 : prev + 1);
if (next == -1)
{
if (prev >= 0)
{
string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, ext.RawData.Length - (prev + 2));
items.Add(item);
}
break;
}
if (prev >= 0 && next > prev)
{
string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, next - (prev + 2));
items.Add(item);
}
prev = next;
}
return items.ToArray();
}
static int IndexOf(byte[] instance, byte item, int start)
{
for (int i = start, l = instance.Length; i < l; i++)
if (instance[i] == item)
return i;
return -1;
}
static string[] EmptyStrings = new string[0];
And finally, this is the code which calls the sign method is located in the form's load handler. Here's everything in the event. The certificate and document to be signed is stored in my project file. So that's why I'm using the "path" variable.
private void Form1_Load(object sender, EventArgs e)
{
string path = Application.StartupPath;
path = path.Substring(0, path.LastIndexOf("\\"));
path = path.Substring(0, path.LastIndexOf("\\"));
byte[] document = System.IO.File.ReadAllBytes(path + "\\test.pdf");
X509Certificate2 certificate = new X509Certificate2(path + "\\cert.pfx", "misko123");
iTextSharp.text.pdf.security.ITSAClient tsaClient = new iTextSharp.text.pdf.security.TSAClientBouncyCastle("http://timestamp.comodoca.com");
byte[] signedDocument = SignMyPDF.Sign(document, certificate, tsaClient);
path = path + "\\test_signed.pdf";
System.IO.File.WriteAllBytes(path, signedDocument);
System.Diagnostics.Process.Start(path);
this.Close();
}
Here is a link to my certificate and the document I want to sign and make ltv enabled.
Certificate
https://drive.google.com/file/d/1P82Cc2sFN_z7fYImEvInWIRkD30OzPt6/view?usp=sharing
Document
https://drive.google.com/file/d/10_SqgmBrmzcQ4Motj6eVZkD0vb812HFY/view?usp=sharing
Please I need help.