简体   繁体   English

通过使用 PDFBox 重新处理现有未签名的签名字段来添加新签名

[英]Adding new signature by reatining existing unsigned signature fields using PDFBox

I want to sign PDF which has signature fields in it already.我想签署 PDF,其中已经有签名字段。 I need to add new signature field retaining existing unsigned signature fields.我需要添加新的签名字段以保留现有的未签名签名字段。 After signing such PDFs, I see that new signature field added by code is always invalid.签署此类 PDF 后,我看到代码添加的新签名字段始终无效。 Says 'document has been altered'.说“文件已被更改”。

Below code being used to compute hash of the document:下面的代码用于计算文档的 hash:

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

Embedding signature code:嵌入签名代码:

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

It did not work with above code.它不适用于上面的代码。

Googled up and saw few solutions where COSObject 'NeedToBeUpdated' flag needs to be set to true.在谷歌上搜索,发现很少有 COSObject 'NeedToBeUpdated' 标志需要设置为 true 的解决方案。 Added below code block before adding new signature fields in above code.在上面的代码中添加新的签名字段之前添加了下面的代码块。

//..
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);
//..

Even this did not work.即使这样也行不通。

Resulting PDF shows signature is invalid:结果 PDF 显示签名无效: 在此处输入图像描述

PDF used for signing with signature fields: PDF 用于签名字段: 在此处输入图像描述

Whats the piece I am missing here?我在这里缺少的是什么?

PDF file: https://drive.google.com/file/d/1-vu9_WIfFo198v6AxoBMxCuyX1rE2FOS/view?usp=share_link PDF 文件: https://drive.google.com/file/d/1-vu9_WIfFo198v6AxoBMxCuyX1rE2FOS/view?usp=share_link

Signed PDF (invalid): https://drive.google.com/file/d/1DD0aKVkonH9a_CfGrj9mACe6DBt4Ijsj/view?usp=share_link签名PDF(无效): https://drive.google.com/file/d/1DD0aKVkonH9a_CfGrj9mACe6DBt4Ijsj/view?usp=share_link

Something indeed damaged your file, and it's apparently not the code you show.某些东西确实损坏了您的文件,而且显然不是您显示的代码。

Comparing your signed file "Formulier DSS-01 - DC+QV Onboarding Checklist-signed.pdf" with your original "Formulier DSS-01 - DC+QV Onboarding Checklist.pdf", one sees that the former is not simply the latter with incremental updates as one would expect but that some regions in that file part have been overwritten with zeros.将您的签名文件“Formulier DSS-01 - DC+QV Onboarding Checklist-signed.pdf”与您的原始文件“Formulier DSS-01 - DC+QV Onboarding Checklist.pdf”进行比较,可以看出前者不仅仅是后者具有增量正如人们所期望的那样更新,但该文件部分中的某些区域已被零覆盖。

The regions in question are from 0x2c000-0x2cfff and 0x40000-0x40fff.有问题的区域来自 0x2c000-0x2cfff 和 0x40000-0x40fff。

After fixing this issue by copying the contents from those regions of the original file into the signed file, Adobe Acrobat validates the signature positively.通过将原始文件的这些区域的内容复制到签名文件中解决此问题后,Adobe Acrobat 会积极验证签名。

So you should try and find out what zeroed those two 4KB regions in your file.因此,您应该尝试找出是什么将文件中的这两个 4KB 区域归零。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM