Introduction
The tip will help you understand how a Word document can be converted to PDF and merge it as single PDF with a digital signature for the secured transport of PDF.
Background
This tip will give a basic idea to merge the PDFs into a single PDF from a Word doc, which can then be digitally signed using AsymmetricKey
values by using various DLLs in conjunction.
The tip demonstrates the following points:
- Replacing the word in the Word document and converting into PDF
- Merging the list of PDF into Single PDF using Spire.Pdf
- Digitally signing the PDF and placement of Signature in the PDF
- Password protection of the signed PDF document
Using the Code
The code example below uses various DLLs to accomplish the task.
- This code provides the demonstration of how to merge the set of PDF documents into a single PDF
- Digitally sign the merged PDF document
- Password protection of the digitally signed PDF document.
Below is the list of DLLs which are used, please include this essential DLL into the source code before using the code.
Part 1 - Variable Declaration and Initialization
using iTextSharp.text;
using iTextSharp.text.pdf;
using Microsoft.Office.Interop.Word;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using Spire.Pdf;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
Pkcs12Store pk12;
try
{
pk12 = new Pkcs12Store(new FileStream(txtSignature.Text, FileMode.Open, FileAccess.Read),
txtPassword.Text.ToCharArray());
}
catch (Exception ex)
{
MessageBox.Show("Signature Passsword incorrect" + ex.Message);
return;
}
In the above piece of code:
- We are declaring the certificate variables
Pkcs12Store
Store is instantiated which takes four parameters:
- Signature text provided by user
- FileMode
- Accesstype
- Password provided by the user
These parameters would be used by the Pkcs12Store
to access the certificate having a pfx extension. This certificate will be used later for digitally signing the document in the below code.
Part 2 - Loading the Document and Replacing the Values from the Database
#region Firstpage Document to pdf conversion and replacing of values
object fileName = Path.GetFullPath
(ConfigurationManager.AppSettings["TemplateDocument"].ToString());
Word.Application wordApp = new Word.Application { Visible = false };
Word.Document aDoc =
wordApp.Documents.Open(ref fileName, ReadOnly: true, Visible: false);
aDoc.Activate();
string text = wordApp.ActiveDocument.Content.Text;
Word.Find fnd = wordApp.ActiveDocument.Content.Find;
fnd.ClearFormatting();
fnd.Replacement.ClearFormatting();
fnd.Forward = true;
fnd.Wrap = Word.WdFindWrap.wdFindContinue;
fnd.Text = "[##FormName]";
fnd.Replacement.Text = formName;
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##COMPANY]";
fnd.Replacement.Text =
((System.Data.DataRowView)(cmbEntity.SelectedItem)).Row.ItemArray[2].ToString();
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##NAME]";
fnd.Replacement.Text = Convert.ToString(dr[0]["RES_DESC"]);
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##EMP_CODE]";
fnd.Replacement.Text = Convert.ToString(row["Emp_Code"]);
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##EMP_PAN]";
if (!String.IsNullOrEmpty(Convert.ToString(row["PAN_Number"])))
fnd.Replacement.Text = Convert.ToString(row["PAN_Number"]);
else
fnd.Replacement.Text = "PANNOTAVBL";
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##EMP_DESG]";
fnd.Replacement.Text = Convert.ToString(dr[0]["RES_DESIGNATION"]);
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##FINANCIAL_YEAR]";
fnd.Replacement.Text = Convert.ToString(cmbYear.SelectedItem.ToString());
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
fnd.Text = "[##ASSESMENT_YEAR]";
var assesmentperiod = CalculateAssesmentYear
(Convert.ToString(cmbYear.SelectedItem.ToString()),"AP");
fnd.Replacement.Text = assesmentperiod.Item1+" - "+assesmentperiod.Item2;
fnd.Execute(Replace: Word.WdReplace.wdReplaceAll);
The above code reads the Doc Template, replaces the values from the database and converts it into the document that would be used by PDF conversion object.
Part 3 - Assigning User Name and Password to the Document for Authentication and Converting from Document to PDF
The code below explains how the files are read from the directory and are assigned with the security username and password before converting it to the secured or protected PDF files.
object oMissing = System.Reflection.Missing.Value;
DirectoryInfo dirInfo = new DirectoryInfo(Path.GetFullPath
(ConfigurationManager.AppSettings["TemplateDocument"].ToString()));
outputFileName = Path.GetFullPath(ConfigurationManager.AppSettings
["EmployeeFirstPage"]"ToString()) +
Convert.ToString(row["Emp_Code"]) +
dirInfo.Name.Replace(".docx", ".pdf");
object fileFormat = Word.WdSaveFormat.wdFormatPDF;
// Save document into PDF Format
if (File.Exists(Convert.ToString(outputFileName)))
{
File.Delete(Convert.ToString(outputFileName));
}
aDoc.SaveAs(ref outputFileName,
ref fileFormat, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing,
ref oMissing, ref oMissing, ref oMissing, ref oMissing);
object saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
((_Document)aDoc).Close(ref saveChanges, ref oMissing, ref oMissing);
aDoc = null;
((_Application)wordApp).Quit(ref oMissing, ref oMissing, ref oMissing);
wordApp = null;
// DoSearchAndReplaceInWord();
#endregion First page document
#region merging of entire documents
//PdfDocument outputDocument = new PdfDocument();
List<string> lstfirspage = new List<string>();
lstfirspage.Add(outputFileName.ToString());
lstfirspage.AddRange(lstdocuments);
try
{
// Save the document...
PdfDocumentBase doc = Spire.Pdf.PdfDocument.MergeFiles(lstfirspage.ToArray<string>());
Spire.Pdf.Security.PdfSecurity securitySettings = doc.Security;
string year = Convert.ToString(cmbYear.SelectedItem);
string filename = Convert.ToString(row["Emp_Code"])+ "_Form16_" + year;
string outputUnSignedFile = @"" + txtDestination.Text +
"\\TUnSigned\\" + filename + ".pdf";
outputFile = @"" + txtDestination.Text + "\\" + filename + ".pdf";
if (!String.IsNullOrWhiteSpace(Convert.ToString(row["PAN_Number"])))
{
if ((Convert.ToString(dr[0]["Res_Status"]) == "Active"))
{
securitySettings.UserPassword = Convert.ToString(row["PAN_Number"]).ToUpper();
securitySettings.OwnerPassword = Convert.ToString(row["PAN_Number"]).ToUpper();
}
word = "PAN in uppercase";
if (File.Exists(txtSource.Text + "\\" + TraceFile + ".pdf"))
{
if (File.Exists(outputUnSignedFile))
{
File.Delete(outputUnSignedFile);
}
doc.Save(outputUnSignedFile, FileFormat.PDF);
}
else
{
if (File.Exists(outputFile))
{
File.Delete(outputFile);
}
doc.Save(outputFile, FileFormat.PDF);
}
}
else
{
if ((Convert.ToString(dr[0]["Res_Status"]) == "Active"))
{
securitySettings.UserPassword = Convert.ToString(row["Emp_Code"]).ToUpper();
securitySettings.OwnerPassword = Convert.ToString(row["Emp_Code"]).ToUpper();
}
word = "Employee Code";
if (File.Exists(outputFile))
{
File.Delete(outputFile);
}
doc.Save(outputFile, FileFormat.PDF);
}
doc.Close();
Spire.Pdf.PdfDocument finaldoc = new Spire.Pdf.PdfDocument();
if (File.Exists(txtSource.Text + "\\" + TraceFile + ".pdf")
&& !String.IsNullOrWhiteSpace(Convert.ToString(row["PAN_Number"])))
{
if (File.Exists(outputFile))
{
File.Delete(outputFile);
}
Part 4 - Digitally Signing the PDF Document using SpirePdf and Asymmetric Key
The following code iterates through the private key entry and finds the suitable one, once the certificate is found the unsigned file will be verified for the signed certificate or key. Once the appropriate key is found, the output will show the green tick mark which satisfies that the PDF has been digitally verified through the desired or the provided certificate.
string alias = null;
System.Collections.IEnumerator i = pk12.Aliases.GetEnumerator();
while (i.MoveNext())
{
alias = ((string)i.Current);
if (pk12.IsKeyEntry(alias))
break;
}
AsymmetricKeyParameter Akp = pk12.GetKey(alias).Key;
X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
X509Certificate[] chain = new X509Certificate[ce.Length];
for (int k = 0; k < ce.Length; ++k)
chain[k] = ce[k].Certificate;
char[] chararr = Convert.ToString(row["PAN_Number"]).ToUpper().ToCharArray();
Byte[] b = new Byte[chararr.Length];
for (int ifor = 0; ifor < chararr.Length; ifor++)
b[ifor] = Convert.ToByte(chararr[ifor]);
PdfReader reader = new PdfReader(outputUnSignedFile,b);
FileStream fout = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
PdfStamper stp = PdfStamper.CreateSignature(reader, fout, '\0');
if ((Convert.ToString(dr[0]["Res_Status"]) == "Active"))
{
stp.SetEncryption(true, Convert.ToString(row["PAN_Number"]).ToUpper(),
Convert.ToString(row["PAN_Number"]).ToUpper(),
PdfWriter.ALLOW_SCREENREADERS | PdfWriter.ALLOW_PRINTING);
}
PdfSignatureAppearance sap = stp.SignatureAppearance;
sap.SetCrypto(Akp, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
sap.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;
Spire.Pdf.PdfDocument doccount = new Spire.Pdf.PdfDocument();
doccount.LoadFromFile(txtSource.Text + "\\" + TraceFile + ".pdf");
sap.SetVisibleSignature(new iTextSharp.text.Rectangle(545,40,455,80),
doccount.Pages.Count + 1, "Signature");
stp.Close();
}
try
{
if (File.Exists(outputUnSignedFile))
{
File.Delete(outputUnSignedFile);
}
if (File.Exists(Convert.ToString(outputFileName)))
{
File.Delete(Convert.ToString(outputFileName));
}
}
catch (Exception)
{
}
finally
{
}
#endregion merging of documents
}
The above code provides a small example of digitally signing and merging PDFs. Developers can use them accordingly for their use with modification according to the requirement.
Thanks for reading! Vote if you find it helpful!