How to decrypt a PDF document with the owner password?

Tags: password encryptiondecryptencryptioniText 7

I need to remove the security/encryption from some PDF documents, preferably with the iText library. This used to be possible, but after a change in version 5.3.5 of the library that solution no longer works.

Posted on StackOverflow on January 9, 2015 by Daniel Pratt

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 {
    PdfReader reader = new PdfReader(src);
    WriterProperties props = new WriterProperties()
            .setStandardEncryption("Hello".getBytes(), "World".getBytes(), EncryptionConstants.ALLOW_PRINTING,
                    EncryptionConstants.ENCRYPTION_AES_128 | EncryptionConstants.DO_NOT_ENCRYPT_METADATA);
    PdfWriter writer = new PdfWriter(new FileOutputStream(dest), props);
    PdfDocument pdfDoc = new PdfDocument(reader, writer);
    pdfDoc.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 {
    PdfReader reader = new PdfReader(src, new ReaderProperties().setPassword("World".getBytes()));
    PdfDocument pdfDoc = new PdfDocument(reader, new PdfWriter(dest));
    System.out.println(new String(reader.computeUserPassword()));
    pdfDoc.close();
}

We create a PdfReader instance passing the owner password in the ReaderProperties 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 setStandardEncryption() method in WriterProperties 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 resulting 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 {
    PdfReader reader = new PdfReader(src, new ReaderProperties().setPassword("World".getBytes()));
    PdfWriter writer = new PdfWriter(dest,
            new WriterProperties().setStandardEncryption(null, "World".getBytes(), EncryptionConstants.ALLOW_PRINTING,
                    EncryptionConstants.ENCRYPTION_AES_128 | EncryptionConstants.DO_NOT_ENCRYPT_METADATA));
    PdfDocument pdfDoc = new PdfDocument(reader, writer);
    pdfDoc.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.kernel.crypto.BadPasswordException: bad.user.password
at com.itextpdf.kernel.crypto.securityhandler.StandardHandlerUsingStandard40.checkPassword(StandardHandlerUsingStandard40.java:211)
at com.itextpdf.kernel.crypto.securityhandler.StandardHandlerUsingStandard40.initKeyAndReadDictionary(StandardHandlerUsingStandard40.java:198)
at com.itextpdf.kernel.crypto.securityhandler.StandardHandlerUsingStandard40.<init>(StandardHandlerUsingStandard40.java:81)
at com.itextpdf.kernel.crypto.securityhandler.StandardHandlerUsingStandard128.<init>(StandardHandlerUsingStandard128.java:59)
at com.itextpdf.kernel.crypto.securityhandler.StandardHandlerUsingAes128.<init>(StandardHandlerUsingAes128.java:66)
at com.itextpdf.kernel.pdf.PdfEncryption.<init>(PdfEncryption.java:193)
at com.itextpdf.kernel.pdf.PdfReader.readDecryptObj(PdfReader.java:473)
at com.itextpdf.kernel.pdf.PdfReader.readPdf(PdfReader.java:457)
at com.itextpdf.kernel.pdf.PdfDocument.open(PdfDocument.java:1257)
at com.itextpdf.kernel.pdf.PdfDocument.<init>(PdfDocument.java:241)
at com.itextpdf.kernel.pdf.PdfDocument.<init>(PdfDocument.java:210)

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 unethicalReading variable. This is done in the DecryptPdf2 example:

public void manipulatePdf(String src, String dest) throws IOException {
    PdfReader reader = new PdfReader(src);
    reader.setUnethicalReading(true);
    PdfDocument pdfDoc = new PdfDocument(reader, new PdfWriter(dest));
    pdfDoc.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 the resulting hello2.pdf file, 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:

public void manipulatePdf(String src, String dest) throws IOException {
    MyReader reader = new MyReader(src);
    reader.setUnethicalReading(true);
    reader.decryptOnPurpose();
    PdfDocument pdfDoc = new PdfDocument(reader, new PdfWriter(dest));
    pdfDoc.close();
    reader.close();
}
 
class MyReader extends PdfReader {
    public MyReader(String filename) throws IOException {
        super(filename);
    }
    public void decryptOnPurpose() {
        encrypted = false;
    }
}

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.

Click this link if you want to see how to answer this question in iText 5.