How to sign and verify multiple signature pdf file using iText? [duplicate] - java

Why PdfStamper increase the size of my signed pdfs every i use?
That's my code:
private static void test(String src, String pwd, String dest) throws Exception {
byte[] pwdByte = pwd != null ? pwd.getBytes() : null;
PdfReader r = null;
PdfStamper stp = null;
FileOutputStream fos = null;
try {
r = new PdfReader(src, pwdByte);
fos = new FileOutputStream(dest);
stp = new PdfStamper(r, fos, '\0', true);
} finally {
stp.close();
fos.close();
r.close();
}
}
If i call test, the resulting pdf increases the size depending on the signed info:
My initial pdf has a LTV sign but i need to add a timestamp. I need to addLtv() some pdfs and later addLtvNoTs() because it's a different TSA. This makes calling twice PdfReader in mode append and every time i do it increases my pdf in 190kb aprox. That means increasing it 380kb.
http://developers.itextpdf.com/question/how-enable-ltv-timestamp-signature
I've tested calling above test method with a pdf with a simple sing and every time i call test() on the resulting pdf it increase 3,5kb each time (i think due to small sign info).
Why pdfstamper increase my pdf size if it's signed even i don't add any change? How can i avoid it? 390kb it's a problem because i need to resign thousand of pdfs and store them.
I'm using itext 5.5.9
Thanks in advance!

This is a strange question because it reveals that you don't understand the concept of a digital signature. When you sign a PDF, you want to make sure that the signature breaks as soon as someone changes a single byte. Every time you sign a document, you protect the bytes of the previous revision of the document by adding a signature dictionary of which the contents contain a signed hash of at least the bytes you want to protect.
See the following figure that shows a schema of a PDF that is signed three times:
Suppose that you would want to add a fourth signature, and you would want the resulting file to have the same size as before, you would have to change some of the bytes in the existing document. That would break at least one of the existing signatures.
You mention LTV. LTV implies adding a Document Security Store (DSS) and a Timestamp signature. The same rules apply: you have to add extra bytes, because the moment you change existing bytes, you'll break an existing signature:
Every time the certificate of the latest timestamp signature expires, you have to repeat the same procedure:
The total file size will always increase.

Related

Signing dynamically generated PDF file with attached signature

