java 通过使用PDFBox保留现有未签名的签名字段添加新签名

brvekthn  于 2023-01-29  发布在  Java
关注(0)|答案(1)|浏览(299)

我想签名已经包含签名域的PDF。我需要添加新的签名域,保留现有的未签名的签名域。签名此类PDF后,我发现通过代码添加的新签名域始终无效。显示“文档已更改”。
下面的代码用于计算文档的哈希:

private DocumentSignatureStructure createSignatureStructureAndComputeHash(byte[] inputFile, File tempFile,
                                                                              SignatureProperties sigProperties)
            throws IOException, NoSuchAlgorithmException {

        try (FileOutputStream fos = new FileOutputStream(tempFile);
             PDDocument doc = PDDocument.load(inputFile);
             SignatureOptions signatureOptions = new SignatureOptions();) {

            signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);
            signatureOptions.setPage(sigProperties.getPage() - 1);
            if (sigProperties.isVisibleSignature()) {
                PDRectangle rect = createSignatureRectangle(doc, sigProperties);
                signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, rect, sigProperties));
            }

            PDSignature signature = new PDSignature();
            signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
            signature.setSignDate(Calendar.getInstance());
            doc.addSignature(signature, signatureOptions);
            ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(fos);

            MessageDigest digest = MessageDigest.getInstance(sigProperties.getHashAlgorithm().getAlgoName());
            byte[] hashBytes = digest.digest(IOUtils.toByteArray(externalSigning.getContent()));
            String base64Hash = Base64.toBase64String(hashBytes);
            externalSigning.setSignature(new byte[0]);
            int offset = signature.getByteRange()[1] + 1;
            IOUtils.closeQuietly(signatureOptions);
            return DocumentSignatureStructure.builder().offset(offset)
                    .hashValue(base64Hash)
                    .build();
        }
    }

嵌入签名代码:

byte[] originalDocumentByte = docBlob.getBytes(1L, (int) docBlob.length());
            File file = new File(getTempFolderPath(), getTempFileName("signed"));
            try (FileOutputStream fos = new FileOutputStream(file);) {
                fos.write(originalDocumentByte);
            }
            try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
                raf.seek(documentSignatureStructure.getOffset());
                raf.write(Hex.getBytes(Base64.decode(encodedSignature)));
            }
            Blob signedAndLtvBlob;
            try (PDDocument doc = PDDocument.load(file);
                 FileOutputStream fos = new FileOutputStream(file);
                 FileInputStream fis = new FileInputStream(file)) {
                if (createDss) {
                    log.info("Adding revocation information to DSS dictionary of PDF");
                    makeLtv(doc, revocationData);
                }
                doc.saveIncremental(fos);
            }

它不适用于上述代码。
在Google上搜索,发现COSObject 'NeedToBeUpdated'标志需要设置为true的解决方案很少。在上述代码中添加新签名字段之前,添加了以下代码块。

//..
if (sigProperties.isVisibleSignature()) {
                PDRectangle rect = createSignatureRectangle(doc, sigProperties);
                signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, rect, sigProperties));
            }

            PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
            COSDictionary catalogDictionary = doc.getDocumentCatalog().getCOSObject();
            catalogDictionary.setNeedToBeUpdated(true);
            COSDictionary acroFormDictionary = (COSDictionary) catalogDictionary.getDictionaryObject(COSName.ACRO_FORM);
            acroFormDictionary.setNeedToBeUpdated(true);
            COSArray array = (COSArray) acroFormDictionary.getDictionaryObject(COSName.FIELDS);
            array.setNeedToBeUpdated(true);
            for (PDField field : acroForm.getFieldTree()) {
                if (field instanceof PDSignatureField) {
                    COSDictionary fieldDictionary = field.getCOSObject();
                    COSDictionary dictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.AP);
                    dictionary.setNeedToBeUpdated(true);
                    COSStream stream = (COSStream) dictionary.getDictionaryObject(COSName.N);
                    stream.setNeedToBeUpdated(true);
                    while (fieldDictionary != null)
                    {
                        fieldDictionary.setNeedToBeUpdated(true);
                        fieldDictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.PARENT);
                    }
                }
            }
            
            PDSignature signature = new PDSignature();
            signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
//..

即使这样也不起作用。
生成的PDF显示签名无效:

用于签名的PDF,签名域为:

我在这里漏掉了什么?
PDF文件:https://drive.google.com/file/d/1-vu9_WIfFo198v6AxoBMxCuyX1rE2FOS/view?usp=share_link
签名的PDF(无效):https://drive.google.com/file/d/1DD0aKVkonH9a_CfGrj9mACe6DBt4Ijsj/view?usp=share_link

66bbxpm5

66bbxpm51#

您的文件确实被损坏了,而且显然不是您显示的代码。
将您的签名文件“Formulier DSS-01 - DC+QV Onboarding Checklist-signed.pdf”与您的原始文件“Formulier DSS-01 - DC+QV Onboarding Checklist.pdf”进行比较,您会发现前者并不像预期的那样只是后者的增量更新,而是该文件部分的某些区域已被零覆盖。
所涉及的区域范围为0x 2c 000 - 0x 2cfff和0x 40000 - 0x 40 fff。
通过将原始文件的这些区域中的内容复制到签名文件中来修复此问题后,Adobe Acrobat将对签名进行有效验证。
因此,您应该尝试找出是什么使文件中的这两个4KB区域归零。

相关问题