首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何用所有者密码解密PDF文档?

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

Stack Overflow用户
提问于 2015-01-09 19:34:16
回答 2查看 10.7K关注 0票数 11

我需要能够从一些PDF文档中删除安全性/加密,最好使用itextsharp库。这曾经是可能的(如何通过使用c#提供文件的密码作为参数来解密pdf文件?),但是最近对库的变化意味着解决方案不再有效。

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

编辑

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

EN

回答 2

Stack Overflow用户

发布于 2015-01-10 13:42:42

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

代码语言:javascript
运行
复制
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();
}

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

您最初的问题听起来像是“我如何用所有者密码解密PDF文档?”

这很容易。DecryptPdf示例向您展示了如何做到这一点:

代码语言:javascript
运行
复制
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文档来检查这一点。

有人可能会说,你的问题属于“它不起作用”的范畴,只能用“它对我有用”的答案来回答。一个人可以投票结束你的问题,因为你没有提供一个可以用来重现问题的代码样本,而任何人都可以提供一个代码样本来证明你错了。

幸运的是,我能读懂字里行间,所以我做了另一个例子。

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

代码语言:javascript
运行
复制
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,但是它可以在没有用户密码的情况下打开:encrypted2.pdf

如果我们想要操作PDF,我们仍然需要知道所有者密码。如果我们不传递密码,那么iText将正确地抛出一个异常:

代码语言:javascript
运行
复制
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示例中完成的:

代码语言:javascript
运行
复制
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();
}

如果文档是用用户和所有者密码加密的,则此示例将无法工作,在这种情况下,您必须传递至少一个密码,要么是“所有者密码”,要么是“用户密码”(您只能使用“用户”密码访问PDF是不道德读取的副作用)。如果只引入了所有者密码,则如果更改了iText标志,则不需要该所有者密码来操作unethicalreading

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

我已经修复了这个bug,修复程序在第5.3.5版中。因此,所有者密码现在被保留下来。您可以通过查看hello2.pdf来检查它,这是我们以“不道德”的方式解密的文件。(如果存在所有者和用户密码,则两者都保留。)

基于这项研究,我假设你的问题是不正确的。您的意思是:“没有所有者密码,我如何解密PDF文档?”或者“如何用用户密码解密PDF?”

取消修复我曾经修复过的bug是没有意义的。我们不会恢复旧iText版本的(错误)行为,但这并不意味着您无法实现您想要的目标。您只需欺骗iText,使其认为PDF没有加密。

DecryptPdf3示例中显示了这一点:

代码语言:javascript
运行
复制
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实例之后,我必须欺骗这个读者,让他们认为原始文件没有使用decryptOnPurpose()方法加密。

这将导致文件hello3.pdf,该文件不再使用所有者密码加密。此示例甚至可以用于从使用用户和所有者密码加密的文件中删除所有密码,只要您有用户密码。

在结束这一答复时,我将对您关于Aspose不是免费的评论作出答复。您知道iText是免费软件,但您也应该知道,免费不是免费的同义词。请阅读我对以下问题的回答,以获得更多信息:iText的Java库是免费的还是要付费的?

票数 12
EN

Stack Overflow用户

发布于 2015-01-09 21:48:30

您可以使用命令行工具qpdf来完成这一任务。

代码语言:javascript
运行
复制
qpdf –-password=s3cr3t –-decrypt protected.pdf unprotected.pdf

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

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

代码语言:javascript
运行
复制
pdftk protected.pdf input_pw s3cr3t output unprotected.pdf
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27867868

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档