I'm working on an application that allows users to generate certain PDF documents using templates, then sign those documents and store the signed documents for later verification.
When using completely external (detached, say RSA) signature, I can do the following optimization: as a document is generated using a template from a small structured dataset, I can ignore the document itself and only store structured data along with signature.
This greatly reduces requirements for disk space and throughput, as PDF file is 50-100 times bigger than it's source data.
However, recently we've got a requirement to use signatures embedded in PDF files themselves. Here it is - iText comes to rescue!
The idea is to have the following process:
Generate PDF on server. This includes getting the source data, transforming it to PDF using template, then using iText insert an empty signature container to it.
Send PDF to client for signing
Extract user signature from PDF. Store it separately along with source data, template version, and byte ranges used for signing
When a document is requested, generate the same document from source data, then insert stored signature using previously recorded byte ranges.
So far, sounds so good.
However, the problem is getting repeatable result during transformation chain
Source Data -> PDF -> PDF + Signature Container
The step Source Data -> PDF obviously works fine. The problem is with inserting a Signature Container. Each time I'm using the same code on the same PDF i get different result (in bytes) of resulting PDF + Container.
I'm using roughly the following code to prepare document for signing:
PdfReader reader = new PdfReader(resultDocument);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfStamper stamper = PdfStamper.createSignature(reader, baos, '\0');
PdfSignatureAppearance sap = stamper.getSignatureAppearance();
sap.setReason("Sign reason");
sap.setLocation("Sign location");
sap.setVisibleSignature(new Rectangle(36, 748, 144, 780), 1, "sig");
sap.setSignDate(externallyStoredSignDate)
PdfSignature dic = new PdfSignature(
PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.setReason(sap.getReason());
dic.setLocation(sap.getLocation());
dic.setContact(sap.getContact());
dic.setDate(new PdfDate(sap.getSignDate()));
sap.setCryptoDictionary(dic);
HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
exc.put(PdfName.CONTENTS, new Integer(8192 * 2 + 2));
sap.preClose(exc);
InputStream data = sap.getRangeStream(); // Whooops - different every time!
Any help on how to get repeatable PDFs with prepared space for signatures is greatly appreciated!
Thanks in advance.

Signing Big PDF files using PDBOX

I'm working on a project used for signing PDF files via Adobe AATL certificates and I'm using the PDFBOX library. My code below is working for files that are smaller than 4MB and breaks for files that are bigger.
NOTE: the code below does not throw an exception. However, the file that it signed is not opening due to integrity issue.
public void signDetached(PDDocument document, File inFile, SignProperties sigProps)
throws IOException {
int accessPermissions = SigUtils.getMDPPermission(document);
if (accessPermissions == 1) {
throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
}
// create signature dictionary
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName(sigProps.getName());
signature.setLocation(sigProps.getLocation());
signature.setReason(sigProps.getReason());
// the signing date, needed for valid signature
signature.setSignDate(Calendar.getInstance());
// Optional: certify
if (accessPermissions == 0)
SigUtils.setMDPPermission(document, signature, 2);
SignatureOptions signatureOptions = new SignatureOptions();
// Size can vary, but should be enough for purpose.
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 3);
// register signature dictionary and sign interface
document.addSignature(signature, this, signatureOptions);
FileOutputStream stream = new FileOutputStream(inFile);
// write incremental (only for signing purpose)
document.saveIncremental(stream);
document.close();
stream.close();
}
UPDATE: PDFBOX version is 2.0.8
Input File anything bigger than 4 MB
This code of yours
FileOutputStream stream = new FileOutputStream(inFile);
// write incremental (only for signing purpose)
document.saveIncremental(stream);
writes into the input file. Don't do that (the javadoc warns about this) because PDFBox also reads from that file when calculating the signature while also writing into it... this results in a really nasty mess. So always save into a different file when signing. And update to the current version.

Adding additional page to signed pdf and sign it again

Is it possible to add additional page to the signed PDF and sign it again without breaking the first signature.
I read in the adobe documentation under incremental updates that it may be possible.
However, I'm not sure if that applies to all the content or just the Annotations (commenting), form fill-in and digital signatures.
I tried to do this by using Apache PDFBox in Java, by signing the document, then loading it, appending the page to it, saving it using saveIncremental() and signing it again.
However, the first signature gets invalidated.
Here is my generateTest method that generates the new PDF:
public byte[] generateTest(InputStream requestPdfIn) throws IOException {
// Create a document and add a page to it
// PDDocument document = new PDDocument();
PDDocument document = PDDocument.load(requestPdfIn);
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
COSBase item = document.getPages().getCOSObject().getItem(COSName.KIDS);
((COSUpdateInfo) item).setNeedToBeUpdated(true);
COSArray kids = (COSArray) item;
kids.setNeedToBeUpdated(true);
((COSUpdateInfo) kids.get(0)).setNeedToBeUpdated(true);
document.getPage(0).getCOSObject().setNeedToBeUpdated(true);
page.getCOSObject().setNeedToBeUpdated(true);
document.getPages().getCOSObject().setNeedToBeUpdated(true);
COSDictionary dict = page.getCOSObject();
while (dict.containsKey(COSName.PARENT)) {
COSBase parent = dict.getDictionaryObject(COSName.PARENT);
if (parent instanceof COSDictionary) {
dict = (COSDictionary) parent;
dict.setNeedToBeUpdated(true);
}
}
document.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);
//document.getDocumentCatalog().getStructureTreeRoot().getCOSObject().setNeedToBeUpdated(true);
// Save the results and ensure that the document is properly closed:
ByteArrayOutputStream confirmationPdfOut = new ByteArrayOutputStream();
document.saveIncremental(confirmationPdfOut);
document.close();
return confirmationPdfOut.toByteArray();
}
I found in this post that all the COSObjects need to have a flag needToBeUpdated set to true.
However, that still doesn't help when trying to add another page to the document, as the first signature gets invalidated when I try to verify the signature using Acrobat Reader.
Is it even possible? Is it possible with the PDFBox?
No, it is not possible. Adding pages to an already signed PDF is not allowed.
In detail
I read in the adobe documentation under incremental updates that it may be possible.
Indeed it is possible to add changes to a PDF without touching the former revision. Thus, if that former revision was signed, the signature mathematically remains valid, it still signs the correct hash value.
But the PDF specification and its main interpretation (by Adobe that is) contain additional restrictions, cf. this stack overflow answer. As you find there at most the following changes are allowed to signed documents:
Adding signature fields
Adding or editing annotations
Supplying form field values
Digitally signing
At least Adobe Acrobat (Reader) does test for such changes in addition to the check for mathematical validity even if numerous other validation services don't.
Thus, your task to add additional page to the signed PDF and sign it again without breaking the first signature cannot be implemented.

