Using Temp File Method for signing in iText Java instead of depending on System Memory - itext

We are using PKCS7 Signatures and are signing a document on the server using a signature created on the client. We are creating Pdf Objects in memory, and want to switch to a system where we do not need to depend on system memory. Currently, I am saving the PdfSignatureAppearance Object created while generating hash in session,and then using this object from session when I receive a response from client (Signed Hash Content). Could you please help me figure out a way through which I do not need to save the PdfSignatureAppearance object in session and can directly use the Temp File for signing?
First, we generate a hash of file after inserting signature appearance as shown in the code below:
File file = new File("To be signed file location")
char version = '\u0000'
ByteArrayOutputStream out = new ByteArrayOutputStream()
String outputFile = "Signed file name"
FileOutputStream fileOutputStream = new FileOutputStream(outputFile)
PdfReader pdfReader = new PdfReader(new FileInputStream(file.absolutePath))
PdfStamper stamper = new PdfStamper(pdfReader, out, version, true)
PdfFormField pdfFormField = PdfFormField.createSignature(stamper.getWriter())
String signatureName = "Signature1"
pdfFormField.setWidget(new Rectangle((float) 20, (float) 20, (float) 100, (float) 60), (PdfName) null)
pdfFormField.setFlags(4)
pdfFormField.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g"))
pdfFormField.setFieldName(signatureName)
pdfFormField.setPage(1)
stamper.addAnnotation(pdfFormField, i)
stamper.close()
pdfReader = new PdfReader(out.toByteArray())
stamper = PdfStamper.createSignature(pdfReader, fileOutputStream, version, (File) null, true)
PdfSignatureAppearance appearance = stamper.getSignatureAppearance()
appearance.setLayer2Text("Digitally Signed by Name")
appearance.setImage(Image.getInstance(esignRequestCO.logoLocation))
appearance.setAcro6Layers(true)
Calendar cal = Calendar.getInstance()
cal.setTime(new Date())
cal.add(12, 5)
appearance.setSignDate(cal)
appearance.setVisibleSignature(signatureName)
int contentEstimated = 16384
HashMap<PdfName, Integer> exc = new HashMap()
exc.put(PdfName.CONTENTS, contentEstimated * 2 + 2)
PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED)
dic.setReason(appearance.getReason())
dic.setLocation(appearance.getLocation())
dic.setDate(new PdfDate(appearance.getSignDate()))
appearance.setCryptoDictionary(dic)
appearance.preClose(exc)
InputStream inp = appearance.getRangeStream()
byte[] bytes = IOUtils.toByteArray(inp)
String hash = DigestUtils.sha256Hex(bytes)
As soon as we get the signed content, we insert the content in the file using same appearance object as shown below:
int contentEstimated = 16384
PdfSignatureAppearance appearance = "PdfSignatureAppearance object from session generated in previous step"
byte[] p7barray = "signed content here".bytes
byte[] paddedSig = new byte[contentEstimated]
System.arraycopy(p7barray, 0, paddedSig, 0, p7barray.length)
PdfDictionary dic2 = new PdfDictionary()
dic2.put(PdfName.CONTENTS, (new PdfString(paddedSig)).setHexWriting(true))
appearance.close(dic2)

Related

Field validation in editable pdf while filling data

I have editable pdf with fields name, roll number , DOB and city, I want to validate the fields like, for example when I enter the value in roll number field, if name is empty then it should alert "name is empty" and similar to roll number and DOB and city.
DOB - Need to validate mm/dd/yyyy format.
I am able to get fields and values using below iTextSharp code, but how can validate and and show alert to user.
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(filename);
using (MemoryStream outputStream = new MemoryStream())
{
//PdfStamper stamper = new PdfStamper(reader, );
using (PdfStamper stamper = new PdfStamper(reader, outputStream, '\0', true))
{
PdfWriter writer = stamper.Writer;
AcroFields acroFields = reader.AcroFields;
AcroFields.Item dateField = acroFields.GetFieldItem("D");
iTextSharp.text.pdf.PdfAction pdfAction = iTextSharp.text.pdf.PdfAction.JavaScript("app.alert('hello');", writer);
iTextSharp.text.pdf.PdfDictionary widgetRefDict = (iTextSharp.text.pdf.PdfDictionary)iTextSharp.text.pdf.PdfReader.GetPdfObject(dateField.GetWidgetRef(0));
iTextSharp.text.pdf.PdfDictionary actionDict = widgetRefDict.GetAsDict(iTextSharp.text.pdf.PdfName.AA);
if (actionDict == null)
{
actionDict = new iTextSharp.text.pdf.PdfDictionary();
// add the newly created action dict
widgetRefDict.Put(iTextSharp.text.pdf.PdfName.AA, actionDict);
}
actionDict.Put(iTextSharp.text.pdf.PdfName.V, pdfAction);
stamper.Close();
reader.Close();
}
byte[] content = outputStream.ToArray();
// Write out PDF from memory stream.
using (FileStream fs = File.Create("d:\\Page1-output.pdf"))
{
fs.Write(content, 0, (int)content.Length);
}
}

