Adding additional page to signed pdf and sign it again - java

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.

Related

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

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.

How to detect a signed pdf is valid with iText?

For a project, I need to check if a digital signature is valid or not.
I already developed a piece of code to do it.
I tested with some pdf (correct or not) and the code seems to work.
But there is a case where the verification does not work.
In Adobe Reader I've the message "Document has been altered or corrupted since it was signed." with a "beautiful" red cross...
Unfortunately, I don't know how to test this case which is the most important.
Because if I understand, the signature is correct, but it's the document which is altered, not the signature.
I suppose we can use the hash of file, but I don't know how to extract correctly this information.
How to check that with Itext ?
For information, I tested the methods as below :
pdfpkcs7.verify(): return true
acroFields.signatureCoversWholeDocument(signame): return true
pdfReader.isRebuilt() : return false
Adobe has documented their selection of algorithms accepted in signatures in this document. In particular they therein state for all applicable PDF versions
DSA only supports SHA1 and adbe.pkcs7.detached
The OP's document, on the other hand, uses DSA with SHA256. The effect is that
Adobe Reader cannot positively validate the hash value but
iText (not restricting itself to those algorithm combinations) can.
Actually the signing algorithm information in signature itself is questionable to start with, it merely uses the OID 1.2.840.10040.4.1 which only indicates that DSA is used, not the digest algorithm it is used with. Strictly speaking a different OID should have been used:
for SHA1withDSA: 1.2.840.10040.4.3
for SHA256withDSA: 2.16.840.1.101.3.4.3.2
(There even are some alternatives most of which meanwhile have been deprecated.)
Finding only 1.2.840.10040.4.1 (only DSA) validators have to guess / deduce the digest, and assuming SHA1 (like Adobe Reader does) is not completely unreasonable.
iText most likely guesses the used SHA256 because this digest algorithm is also used to calculate the document digest in the signed attribute messageDigest.
iText :Avoiding the PDF Digital Signature Vulnerabilities with iText. links
In February 2019, a team of security researchers from the Ruhr-University Bochum in Germany published details of vulnerabilities in the digital signing system of many PDF viewers and online PDF digital signing services. After investigating these vulnerabilities, we found that recent updates to iText introduced in version 7.1.5 mean we are not vulnerable to the described attacks.
However, it was determined that the current names of the methods for checking and verifying signatures could be improved to better reflect their functionality. Therefore we have decided to deprecate the SignatureUtil#verifySignature and PdfPKCS7#verify methods, and replace them with SignatureUtil#readSignatureData and PdfPKCS7#verifySignatureIntegrityAndAuthenticity which have been introduced in iText 7.1.6.
Below are code examples showing how to use the signature integrity and authenticity checks for each of these versions of iText.
iText 7.1.6:
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
// Checks that signature is genuine and the document was not modified.
boolean genuineAndWasNotModified = false;
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
try {
PdfPKCS7 signature1 = signatureUtil.readSignatureData(signatureFieldName);
if (signature1 != null) {
genuineAndWasNotModified = signature1.verifySignatureIntegrityAndAuthenticity();
}
} catch (Exception ignored) {
// ignoring exceptions,
// we are only interested in signatures that are passing the check successfully
}
pdfDocument.close();
The second and third types of attack, Incremental Saving Attack (ISA) and Signature Wrapping (SWA), are based on attempts to insert into the PDF file some malicious data to override signed data.
In order to validate every signature, it is necessary to check if it covers the entire file, otherwise iText cannot be sure that signature in question indeed signs the data that constitutes the current PdfDocument and all its contents. Even though the signature is authentic and signed data integrity is intact, iText will always check that signed data is not only a part of the PDF content but is also a valid PDF file.
iText implements this check in the SignatureUtil.signatureCoversWholeDocument(String fieldName) method. For both ISA and SWA attacks, this method will return false, because some unsigned data was inserted into the file:
We actually addressed this specific issue back in November 2018 (before we were aware of the reported vulnerabilities) with a rewrite of the signatureCoversWholeDocument() method. Providing you use iText 7.1.5 (or newer) the following code should correctly validate the PDF:
PdfDocument pdfDocument = new PdfDocument(new PdfReader(input));
String signatureFieldName = "Signature1";
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
Boolean completeDocumentIsSigned = signatureUtil.signatureCoversWholeDocument(signatureFieldName)}
if (!completeDocumentIsSigned)
{
// handle PDF file which contains NOT signed data
}
pdfDocument.close();
Note: Signatures that do not cover the entire document cannot be considered as verifying the PDF file, because content that is not covered by the signature might have been modified since the signature was created.

iText adding ltv info