LTV of Certifying Signatures

I've been working with iText to do digital signatures on PDF files for the past weeks and based on what i've understood that there is two ways to add the information to the PDF to make it LTV enabled:
Adding the information with the code provided in the iText example, this method requires the signature to be already present because the DSS & VRI dictionaries it creates references the signature.
Embedding the crl bytes & ocsp response in the signature at signing time.
Eventhough the first method results in a nice and tidy pdf file the problem with is is that it modifies the pdf file to create/append the entries which results in an invalidation of the certifying signature,
the second one works fine but it increases the pdf size substantially depending on the size of the crl list (that will also probably increase overtime).
Wrapping up, is there any other way to make the certifying signature LTV enabled other than embedding the information in the signature itself? Is there any way to create the dds/vri dictionaries at signing time?
EDIT: Here's more info as requested on the comments:
The code used to add the ltv information:
public static void processDocumentLtv(String filePath) throws IOException, GeneralSecurityException, DocumentException {
long startTime = System.currentTimeMillis();
File original = new File(filePath);
File temp = new File(filePath + ".ltv");
PdfReader reader = new PdfReader(filePath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(temp), '\0', true);
LtvVerification ltvVerification = stamper.getLtvVerification();
OcspClient ocspClient = new OcspClientBouncyCastle();
AcroFields fields = stamper.getAcroFields();
List<String> signatureNames = fields.getSignatureNames();
String sigName = signatureNames.get(signatureNames.size() - 1);
PdfPKCS7 pkcs7 = fields.verifySignature(sigName);
Certificate[] chain = pkcs7.getSignCertificateChain();
X509Certificate x509certificate = pkcs7.getSigningCertificate();
byte[] ocspResponse = ocspClient.getEncoded(x509certificate, CertificateUtils.getParent(x509certificate, chain), null);
Collection<byte[]> crlBytes = CertificateUtils.fetchCrlBytes(x509certificate, chain);
Collection<byte[]> ocspBytes = null;
if (ocspResponse != null) {
ocspBytes = new ArrayList<>();
ocspBytes.add(ocspResponse);
}
ltvVerification.addVerification(sigName, ocspBytes, crlBytes, null);
ltvVerification.merge();
stamper.close();
reader.close();
Files.copy(temp.toPath(), original.toPath(), StandardCopyOption.REPLACE_EXISTING);
temp.delete();
logger.info("Took {}ms to do add the ltv information to the document.", (System.currentTimeMillis() - startTime));
}
The document samples:
Before trying to add LTV data: https://www.dropbox.com/s/79ll23ndt9mbh3g/pdf-sample-pre-ltv.pdf?dl=0
After running through the code above: https://www.dropbox.com/s/hjl73es6hrqspi3/pdf-sample-post-ltv.pdf?dl=0
I'm using Adobe Reader DC v15.017.20053 as my PDF Viewer.
Observations with the sample file
I performed some tests with the OP's sample PDF. Indeed, Adobe Acrobat (Reader) does not like PAdES-4 LTV additions generated by iText to a PDF with no-changes-allowed certification, and it is a bit brain-split about it, saying both
Some of the changes that have been made to this document since this signature was applied are not permitted by the document author.
and
There have been no changes made to this document since this signature was applied.
(Adobe Acrobat signature properties dialog after pressing Compute Modifications List)
This situation remained even when I removed any change in excess of the addition of the LTV information (iText additionally adjusts the document modification date meta data), I eventually even removed the added Extensions entry ESIC (BaseVersion 1.7, ExtensionLevel 5) which indicates to a PDF viewer that PAdES-4 LTV content may be present, only the DSS reference and contents remained.
Thus, Adobe Acrobat violates the PAdES-4 specification which requires
DocMDP restrictions (see ISO 32000-1 1 clause 12.8.2.2) shall not apply to incremental updates to a PDF document containing a DSS dictionary and associated VRI, Certs, CRLs and OCSPs.
(ETSI TS 102 778-4 V1.1.2 (2009-12) Annex A.1 Document Security Store)
even though Leonard Rosenthol (the Adobe PDF evangelist at the time) assured on the iText mailing list
I checked with my engineers and they indeed verified that LTV is fully supported on DocMDP/Cert signatures.
(Reply to "Verify certified (with transform method DocMDP) signatures" dated Jan 17, 2012; 3:15pm)
I have not checked two options, though, probably Adobe Acrobat only adheres to the PAdES-4 requirement above if the certification signature is a PAdES-3 signature, or if the certified document already in the just certified version at least contains an Extensions entry ESIC (BaseVersion 1.7, ExtensionLevel 5).
The document at hand contains a legacy ISO 32000-1 signature (which can be regarded a PAdES-2 signature but which can also be regarded a PAdES-unaware signature) and indicates PDF version 1.3 without an ESIC extension entry.
Before finally calling it an Adobe Acrobat bug, I'd try changing using a PAdES-3 signature and the ESIC extension entry (or an ADBE one according to PAdES-4 section 4.4).
The question itself
Wrapping up, is there any other way to make the certifying signature LTV enabled other than embedding the information in the signature itself? Is there any way to create the dds/vri dictionaries at signing time?
The PAdES-4 additions are described as referring to signatures in prior revisions of the document, not to signatures added in the same revision. Thus, while it would be technically possible to add the information in the same revision, there is no guarantee they will be used by a conforming PDF viewer.