How to place the Same Digital signatures to Multiple places in PDF using itextsharp.net

I have implemented Digital Signature using iTextSharp Dll to sign PDF files with a single signature creating empty signature fields and update the signature field with signed hash working fine. Now, I want to place the same digital signature in every page of pdf. It's my client requirement.
I’m using the following code:
public class MyExternalSignatureContainer : IExternalSignatureContainer
{
private readonly byte[] signedBytes;
public MyExternalSignatureContainer(byte[] signedBytes)
{
this.signedBytes = signedBytes;
}
public byte[] Sign(Stream data)
{
return signedBytes;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
}
Below code used in program
PdfReader reader = new PdfReader(unsignedPdf);
FileStream os = File.OpenWrite(tempPdf);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(36, 748, 144, 780), 1, null);
for (int i = 1; i < 8; i++)
{
var signatureField = PdfFormField.CreateSignature(stamper.Writer);
var signatureRect = new Rectangle(200, 200, 100, 100);
signatureField.Put(PdfName.T, new PdfString("ClientSignature_"+i.ToString()));
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
signatureField.Put(PdfName.V, PRef);
signatureField.Put(PdfName.F, new PdfNumber("132"));
signatureField.SetWidget(signatureRect, null);
signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);
PdfDictionary xobject1 = new PdfDictionary();
PdfDictionary xobject2 = new PdfDictionary();
xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
xobject2.Put(PdfName.AP, xobject1);
signatureField.Put(PdfName.AP, xobject1);
signatureField.SetPage();
PdfDictionary xobject3 = new PdfDictionary();
PdfDictionary xobject4 = new PdfDictionary();
xobject4.Put(PdfName.FRM, appearance.GetAppearance().IndirectReference);
xobject3.Put(PdfName.XOBJECT, xobject4);
signatureField.Put(PdfName.DR, xobject3);
stamper.AddAnnotation(signatureField, i);
}
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
stamper.Close();
byte[] SignedHash = DoEsign(SHA256Managed.Create().ComputeHash(appearance.GetRangeStream());
os.close();
reader.close();
reader = new PdfReader(tempPdf))
os = File.OpenWrite(signedPdf)
IExternalSignatureContainer external1 = new MyExternalSignatureContainer(SignedHash);
MakeSignature.SignDeferred(reader, signatureFieldName, os, external1);
os.close();
reader.close();
Please suggest me to complete the task
To give all signature fields the same single value wrapping the newly created signature container, they must all reference the same indirect object as value. Unfortunately iText creates the indirect object for the signature value only after the application code had the chance to add its additional fields which in turn require a reference to that signature value object. Thus, the application code has to anticipate the object number that indirect object will have.
This anticipation or prediction of the object number is very delicate, it depends on the exact same use case and can also become incorrect as the result of minor changes in the iTextSharp library
To make this easier, the application code should add those signature fields with their signature value references as late as possible, so there are as few other new indirect objects created as possible until iText creates the value indirect object.
As it turns out, the ModifySigningDictionary method of an IExternalSignatureContainer is a good position for that.
As soon as one adds one's code there, another issue pops up: There is no means to set the anticipated object number in a PdfIndirectReference instance externally. One way to get around this is to mimic such a reference using a PdfLiteral. (Well, probably one could also use reflection for this.)
Furthermore it turns out that one best creates the appearance streams to use by all one's additional signature fields before building that PdfLiteral mimicking a PdfIndirectReference as this simplifies the calculation of the object number iText will use for the actual value object.
With this in mind, here a proof-of concept. This proof of concept makes use of an IExternalSignature instance for actually signing. This is not a necessary precondition, one can also use an IExternalSignatureContainer instead with only a few changes, even an ExternalBlankSignatureContainer as in the question to later finalize the signature using MakeSignature.SignDeferred.
So given cipher parameters cp (private key material, e.g. pk.Key for an Org.BouncyCastle.Pkcs.AsymmetricKeyEntry pk) and a certificate chain chain, one would use
PdfReader reader = new PdfReader(SRC);
FileStream os = new FileStream(DEST, FileMode.Create, FileAccess.Write);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = "Reason1";
appearance.Contact = "";
appearance.Location = "Location1";
appearance.Acro6Layers = false;
appearance.Image = null;
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(10, 10, 100, 100), reader.NumberOfPages, null);
IExternalSignature externalSignature = new PrivateKeySignature(cp, "SHA-256");
AllPagesSignatureContainer allPagesContainer = new AllPagesSignatureContainer(appearance, externalSignature, chain);
MakeSignature.SignExternalContainer(appearance, allPagesContainer, 8192);
with this external signature container class
public class AllPagesSignatureContainer : IExternalSignatureContainer
{
public AllPagesSignatureContainer(PdfSignatureAppearance appearance, IExternalSignature externalSignature, ICollection<X509Certificate> chain)
{
this.appearance = appearance;
this.chain = chain;
this.externalSignature = externalSignature;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.FILTER, PdfName.ADOBE_PPKMS);
signDic.Put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
PdfStamper stamper = appearance.Stamper;
PdfReader reader = stamper.Reader;
PdfDictionary xobject1 = new PdfDictionary();
PdfDictionary xobject2 = new PdfDictionary();
xobject1.Put(PdfName.N, appearance.GetAppearance().IndirectReference);
xobject2.Put(PdfName.AP, xobject1);
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");
for (int i = 1; i < reader.NumberOfPages; i++)
{
var signatureField = PdfFormField.CreateSignature(stamper.Writer);
signatureField.Put(PdfName.T, new PdfString("ClientSignature_" + i.ToString()));
signatureField.Put(PdfName.V, PRefLiteral);
signatureField.Put(PdfName.F, new PdfNumber("132"));
signatureField.SetWidget(appearance.Rect, null);
signatureField.Put(PdfName.SUBTYPE, PdfName.WIDGET);
signatureField.Put(PdfName.AP, xobject1);
signatureField.SetPage();
Console.WriteLine(signatureField);
stamper.AddAnnotation(signatureField, i);
}
}
public byte[] Sign(Stream data)
{
String hashAlgorithm = externalSignature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
IDigest messageDigest = DigestUtilities.GetDigest(hashAlgorithm);
byte[] hash = DigestAlgorithms.Digest(data, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
byte[] extSignature = externalSignature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, externalSignature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
}
PdfSignatureAppearance appearance;
ICollection<X509Certificate> chain;
IExternalSignature externalSignature;
}
The predicted indirect object number of the signature value in the line
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + 1 + 2*(reader.NumberOfPages - 1)) + " 0 R");
strictly depends upon the use case being "exactly one signature field per page". For different use cases the estimate the prediction would differ.
I stress this here once again because e.g. the OP of this question did not take this into account when trying "to place multiple signatures on single page".
Another strict requirement for the object number prediction above is that the PdfStamper is created as above, i.e. not in append mode. If the signature is applied as an incremental update, i.e. in append mode, the lines above have to be replaced by
stamper.Writer.AddToBody(new PdfNull(), stamper.Writer.PdfIndirectReference, true);
PdfIndirectReference PRef = stamper.Writer.PdfIndirectReference;
PdfLiteral PRefLiteral = new PdfLiteral((PRef.Number + reader.NumberOfPages) + " 0 R");
This made a difference in the context of this question; the first line, adding an indirect null object to the PDF, is necessary to make sure that in case of PDFs with object streams the object stream object number has already been determined and does not slip between the next objects, resulting in an off-by-one error for our prediction.
Beware: While this procedure creates something which does not violate the letter of the PDF specifications (which only forbid the cases where the same field object is referenced from multiple pages, be it via the same or via distinct widgets), it clearly does violate its intent, its spirit. Thus, this procedure might also become forbidden as part of a Corrigenda document for the specification.