I'm trying to 'complete to ltv' a pdf that is already signed and i found this code using itext:
http://developers.itextpdf.com/question/how-enable-ltv-timestamp-signature
public void addLtv(String src, String dest, OcspClient ocsp, CrlClient crl, TSAClient tsa)
throws IOException, DocumentException, GeneralSecurityException {
PdfReader r = new PdfReader(src);
FileOutputStream fos = new FileOutputStream(dest);
PdfStamper stp = PdfStamper.createSignature(r, fos, '\0', null, true);
LtvVerification v = stp.getLtvVerification();
AcroFields fields = stp.getAcroFields();
List<String> names = fields.getSignatureNames();
String sigName = names.get(names.size() - 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 {
for (String name : names) {
v.addVerification(name, ocsp, crl,
LtvVerification.CertificateOption.WHOLE_CHAIN,
LtvVerification.Level.OCSP_CRL,
LtvVerification.CertificateInclusion.NO);
}
}
PdfSignatureAppearance sap = stp.getSignatureAppearance();
LtvTimestamp.timestamp(sap, tsa, null);
}
I read it has a 'problem' with Adobe because the timestamp applied is not recognized as LTV-enabled and suggests applying a new dss to solve this.
My questions:
Can this info be added before applying the timestamp? If i'm adding a dss to complete, i could add TSA timestamp info (ocsp, crl...) too (maybe with a fake sign to get info) and then apply the timestamp without needing a new dss again.
If yes... this is approved by ETSI? Can iText handle it? I noticed that addVerification adds info from signatures already included, but seems i can't add the required info with this method. There's another way to add 'free' verifications or addVerification let's me and i didn't notice?
If no... Why? Then why i not need to timestamp again the new dss added?
As you can see... i'm not an expert and i need some help.
Thanks a lot for your help!
My questions:
Can this info be added before applying the timestamp? If i'm adding a dss to complete, i could add TSA timestamp info (ocsp, crl...) too (maybe with a fake sign to get info) and then apply the timestamp without needing a new dss again.
If yes... this is approved by ETSI? Can iText handle it? I noticed that addVerification adds info from signatures already included, but seems i can't add the required info with this method. There's another way to add 'free' verifications or addVerification let's me and i didn't notice?
If no... Why? Then why i not need to timestamp again the new dss added?
Technically you can add any validation related information before applying the signature / time stamp the relate to. Actually you even have to do this in case of ol'fashioned ISO 32000-1 signatures which required validation information to be in a signed attribute.
Whether such information are accepted by verifiers, depends.
ETSI TS 102 778-4 V1.1.1 says:
4.3 Validation Process
It is recommended that that validation process be as follows:
The "latest" document Time-stamp should be validated at current time with validation data collected at the
current time.
The "inner" document Time-stamp should be validated at previous document Time-stamp time with the
validation data present (and time-stamped for the successive enveloping time-stamps) in the previous DSS.
The signature and the signature Time-stamp should be validated at the latest innermost LTV document Timestamp
time using the validation data stored in the DSS and time-stamped (by the successive enveloping timestamps)
Validation of documents without document Time-stamps is outside the scope of this profile.
If a verifier validates according to these recommendations, it will not accept your validation information as you want it to, at least it will not recognize the time stamp stamping the information for its validation.
But as these only are recommendations and other TS or EN documents might recommend differently, the verifiers you are interested in may accept your validation information as desired by you.

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.

Creating a non-editable pdf document using PD4ml

I am using pd4ml to create pdf documents, however I don't want the user to be able to edit those documents using ms word 2013.
here is what I have tried so far
pd4ml = new PD4ML();
pd4ml.setPageSize(PD4Constants.A4);
pd4ml.setPageInsetsMM(new Insets(TOPVALUE, LEFTVALUE, BOTTOMVALUE, RIGHTVALUE));
pd4ml.setHtmlWidth(USERSPACEWIDTH);
pd4ml.enableImgSplit(false);
pd4ml.disableHyperlinks();
//some more code
pd4ml.render(arrayOfURLs, byteArrayOutputStream);
//some more code
then I read the PD4ML API documentation and added this line of code pd4ml.generatePdfa(true); I thought the problem was solved when I opened the document in adobe reader and saw this message "
this file claims compliance with the pdf/a standard and has been opened read-only", but of course it was still editable; so any suggestions on how this is done in pd4ml, or any reference to an api I can use to add this restriction to the generated pdf will be more than welcomed.
Tried this?
This if from the documentation by the way:
AllowModify
public static final int AllowModify
Document access permission (bit 4, value = 8).
Modify the contents of the document by operations other than those controlled by bits 6, 9, and 11.
See Also:
PD4ML.setPermissions(String, int, boolean), Constant Field Values
Also you may want to do:
pd4ml.setPermissions("", 0xffffffff ^ PD4Constants.AllowModify, false);
to disable the modification.
More information here: http://pd4ml.com/cookbook/pd4ml_pdf_security.htm

Categories

Resources