Applying Password to a PDF file which is having a digital Signature in java

I am facing the problem with my digital signature verification when I am applying password to the file from java code.
In my scenario I am getting the file which is already having a digital signature.
I want to put password on pdf file which is digital signed.
I am able to put the password but the concern is that now the digital signature is not verified.
Suggest me some way how we can achieve it.
Is it possible to have it?
Please let me know if any more clarification required from my side....
You cannot put a password on a PDF previously signed without breaking the signature. A signature checks whether the signed data is unchanged as a stream of bytes. A password changes those bytes. Thus, the signature will break if a password does applied afterwards.
You might consider signing and encrypting at the same time.
If that is not possible in your case, you might consider adding the signed PDF to a PDF portfolio and encrypting that portfolio. In that case the signed PDF would have to be opened from the portfolio which implies decrypting it.
PS I assumed in all of the above that you meant regular PDF signatures, not XFA signatures.
Firstly try to convert the digital Signature into a string and after that use this piece of code
public static String passwordProtectFile(File srcFile, File destFile,
String password) throws DocumentException, IOException {
String message = null;
PdfReader pdfReader = new PdfReader(new FileInputStream(srcFile));
FileOutputStream fos = new FileOutputStream(destFile);
PdfStamper stamper = new PdfStamper(pdfReader, fos);
stamper.setEncryption(password.getBytes(), password.getBytes(),
PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128
| PdfWriter.DO_NOT_ENCRYPT_METADATA);
stamper.close();
fos.close();
pdfReader.close();
message = "The Given File is Now Password Protected";
return message;
}

Categories

Resources