How can I get ByteRange with iText7?

As the picture show , I need to get byte array from ByteRange to do some verify , they are 0 to 840 and 960 to 1200.
I found the similar question : In Itext 7, how to get the range stream to sign a pdf?
iText in its own verification code needs to do the same thing. It does so in its SignatureUtil class. Thus, one can simply borrow from that code, e.g. like this:
try ( PdfReader pdfReader = new PdfReader(SOURCE_PDF);
PdfDocument pdfDocument = new PdfDocument(pdfReader);) {
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
for (String name : signatureUtil.getSignatureNames()) {
PdfSignature signature = signatureUtil.getSignature(name);
PdfArray b = signature.getByteRange();
RandomAccessFileOrArray rf = pdfReader.getSafeFile();
try ( InputStream rg = new RASInputStream(new RandomAccessSourceFactory().createRanged(rf.createSourceView(), SignatureUtil.asLongArray(b)));
OutputStream result = TARGET_STREAM_FOR_name_BYTES) {
byte[] buf = new byte[8192];
int rd;
while ((rd = rg.read(buf, 0, buf.length)) > 0) {
result.write(buf, 0, rd);
}
}
}
}
(RetrieveSignedRanges test testExtractSignedBytes)
If you want the byte range as a byte[] in memory, you can use a ByteArrayOutputStream as TARGET_STREAM_FOR_name_BYTES and retrieve the resulting byte array from it.

