We need to embed a file into a pdf with java, and we need to have it crypted with certificate
I've found something using (if I'm not worng) itext some months ago, but I cannot find it anymore
The certs are on a smart card
Encrypting a PDF is done with a public certificate. Once a PDF is encrypted, only the person with the corresponding private certificate can open the PDF. In your scenario, this would mean that only the person who owns the smart card can open the document.
First you need to extract the public certificate from the smart card. The main question here is: do you want to do this in Java? If so, do you want to do this using PKCS#11? Using MSCAPI? Using a smart card API? I honestly don't think that's what you want to do. I think you want the owners of the smart card to extract their public certificate manually and to send it to you. If this assumption is wrong, you need to post another question: how to get a public certificate from a smart card.
Once you have this certificate, you can encrypt the PDF like this:
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Certificate cert = getPublicCertificate("resources/encryption/public.cer");
stamper.setEncryption(new Certificate[]{cert},
new int[]{PdfWriter.ALLOW_PRINTING}, PdfWriter.ENCRYPTION_AES_128);
stamper.close();
reader.close();
The public certificate is stored in the file public.cer. That's the file your end user extracted from the smart card.
If you want to encrypt attachments separately, you'll have to do this at the level of those files. It isn't clear if you're talking about PDFs or about another type of document.
I have copy pasted the source code snippet from my book.
Related
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.
i am working on a project that requires a password to access every downloaded PDF file. The password is to be fetched from a database (I am using MySQL).
I have searched for a java code that would relate to this type of task but I have barely got any. Most answers are inclined to form filling after the document is already downloaded.
I have thought about making the PDF files templates that would only display information if the password field that would be auto-filled (In case i am forced to use this option) but i am afraid that would take a lot of doing.
i have read on the context of iTEXT by Bruno Lowagie directed by a question on Stackoverflow but the closest i got was this snippet that answers a question by "affan" on how to fill a PDF automatically using external data from a database.
I recon that this snippet is to be used to fill in an already open PDF document.
This is the snippet:
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.setField(key, value);
stamper.setFormFlattening(true);
stamper.close();
reader.close();
Someone please help out with how i could automatically fill the password field of a PDF that requires a password for access.
It seems that you are mixing two different things:
Form fields, in which you have a text field for which a specific field flag is set so that the text that is added is obfuscated (e.g. "Password" is shown as "********"). Reading your requirement, this is not what you want. Also: I see that you set the form flattening to true and that removes all the field information, including the field flag that changes a text field into a password field.
Encryption, in which you protect a document with one or two passwords.
I assume that you want to protect a document, but I have to admit that my assumption could be wrong. I have no idea why you are mentioning form field in your question.
If you protect a document with an owner password only, everyone has access to that document, but you can put in place some permissions. This is not a secure solution, because many viewers ignore the owner password; moreover, it is very easy to remove the owner password.
If you protect a document with an owner and a user password, only people who know either the owner password or the user password can view the document. All the content of the document is encrypted (except for the Metadata if you want the metadata to be accessible).
Please read the answer to the following questions:
How to protect a PDF with a username and password?
How to protect an already existing PDF with a password?
iText setEncryption error
BadPasswordException: Bad user password (this is probably the most interesting one)
For a more elaborate answer, please consult the FAQ on the official web site.
for iText 5: How to decrypt a PDF document with the owner password?
for iText 7: How to decrypt a PDF document with the owner password?
Form fields (even when using the password flag on a text field) and using encryption to protect a document, are two completely separate topics in the PDF specification (ISO-32000). You can't "automatically fill a password field" in a PDF form, and hope that you can use the value of that field to open a protected document.
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.
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;
}
I've been working on settting passwords on PDFs to prevent copy/paste and allow printing, add watermarks and set an owner password to prevent further changes.
Everything works well as expected, no issue there.
Then I downloaded this free for 15 days pdf removal tool, a-pdf. In a blink it removes all protection, no matter the complexity of the password (tried with 50 char length will all kind of chars).
I see there are other methods in itextPDF to encrypt a document. I used the following:
File f = new File("C:/TEMP/zip/waterMarked.pdf");
String hardPassword = "D 5BaIZQ# CqAk+NQCW)7Dkgb#i&02ifu!2TMX*d 0TGK(j(Kq";
byte[] hardPasswordByte = hardPassword.getBytes();
PdfReader reader = new PdfReader("C:/TEMP/zip/Original_document-9.pdf");
FileOutputStream out = new FileOutputStream(f);
PdfStamper stamp = new PdfStamper(reader, out);
//first argument is the user password. If set to something it asks for password when opening file, not wanted.
stamp.setEncryption(null, hardPasswordByte, PdfWriter.ALLOW_PRINTING, true);
//do stuff on the stamper, save file.
Does anyone knows a better way to protect PDF documents from Java code ?
PDF files support 2 passwords: user password and owner password. A user can view the PDF file if he knows any of these passwords. If the file has a user password, when the file is opened with a PDF viewer, the viewer asks the user to enter a password and either the user or owner passwords will work. If the file has only an owner password, the document is displayed automatically and the password is required when trying to change the file's access rights. This is the flow of operations suggested by PDF specification, but in reality it works like this: if the file is protected with a user password, brute force approach is required for password cracking, the longer the password is the longer it takes to crack. Problem is your real users need the password to open the file. If the file is protected only with an owner password, there is a default decryption key (remember, any viewer can display the PDF file without requesting a password) and the application that processes the PDF file decides whether to respect or not the document access rights. Once the file has been decrypted, it is saved without encryption and the output file has no longer a password. Since your documents have only the owner password, the tool removes it without problems using the default decryption key.
There are a few solutions (more or less related to iText) depending on your audience:
simple PDF encryption (with the problems above) if your audience is widespread, for example you publish papers on a website; 3rd party DRM solution, more complex and requires various plugins installed on your users' computers; certificate encryption (no sure if iText supports it), again complex, requires each user to have a digital certificate and documents access is defined for each user. Last 2 options work in a controlled enterprise environment.