如何使用所有者密码解密PDF文档?

时间:2015-01-09 19:34:16

标签: c# pdf encryption itextsharp

我需要能够从某些PDF文档中删除安全性/加密,最好使用itextsharp库。这曾经是可能的(How to decrypt a pdf file by supplying password of the file as argument using c#?),但是对库的更新change意味着该解决方案不再有效。

我知道这可以通过Aspose PDF库(example)完成,但这似乎是一个昂贵的选择。

修改

所以这一次我以为我拥有用于​​测试此文档的文档的所有者密码。但实际上我的密码是用户密码。 原因我认为它是所有者密码是因为它工作作为所有者密码而其他值不起作用。我认为用户密码代替用户密码的原因是PdfReader.unethicalreading字段设置为true(它是一个碰巧在代码中的其他地方设置的全局标志)

2 个答案:

答案 0 :(得分:12)

为了测试加密PDF文件的代码,我们需要一个加密的PDF示例。我们将使用EncryptPdf示例创建此类文件。

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();
}

使用此代码,我创建了一个加密文件hello_encrypted.pdf,我将在第一个示例中使用该文件演示如何解密文件。

您的原始问题听起来像"如何使用所有者密码解密PDF文档?"

这很容易。 DecryptPdf示例向您展示了如何执行此操作:

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();
}

我们创建一个PdfReader实例,将所有者密码作为第二个参数传递。如果我们想知道用户密码,我们可以使用computeUserPassword()方法。我们是否应该加密文件,而不是使用我们知道的所有者密码和我们计算的用户密码,并使用setEncryption()方法重新引入安全性。

但是,由于我们没有这样做,所有安全措施都会被删除,这正是您想要的。可以通过查看hello.pdf文档来检查这一点。

有人可能会说你的问题属于"它没有工作"问题只能通过&#34来回答;它适用于我"回答。可以投票结束你的问题,因为你没有提供可用于重现问题的代码示例,而任何人都可以提供证明你错误的代码示例。

幸运的是,我可以在两行之间阅读,所以我做了另一个例子。

许多PDF都是在没有用户密码的情况下加密的。任何人都可以打开它们,但会添加加密以强制执行某些限制(例如,您可以查看文档,但无法打印它)。在这种情况下,只有一个所有者密码,如EncryptPdfWithoutUserPassword示例中所示:

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();
}

现在我们获得一个加密的PDF,但可以在没有用户密码的情况下打开:hello_encrypted2.pdf

如果我们想操纵PDF,我们仍然需要知道所有者密码。如果我们没有通过密码,那么iText将正确地抛出异常:

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)

但是,如果我们不记得所有者密码怎么办?如果PDF是由第三方制作而我们不想尊重该第三方的意愿怎么办?

在这种情况下,您可以故意不道德并更改静态unethicalreading变量的值。这是在DecryptPdf2示例中完成的:

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();
}

如果文档是使用用户所有者密码加密的,则此示例将不起作用,在这种情况下,您必须至少传递一个密码,即&#34;所有者密码& #34;或者#34;用户密码&#34; (您只能使用&#34;用户&#34;密码访问PDF,这是不道德阅读的副作用)。如果仅引入了所有者密码,则在更改unethicalreading标记时,iText不需要该所有者密码来操作PDF。

但是:在这种情况下,iText中的一个错误也删除了所有者密码。这不是理想的行为。在第一个PdfDecrypt示例中,我们看到我们可以检索用户密码(如果存在用户密码),但无法检索所有者密码。这是真正的秘密。使用您所引用的旧版iText,操作后将所有者密码从文件中删除,并且所有者密码永久丢失。

我已修复此错误,修复程序在5.3.5版中。因此,现在保留了所有者密码。您可以通过查看hello2.pdf来检查这一点,DecryptPdf3是我们在&#34;不道德的#34;中解密的文件。办法。 (如果有所有者和用户密码,则两者都会被保留。)

根据这项研究,我假设您的问题不正确。您打算问:&#34;如何在没有所有者密码的情况下解密PDF文档?&#34;或&#34;如何使用用户密码解密PDF?&#34;

unfix 我修复过的错误没有意义。我们不会恢复旧iText版本的(错误)行为,但这并不意味着您无法达到您想要的效果。你只需要愚弄iText认为PDF没有被加密。

这显示在hello3.pdf示例中:

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();
}

我们现在使用PdfReader的自定义子类而不是PdfReader。我将其命名为MyReader,并添加了一个额外的方法,允许我将encrypted变量设置为false

我仍然需要使用unethicalreading并在创建MyReader实例后立即使用,我不得不欺骗此读者认为原始文件未使用{{1}进行加密方法。

这会生成文件Is iText Java library free of charge or have any fees to be paid?,该文件不再使用所有者密码加密。只要您拥有用户密码,此示例甚至可用于从使用用户密码加密的文件中删除所有密码。

我将在回答你关于Aspose不是免费的评论时给出答案。您知道iText是免费软件,但您也应该知道 free 不是的免费的同义词。有关详细信息,请阅读我对以下问题的回答:{{3}}

答案 1 :(得分:2)

您可以使用命令行工具 qpdf

来执行此操作
qpdf –-password=s3cr3t –-decrypt protected.pdf unprotected.pdf

qpdf还提供了可从其他程序中使用的API。

或者,您也可以使用命令行工具pdftk

pdftk protected.pdf input_pw s3cr3t output unprotected.pdf