Adding page numbers to pdf using iTextSharp on second pass is not working

So as the title says I am trying to add page numbers to an existing pdf document, I have researched this problem and come up with a few tutorials/examples.
Here is the simplest example i can find
My code Compiles and runs successfully however the changes are not reflected in the pdf
my code
byte[] bytes = File.ReadAllBytes(filePath + ".pdf");
PdfReader pdfReader = new PdfReader(bytes);
using (MemoryStream ms = new MemoryStream())
{
using (PdfStamper stamper = new PdfStamper(pdfReader, ms,'\0',true))
{
int n = pdfReader.NumberOfPages;
for (int i = 1; i <= n; i++)
{
creatPageCountFooter(i + 1, n).WriteSelectedRows(0, -1, 34, 803, stamper.GetOverContent(i));
//ColumnText.ShowTextAligned(stamper.GetUnderContent(i), Element.ALIGN_RIGHT, new Phrase(i.ToString(), blackFont), 568f, 15f, 0);
}
ms.ToArray();
}
pdfReader.Close();
}
File.WriteAllBytes(filePath + ".pdf", bytes);
the function "creatPageCountFooter"
/**
* Create a header table with page X of Y
* #param count the page number
* #param Total the total number of pages
* #return a table that can be used as header
*/
protected PdfPTable creatPageCountFooter(int count,int Total)
{
PdfPTable pageCount = new PdfPTable(3);
pageCount.TotalWidth=250;
BaseFont bfTimes = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
iTextSharp.text.Font times = new iTextSharp.text.Font(bfTimes, 6);
PdfPCell Cell = new PdfPCell(new Phrase(DateTime.Now.ToString("dd MMM yyyy"), times));
Cell.HorizontalAlignment = Element.ALIGN_RIGHT;
Cell.Border = 0;
pageCount.AddCell(Cell);
Cell = new PdfPCell(new Phrase(count.ToString() +" / "+ Total.ToString(), times));
Cell.HorizontalAlignment = Element.ALIGN_MIDDLE;
Cell.Border = 0;
pageCount.AddCell(Cell);
Cell = new PdfPCell(new Phrase("Company name " + DateTime.Now.ToString("yyyy"), times));
Cell.HorizontalAlignment = Element.ALIGN_MIDDLE;
Cell.Border = 0;
pageCount.AddCell(Cell);
return pageCount;
}
As a further note I have checked that this code actually runs and I have tried writing the file over the existing document or creating a new document and both times the changes don't reflect.
I will provide further details if required.
Your code writes the byte[] bytes to a file:
File.WriteAllBytes(filePath + ".pdf", bytes);
But the only code in which that variable is set, is the initial
byte[] bytes = File.ReadAllBytes(filePath + ".pdf");
Thus, it is not surprising that the changes are not reflected in the pdf result file because you simply write the original bytes unchanged.
I assume you meant to set bytes to the contents of the memory stream in this line
ms.ToArray();
but forgot the bytes =. Unfortunately, though, that array retrieval call happens too early, it is still inside the using PdfStamper block:
using (PdfStamper stamper = new PdfStamper(pdfReader, ms,'\0',true))
{
...
ms.ToArray();
}
Just like in the sample you refer to, the array has to be retrieved outside that using block, so that it is retrieved after the PdfStamper implicitly gets closed during disposal. Thus:
using (PdfStamper stamper = new PdfStamper(pdfReader, ms,'\0',true))
{
...
}
bytes = ms.ToArray();

SQL FileStream Opening XPS FIle

I am trying to read an XPS file that I have inserted into my database using the Filestream attribute. Here is my code thus far.
RewardsDataContext dx = new RewardsDataContext();
var img = (from c in dx.RwProductsTests
select c).FirstOrDefault();
byte[] buffer = img.Blob.ToArray();
MemoryStream ms = new MemoryStream();
ms.Write(buffer, 0, buffer.Length);
ms.Position = 0;
Package package = Package.Open(ms, FileMode.OpenOrCreate, FileAccess.ReadWrite);
XpsDocument MyDoc = new XpsDocument(package);
dv1.Document = MyDoc.GetFixedDocumentSequence();
dx.Dispose();
and I get the following error.
XpsDocument URI is null. Use XpsDocument constructor that takes URI parameter.