当前位置: 首页 > 知识库问答 >
问题:

Adobe Reader在“另存为”期间仍然修改PDFBox加密/锁定的PDF

钮长恨
2023-03-14

我正在做一个实现,我们的系统生成一个PDF文件供用户下载。我们的过程和系统的关键是,这个PDF文件不应该被用户或用户计算机上的程序修改(至少,不是没有恶意的),因为文件可以在稍后上传到系统中,我们需要通过比较它的哈希值来确定文件处于它的原始状态。

我们认为我们首先禁用所有权限(CanModify,CanAssembleDocument等),然后使用所有者的密码加密文档,从而实现了这一点。这阻止了我们可以访问的所有阅读器对文件的修改。现在,我们的一个用户在Acrobat Reader中打开PDF文件并将文档“另存为”到一个新的PDF文件时,就会修改该文件。我们不能用同样的读者版本(2015.006.30497)复制这个,但他每次都可以。

对我们来说,签署PDF文档不是一种选择,至少不是使用PKI或用户可以在阅读器中看到的任何可见签名。如果有某种隐形的签约选择,那将是很好的,但我不知道如何。

下面是我们用来锁定PDF的代码。出于测试目的,我们禁用了所有权限,但没有任何效果。我们使用的是PDFBox 2.0.11。

有什么建议有什么选项可以更好地锁定文件进行修改?

    public static byte[] SealFile(byte[] pdfFile, String password) throws IOException
    {   PDDocument doc =PDDocument.load(pdfFile);
        ByteArrayOutputStream bos= new ByteArrayOutputStream();
        byte[] returnvalue =null;
        int keyLength = 256;

        AccessPermission ap = new AccessPermission();

        //Disable all
        ap.setCanModifyAnnotations(false);
        ap.setCanAssembleDocument(false); .
        ap.setCanFillInForm(false);
        ap.setCanModify(false);
        ap.setCanExtractContent(false);
        ap.setCanExtractForAccessibility(false);
        ap.setCanPrint(false);

        //The user password is empty ("") so user can read without password. The admin password is
        // set to lock/encrypt the document.
        StandardProtectionPolicy spp = new StandardProtectionPolicy(password, "", ap);
        spp.setEncryptionKeyLength(keyLength);
        spp.setPermissions(ap);
        doc.protect(spp);
        doc.save(bos);
        doc.close();
        bos.flush();
        return bos.toByteArray();
    }

这将导致Adobe属性:

    /*
     *  This method is used to add the 'appendOnly flag' to the PDF document. This flag is part of
     *  the AcroForm functionality that instructs a PDF reader that the file is signed and should not be
     *  modified during the 'saved as' function. For full description see PDF specification PDF 32000-1:2008
     *  (https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf)
     *  paragraph 12.7.2 Interactive Form Dictionary
     */
    public static void addAcroFormSigFlags(PDDocument pdfDoc) {
        PDDocumentCatalog catalog = pdfDoc.getDocumentCatalog();
        PDAcroForm acroForm = catalog.getAcroForm();
        if (acroForm == null) {
            acroForm = new PDAcroForm(pdfDoc);
            catalog.setAcroForm(acroForm);

        }
        // AppendOnly:
        // If set, the document contains signatures that may be invalidated if the
        // file is saved (wirtten) in a way that alters its previous contents, as
        // opposed to an incremental update. Merely updating the file by appending
        // new information to the end of the previous version is safe (see h.7,
        // "Updating Example"). Conforming readers may use this flag to inform a
        // user requesting a full save that signatures will be invalidated and
        // require explicit confirmation before continuing with the operation
        acroForm.setAppendOnly(true);

        // SignatureExists: (Currently not used by us)
        // If set, the document contains at least one signature field. This flag
        // allows a conforming reader to enable user interface items (such as menu
        // items or pushbuttons) related to signature processing without having to
        // scan the entire document for the presence of signature fields.
//        acroForm.setSignaturesExist(true);

        // flag objects that changed (in case a 'saveIncremental' is done hereafter)
        catalog.getCOSObject().setNeedToBeUpdated(true);
        acroForm.getCOSObject().setNeedToBeUpdated(true);

    }

共有1个答案

田志
2023-03-14

即使实际签署PDF文档对您来说不是一个选项,您也可以尝试设置AcroForm标志来声明存在签名。

这将防止对这些标志敏感的程序(如Adobe Reader)将更改应用到PDF,或者至少它们应该将更改应用为增量更新,而增量更新可以通过截断文件的原始大小来撤消。

所讨论的flags条目是AcroForm字典中的SigFlags条目。

1-SignaturesExist-如果设置,文档至少包含一个签名字段。该标志允许交互式PDF处理器启用与签名处理相关的用户界面项(例如菜单项或按钮),而不必扫描整个文档以查找签名字段的存在。

2-AppendOnly-如果设置了,文档包含的签名可能会在文件以改变其先前内容的方式保存(写入)而不是增量更新时失效。仅仅通过在上一版本的末尾添加新信息来更新文件是安全的(见H.7,“更新示例”)。交互式PDF处理器可以使用该标志通知请求完全保存的用户签名将无效,并且在继续操作之前需要明确的确认。

(ISO 32000-2,表225-签名标志)

 类似资料:
  • 守护进程系统线程[Java2D Disposer](暂停(异常OutOfMemoryError))拥有:Win32Graphics环境(id=116)拥有:FontStrikeDisposer(id=117)D3DGraphicsDevice.getDeviceCaps(int)line: 108 D3DGraphicsDevice.createDevice(int)line: 87 Win32G

  • 我有以下问题: 我在我的Java应用程序中使用算法“RSA/ECB/PKCS1Padding”进行加密和解密。 问候

  • 修剪docker时遇到了问题。构建映像后,我运行“docker系统修剪-卷-a-f”,但它不会从“/var/lib/docker/overlay2”释放空间。请参阅下面 在构建映像之前,请先释放磁盘空间 塑造形象 图像生成后的大小: 它只释放了122.2MB。修剪后的尺寸: 如您所见,共有0个容器/图像: 但是"/var/lib/docker/overlay2"的大小只从3.9G减少到3.7G。如

  • 但是当我运行redis-cli时,我仍然得到输出中的值: 为什么redis的钥匙没有被移除?供参考:所有的东西都在我的本地机器上测试,只包括Redis。

  • 设置: 具有一个片段的活动,该片段通过单击按钮进行实例化。在fragment的构造函数中,使用了Bundle。在Bundle和ArrayList中 问题:分离片段时,字符串(姓氏)会按预期销毁,但数组列表会持续存在。因此,在调用片段的新实例时,会出现前一个ArrayList条目。回调不是问题所在。该行为也会在没有回调的情况下出现。 我已经检查了变量(和)和在点片段构造函数(),片段在方法()和片段

  • 问题内容: 我注意到这个构造函数非常痛苦(即使在Stack Overflow上也是如此)。即使文档明确指出,人们仍会使用它: 此构造函数的结果可能无法预测 http://java.sun.com/javase/6/docs/api/java/math/BigDecimal.html#BigDecimal(double) 我什至看到JSR-13得到批准,并提出了以下建议: 可能不推荐使用的现有规范: