Can I fill in an encrypted PDF with iTextSharp? - c#

I have a fillable, saveable PDF file that has an owner password (that I don't have access to). I can fill it out in Adobe reader, export the FDF file, modify the FDF file, and then import it.
Then I tried to do it with iText for .NET. I can't create a PdfStamper from my PdfReader because I didn't provide the owner password to the reader. Is there any way to do this programmatically or must I recreate the document?
Even using FdfReader requires a PdfStamper. Am I missing anything? Anything legal that is - I'm pretty sure I could hack the document, but I can't. Ironically, recreating it would probably be ok.

This line will bypass edit password checking in iTextSharp:
PdfReader.unethicalreading = true;

[I found this question several months after it was posted and I'm posting this solution now for anyone who comes across this question in a search.]
I was in the exact same situation: my customer had a PDF with fillable fields that I needed to programmatically access. Unfortunately the PDF was password protected and they didn't have the password so I found couldn't work with their file.
What I discovered was that iTextSharp version 4.0.4 (and later) enforces password restrictions, earlier versions did not.
So I downloaded version 4.0.3 and sure enough it worked. In my case I didn't even have to change my code to use this older version.
You can download 4.0.3 (and all other versions) at SourceForge.

Two important things
Set PdfReader.unethicalreading = true to prevent BadPasswordException.
Set append mode in PdfStamper's constructor, otherwise the Adobe Reader Extensions signature becomes broken and Adobe Reader will display following message: "This document contained certain rights to enable special features in Adobe Reader. The document has been changed since it was created and these rights are no longer valid. Please contact the author for the original version of this document."
So all you need to do is this:
PdfReader.unethicalreading = true;
using (var pdfReader = new PdfReader("form.pdf"))
{
using (var outputStream = new FileStream("filled.pdf", FileMode.Create, FileAccess.Write))
{
using (var stamper = new iTextSharp.text.pdf.PdfStamper(pdfReader, outputStream, '\0', true))
{
stamper.AcroFields.Xfa.FillXfaForm("data.xml");
}
}
}
See How to fill XFA form using iText?

Unless someone else chimes in, I'll assume the answer is "No"
I wound up regenerating the PDF in an unencrypted form.

Related

Visible signature created using iText 7 not shown in chrome

I am using iText 7 to sign pdf documents.
This works without problems, and the signature is shown as valid.
In addition to the digital signature, i want to show a visual representation on the pdf. This is described in the digital signature book chapter 2.4 Creating different signature appearances.
The produced pdf shows this appearance if i open it using adobe reader.
The first image is a pdf created using word and the save as pdf functionality.
The second image is a demo pdf i just downloaded random.
If i open the first pdf in chrome, the signature appearance text is not shown, but if i open the pdf which was initially created using word, the signature apperance is missing.
Any ideas on whats wrong with the pdf which doesn't show the signature appearance in chrome?
edit: Links to the documents
Pdf which shows signature in chrome
https://1drv.ms/b/s!AkROTDoCWFJnkd5VOFjUHZfpQXzJWQ?e=MeyZje
Pdf which doesn't show signature in chrome
https://1drv.ms/b/s!AkROTDoCWFJnkd5W5P3MCbb8fwLASA?e=zsmks0
edit 2: Code sample
The following code sample will sign a pdf document using a local certificate and place some text into the SignatureAppearance which is not shown in chrome.
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Signatures;
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace PdfSigning.Lib.Helpers
{
public class SignPdfTest
{
public static byte[] SingPdfUsingCertificate(X509Certificate2 cert2, byte[] pdfToSign)
{
var apk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(cert2.PrivateKey).Private;
IExternalSignature pks = new PrivateKeySignature(apk, DigestAlgorithms.SHA512);
var cp = new Org.BouncyCastle.X509.X509CertificateParser();
var chain = new[] { cp.ReadCertificate(cert2.RawData) };
using (PdfReader reader = new PdfReader(new MemoryStream(pdfToSign)))
{
using (MemoryStream fout = new MemoryStream())
{
StampingProperties sp = new StampingProperties();
sp.UseAppendMode();
PdfSigner signer = new PdfSigner(reader, fout, sp);
PdfSignatureAppearance appearance = signer.GetSignatureAppearance();
appearance.SetPageNumber(1);
appearance.SetLayer2Text("Hello world");
appearance.SetLayer2FontSize(8);
Rectangle pr = new Rectangle(10, 10, 200, 100);
appearance.SetPageRect(pr);
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION);
appearance.SetPageRect(pr);
signer.SignDetached(pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
return fout.ToArray();
}
}
}
}
}
private static void SignDocumentUsingCertificateConfiguration()
{
try
{
var certificateSignatureConfiguration = new CertificateSignatureConfiguration();
var cert2 = new X509Certificate2(#"C:\temp\MyCertificate.pfx", "mypassword", X509KeyStorageFlags.Exportable);
CertificatePdfSigner certPdfSigner = new CertificatePdfSigner(certificateSignatureConfiguration);
byte[] signedPdf = PdfSigning.Lib.Helpers.SignPdfTest.SingPdfUsingCertificate(cert2, File.ReadAllBytes(#"C:\temp\WordSaveAsPdf.pdf"));
File.WriteAllBytes(#"C:\temp\WordSaveAsPdf_Signed.pdf", signedPdf);
Console.WriteLine("Done");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
In short
Chrome appears to not read object streams of hybrid reference PDFs, in particular not in the incremental update added during signature creation.
iText, on the other hand, puts nearly all its changes during signing into an object stream.
Thus, Chrome is not aware of the added signature and its appearance.
One can resolve the situation by forcing iText not to create an object stream here.
What is special about the Word generated source PDF?
PDF files contain object cross reference information which map object numbers to offsets of the respective starts of these objects in the file. These information can be stored in two ways, as cross reference table and (since PDF 1.5) also as cross reference stream. Also since PDF 1.5 the format allows to put non-stream objects into so called object streams which allows superior compression as only stream contents can be compressed.
As most PDF viewers at the time PDF 1.5 has been introduced did not support cross reference and object streams, a mixed, hybrid reference style was also introduced then. In this style the basic objects in a PDF which are strictly necessary to display it, are added normally (not in object streams) and are referenced from cross reference tables. Extra information which is not strictly necessary is then added in object streams and referenced from cross reference streams.
MS Word creates PDFs in this hybrid style and is virtually the only software to do so.
What is special about the iText signed result PDF?
iText put nearly all the changes into an object stream in a new incremental update.
Apparently, though, Chrome does not fully support object and cross reference streams, in particular not if combined with further incremental updates.
Thus, Chrome is not aware of the added signature and its visualization.
How to resolve the problem?
What we need to do, therefore, is convince iText that it shall not add important data in an object stream during signing. Due to member variable visibilities this is not as easy as one would like; I used reflection here for that.
In your code simply use the following PdfSignerNoObjectStream instead of PdfSigner:
public class PdfSignerNoObjectStream : PdfSigner
{
public PdfSignerNoObjectStream(PdfReader reader, Stream outputStream, StampingProperties properties) : base(reader, outputStream, properties)
{
}
protected override PdfDocument InitDocument(PdfReader reader, PdfWriter writer, StampingProperties properties)
{
try
{
return base.InitDocument(reader, writer, properties);
}
finally
{
FieldInfo propertiesField = typeof(PdfWriter).GetField("properties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
WriterProperties writerProperties = (WriterProperties) propertiesField.GetValue(writer);
writerProperties.SetFullCompressionMode(false);
}
}
}
Beware, though, tweaking iText functionality like this is not guaranteed to work across versions. I tested it for a recent iText-7.1.7-SNAPSHOT development state; I expect it to also work for the previous 7.1.x versions.
Is this a Chrome bug? Or an iText bug? Or what?
Most likely it's kind of both.
On one hand the Chrome PDF viewer appears to have issues with hybrid reference PDFs. Considering how long they have been part of the PDF format, that is somewhat disappointing.
And on the other hand the PDF specification requires in the context of hybrid reference documents:
In general, the objects that may be hidden are optional objects specified by indirect references. [...]
Items that shall be visible include the entire page tree, fonts, font descriptors, and width tables. Objects that may be hidden in a hybrid-reference file include the structure tree, the outline tree, article threads, annotations, destinations, Web Capture information, and page labels.
(ISO 32000-1, section 7.5.8.4 Compatibility with Applications That Do Not Support Compressed Reference Streams)
In the case at hand an (updated) page object is in the object stream, i.e. hidden from viewers not supporting cross reference and object streams.
Currently iText 7 PdfDocument attempts to enforce FullCompression on PdfWriters if the underlying PdfReader has any cross reference stream (HasXrefStm):
writer.properties.isFullCompression = reader.HasXrefStm();
(PdfDocument method Open)
Probably it shouldn't enforce that if the PdfReader also is identified as hybrid reference stream (HasHybridXref).
This might be simply caused by the chrome build-in PDF reader. As far as I understood his case, the person who requested help from Chrome devs in this question has received some answers and was redirected to another part of the forum where he could get help. I can try to recreate the problem with itext-sharp 5 (I used that in a previous project) and see if that signature is not shown in Chrome but the odds won't be good.
This sounds an awful lot like a case of the "Needs Appearances" flag not being set. Back in my day (wheeze) iText form fields were generated with as little graphical data as possible, and would set the \NeedsAppearances flag to true, letting the PDF viewer in question (Acrobat Reader was about it back then) that it needed to generate the form fields' appearances before trying to draw them to screen.
And visible PDF Signatures are held in form fields.
So its at least theoretically possible that you can fix this programmatically by telling iText to (re?)generate the form field appearances.

How does PdfEncryptionSettings API work in Itext?

It may be I'm not familiar with the iText library, but I've got the code below for protecting the document and used "PdfWriter.ALLOW_PRINTING" for Encryption setting:
using (Stream output = new FileStream(_outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
PdfEncryptor.Encrypt(_pdfReader, output, true, docKey, docKey, PdfWriter.ALLOW_PRINTING);
output.Close();
}
The output file it generated does have the password protection, but after I open it, the pdf could still be edited, I could change bookmarks, add comments, etc and save the changes.
Is there anything I may have missed, or how should we understand the permission settings here? My iText version is 5.5.10.0
The issue with the code above is the same user password and owner password are used. Making them different and opening document with user password resolved the issue

Password-protected pdf file [duplicate]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
I need to be able to remove the security/encryption from some PDF documents, preferably with the itextsharp library. This used to be possible (How to decrypt a pdf file by supplying password of the file as argument using c#?), but a more recent change to the library means that solution no longer works.
I know this can be done with the Aspose PDF library (example), but that appears to be an expensive option.
Edit
So all this time I thought I was in possession of the owner password for the document I was using to test this. But in fact the password I had was the user password. The reason I thought it was the owner password was because it worked as the owner password and other values did not work. I believe the reason the user password worked in place of the user password was the fact that the PdfReader.unethicalreading field was set to true (it's a global flag that happened to be set elsewhere in code).
In order to test code to encrypt a PDF file, we need a sample PDF that is encrypted. We'll create such a file using the EncryptPdf example.
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setEncryption("Hello".getBytes(), "World".getBytes(),
PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128 | PdfWriter.DO_NOT_ENCRYPT_METADATA);
stamper.close();
}
With this code, I create an encrypted file hello_encrypted.pdf that I will use in the first example demonstrating how to decrypt a file.
Your original question sounds like "How can I decrypt a PDF document with the owner password?"
That is easy. The DecryptPdf example shows you how to do this:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src, "World".getBytes());
System.out.println(new String(reader.computeUserPassword()));
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
We create a PdfReader instance passing the owner password as the second parameter. If we want to know the user password, we can use the computeUserPassword() method. Should we want to encrypt the file, than we can use the owner password we know and the user password we computed and use the setEncryption() method to reintroduce security.
However, as we didn't do this, all security is removed, which is exactly what you wanted. This can be checked by looking at the hello.pdf document.
One could argue that your question falls in the category of "It doesn't work" questions that can only be answered with an "it works for me" answer. One could vote to close your question because you didn't provide a code sample that can be use to reproduce the problem, whereas anyone can provide a code sample that proves you wrong.
Fortunately, I can read between the lines, so I have made another example.
Many PDFs are encrypted without a user password. They can be opened by anyone, but encryption is added to enforce certain restrictions (e.g. you can view the document, but you can not print it). In this case, there is only an owner password, as is shown in the EncryptPdfWithoutUserPassword example:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.setEncryption(null, "World".getBytes(),
PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128 | PdfWriter.DO_NOT_ENCRYPT_METADATA);
stamper.close();
reader.close();
}
Now we get a PDF that is encrypted, but that can be opened without a user password: hello_encrypted2.pdf
We still need to know the owner password if we want to manipulate the PDF. If we don't pass the password, then iText will rightfully throw an exception:
Exception in thread "main" com.itextpdf.text.exceptions.BadPasswordException: Bad user password
at com.itextpdf.text.pdf.PdfReader.readPdf(PdfReader.java:681)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:181)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:230)
at com.itextpdf.text.pdf.PdfReader.<init>(PdfReader.java:207)
at sandbox.security.DecryptPdf.manipulatePdf(DecryptPdf.java:26)
at sandbox.security.DecryptPdf.main(DecryptPdf.java:22)
But what if we don't remember that owner password? What if the PDF was produced by a third party and we do not want to respect the wishes of that third party?
In that case, you can deliberately be unethical and change the value of the static unethicalreading variable. This is done in the DecryptPdf2 example:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader.unethicalreading = true;
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
This example will not work if the document was encrypted with a user and an owner password, in that case, you will have to pass at least one password, either the "owner password" or the "user password" (the fact that you have access to the PDF using only the "user" password is a side-effect of unethical reading). If only an owner password was introduced, iText does not need that owner password to manipulate the PDF if you change the unethicalreading flag.
However: there used to be a bug in iText that also removed the owner password(s) in this case. That is not the desired behavior. In the first PdfDecrypt example, we saw that we can retrieve the user password (if a user password was present), but there is no way to retrieve the owner password. It is truly secret. With the older versions of iText you refer to, the owner password was removed from the file after manipulating it, and that owner password was lost for eternity.
I have fixed this bug and the fix is in release 5.3.5. As a result, the owner password is now preserved. You can check this by looking at hello2.pdf, which is the file we decrypted in an "unethical" way. (If there was an owner and a user password, both are preserved.)
Based on this research, I am making the assumption that your question is incorrect. You meant to ask: "How can I decrypt a PDF document without the owner password?" or "How can I decrypt a PDF with the user password?"
It doesn't make sense to unfix a bug that I once fixed. We will not restore the (wrong) behavior of the old iText versions, but that doesn't mean that you can't achieve what you want. You'll only have to fool iText into thinking that the PDF wasn't encrypted.
This is shown in the DecryptPdf3 example:
class MyReader extends PdfReader {
public MyReader(String filename) throws IOException {
super(filename);
}
public void decryptOnPurpose() {
encrypted = false;
}
}
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
MyReader.unethicalreading = true;
MyReader reader = new MyReader(src);
reader.decryptOnPurpose();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
Instead of PdfReader, we are now using a custom subclass of PdfReader. I have named it MyReader and I have added an extra method that allows me to set the encrypted variable to false.
I still need to use unethicalreading and right after creating the MyReader instance, I have to fool this reader into thinking that the original file wasn't encrypted by using the decryptOnPurpose() method.
This results in the file hello3.pdf which is a file that is no longer encrypted with an owner password. This example can even be used to remove all passwords from a file that is encrypted with a user and an owner password as long as you have the user password.
I'll conclude this answer with a comment in answer to your remark about Aspose not being free of charge. You know that iText is free software, but you should also know that free isn't a synonym of for free. Please read my answer to the following question for more info: Is iText Java library free of charge or have any fees to be paid?
You can do it with the command line tool qpdf:
qpdf –-password=s3cr3t –-decrypt protected.pdf unprotected.pdf
qpdf also provides an API to be used from other programs.
Alternatively, you can also use the command line tool pdftk:
pdftk protected.pdf input_pw s3cr3t output unprotected.pdf

Error in siging a pdf filled using itext

I am using iText to fill a pdf. This pdf contains a xfa from, I fill this form through a xml file . After filling the form user need to download it and sign it manually. So far so good everything works fine every field in form filled properly. If user sign the file adobe reader make a new copy of file with signature.
But when user try to sign that pdf it gives following error and the newly generated file with signature doesn't save data, It make all field blank.
At least one signature has problems
When user sign pdf adobe reader also give a popup on signature verification
But if i fill the same pdf manually without using itext it allow me to sign pdf successfully
this is the code that i am using to fill pdf with xml data :
public static byte[] FillXfaForm(byte[] byteArray, String xmlFilePath)
{
PdfReader reader = new PdfReader(byteArray);
using (MemoryStream ms = new MemoryStream())
{
using (PdfStamper stamper = new PdfStamper(reader, ms,'\0',true))
{
stamper.Writer.CloseStream = false;
stamper.AcroFields.Xfa.FillXfaForm(xmlFilePath);
}
if(File.Exists(xmlFilePath))
{
File.Delete(xmlFilePath);
}
return ms.ToArray();
}
}
And here is the screen shot of error message
Please help me to solve this issue.
Finally I fix this problem
The cause of this problem is that i update whole xml document to fill xfa from but when i only update the data part not the whole xml it works without any error.
I don't know what is the difference it really create as in this similar question "Bruno Lowagie" state that you can either use full xml replace or you can change data part only.
How can I set XFA data in a static XFA form in iTextSharp and get it to save?
But for me it allow me to sign the document only if I replace data part not the whole xml data.
I hope it will help someone facing similar problem.

How to Check PDF is Reader enabled or not using C#?

My only requirement is to find a selected pdf in a folder is Reader enabled or not, more specifically if usage rights are defined in a way that allows people to add annotations (e.g. comments).
I am doing this in windows application. If I click a button, an event is triggered searching a folder for PDF files. This event needs to check whether or not the PDFs in the folder are Reader enabled for comments. If they are, I need to remove the comment usage rights or revert the PDF back to its original version.
My code can only find PDF files in the folder. I don`t know how to check if the selected PDF is comment enabled or not. Please be gentle and suggest solution.
Here's my code:
private void button1_Click(object sender, EventArgs e)
{
{
string[] filePaths = Directory.GetFiles("D:\\myfolder\\pdffolder");
List<ListViewItem> files = new List<ListViewItem>();
foreach (string filePath in filePaths)
{
---need to check comment enabled or not---
}
}
}
You want to know if a PDF is Reader enabled or not. Reader enabling is established by adding a digital signature known as a Usage Rights (UR) signature. If you have an instance of PdfReader, you can check whether or not a PDF is Reader enabled by using the hasUsageRights() method:
PdfReader reader = new PdfReader(path_to_file);
boolean isReaderEnabled = reader.hasUsageRights();
Usage rights can encompass many different things, such as allowing people to comment, allowing people to save a filled out form, allowing people to sign a document,...
To find out which rights are enabled, you have to inspect either the UR or the UR3 dictionary (note that UR is deprecated, but there may still be PDFs out there that have a UR dictionary):
PdfDictionary catalog = reader.getCatalog();
PdfDictionary perms = catalog.getAsDict(PdfName.PERMS);
PdfDictionary ur = null;
if (perms != null) {
PdfDictionary ur = perms.getAsDict(PdfName.UR);
if (ur == null)
ur = perms.getAsDict(PdfName.UR3);
}
}
If ur remains null, there are no usage rights. If you only want to check if commenting is enabled, you'll have to inspect the entries of the ur dictionary. There will be an /Annots entry with as value an array with values such as Create, Delete, Modify, Copy, Import, Export, Online and SummaryView. FOr the full overview of possible entries, see Table 255 "Entries in the UR transform parameters dictionary" of ISO-32000-1.
You can remove all usage rights like this:
PdfReader reader = new PdfReader(path_to_file);
if (reader.hasUsageRights()) {
reader.removeUsageRights();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(path_to_new_file));
stamper.close();
}
It is impossible to remove only the usage rights for commenting while preserving other usage rights (if present). Just removing the /Annots entry from the /UR or /UR3 dictionary will break the digital signature that enables usage rights. This digital signature is created with a private key owned by Adobe and no third party tool (other than an Adobe product) is allowed to use that key.
Final note:
all code snippet were written in Java, but iTextSharp has corresponding methods or properties in C#. It shouldn't be a problem to port the snippets to C#.
In many cases, it's sufficient to change a lower case into an upper case:
Java: object.add(something);
C#: object.Add(something);
Or you have to remove the set/get:
Java: object.setSomething(something);
C#: object.Something = something;
Thanks for all who takes effect to my question. I finally found the answer in a similar way by reading the PDF and check for a particular string (particular string presented if Comment enabled on the PDF).
The particular string starts with /Annot ....., First I read the PDF thru System.IO, then store in a string and looking for the particular string, If the searching string available then the PDF is comment enabled else not.

Categories