Java 带有PDFBox 2.0.12的外部签名

Java 带有PDFBox 2.0.12的外部签名,java,pdfbox,Java,Pdfbox,签署修改的问题 Iam使用DSC令牌传递文档哈希和签名哈希(外部签名) 我收到错误,如:“文档自签名后已被更改或损坏” 获取文档的哈希:- public String genrateDigitalCertificateSign() { try { src = new FileInputStream(inputFilePath); OutputStream dest = new FileOutputStream(new File(RE

签署修改的问题

Iam使用DSC令牌传递文档哈希和签名哈希(外部签名)

我收到错误,如:“文档自签名后已被更改或损坏” 获取文档的哈希:-

public String genrateDigitalCertificateSign() {
        try {
            src = new FileInputStream(inputFilePath);
            OutputStream dest = new FileOutputStream(new File(RESULT_FOLDER, "Test.pdf"));
            pdDocument = PDDocument.load(src);

            PDSignature pds = null;
            String hashdocument = null;
            File imgFile = new File(inputImgPath);
            PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
            if (acroForm == null) {
                pdDocument.getDocumentCatalog().setAcroForm(acroForm = new PDAcroForm(pdDocument));
            }
            acroForm.setSignaturesExist(true);
            acroForm.setAppendOnly(true);
            acroForm.getCOSObject().setDirect(true);

            pds = new PDSignature();
            pds.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            pds.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
            pds.setSignDate(Calendar.getInstance());

            PDPage pdPage = pdDocument.getPage(0);
            PDImageXObject pdImage = PDImageXObject.createFromFileByContent(imgFile, pdDocument);

            //visible signature rectangle
            rectangle = new PDRectangle(200.00, 200.00,150.00,50.00);
            
            List<PDField> acroFormFields = acroForm.getFields();
            PDSignatureField signatureField = new PDSignatureField(acroForm);
            acroForm.setSignaturesExist(true);
            acroForm.setAppendOnly(true);
            acroForm.getCOSObject().setDirect(true);
            signatureField.setValue(pds);
            acroFormFields.add(signatureField);
            pdDocument.addSignature(pds);
            
            //creating visible stamp
            createVisualSignatureTemplate(pdDocument, signatureField, pdPage, rectangle, pdImage, signDisplayInfo);

            externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
            InputStream dataToSign = externalSigning.getContent();
            hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
            return hashdocument;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
之后,签名被追加,但在打开签名的PDF时,会出现如下图所示的错误

PDF文件链接:

[已编辑]

public byte[] genrateDigitalCertificateSign() {
        try {
            src = new FileInputStream(inputFilePath);
            OutputStream dest = new FileOutputStream(new File(RESULT_FOLDER, "Test.pdf"));
            pdDocument = PDDocument.load(src);

            PDSignature pds = null;
            String hashdocument = null;
            File imgFile = new File(inputImgPath);
            PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
            if (acroForm == null) {
                pdDocument.getDocumentCatalog().setAcroForm(acroForm = new PDAcroForm(pdDocument));
            }
            acroForm.setSignaturesExist(true);
            acroForm.setAppendOnly(true);
            acroForm.getCOSObject().setDirect(true);

            pds = new PDSignature();
            pds.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            pds.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
            pds.setSignDate(Calendar.getInstance());

            PDPage pdPage = pdDocument.getPage(0);
            PDImageXObject pdImage = PDImageXObject.createFromFileByContent(imgFile, pdDocument);

            //visible signature rectangle
            rectangle = new PDRectangle(200.00, 200.00,150.00,50.00);
            
            List<PDField> acroFormFields = acroForm.getFields();
            PDSignatureField signatureField = new PDSignatureField(acroForm);
            acroForm.setSignaturesExist(true);
            acroForm.setAppendOnly(true);
            acroForm.getCOSObject().setDirect(true);
            signatureField.setValue(pds);
            acroFormFields.add(signatureField);
            pdDocument.addSignature(pds);
            
            //creating visible stamp
            createVisualSignatureTemplate(pdDocument, signatureField, pdPage, rectangle, pdImage, signDisplayInfo);

            externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
            InputStream dataToSign = externalSigning.getContent();
            hashdocument = DigestUtils.sha256(dataToSign); // hash is generated
            return hashdocument;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

好的,代码中有两个问题:

双重散列 您的代码创建了一个签名,该签名包含文档数据的散列,其中只需要文档数据的散列

您的代码主要是从PDFBox示例中借用的。如果是外部签名,则检索要签名的内容并将其转发到
sign
方法:

byte[] cmsSignature = sign(externalSigning.getContent());
(来自扩展了
CreateSignatureBase
CreateSignatureBase

但是,在代码中,您首先散列要签名的内容,然后将该散列转发给sign方法:

public String genrateDigitalCertificateSign() {
    ...
    InputStream dataToSign = externalSigning.getContent();
    hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
    return hashdocument;
    ...
}

byte[] signedHash = sign(hash);
就创建CMS签名容器而言,您的
sign
方法使用与PDFBox示例相同的代码

因此,您的代码对文档数据进行哈希运算的次数过多。要解决这个问题,你必须

  • 要么放弃散列步骤,将文档数据转发给签名
  • 或者更改
    符号
    方法,使其输入不再散列
我假设您的目的是计算散列并将其转发到一个单独的签名服务,因此您可以选择后者

对散列进行十六进制编码 哈希生成方法
genrateDigitalCertificateSign
返回的哈希值不是实际的
字节[]
,而是十六进制编码并返回十六进制字符串:

hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;
但是,您的其他方法希望获取并操作实际的
字节[]

要解决这个问题,你必须

  • 不是以十六进制编码开始,即使用
    DigestUtils.sha256
    而不是
    DigestUtils.sha256Hex
    ,并从
    genrateDigitalCertificateSign
    返回
    字节[]
    而不是
    字符串
  • 或者在将哈希值馈送到
    signedPDF
    方法中的
    sign
    方法之前,先对哈希值进行十六进制解码

我假设您的目的是以字符串形式传输散列,因此您可以选择后者。

请共享一个示例PDF进行分析。此外,您可能需要提供更多的代码,您提供的位看起来非常不完整。@mkl文件和初始哈希生成代码也已上载,请检查。感谢您的建议,我尝试了两种方法,但仍然面临相同的问题。在您的示例PDF中,这两种错误是问题所在。请共享一个新的PDF,该PDF带有您已更正这两个错误的代码签名。考虑在你的问题文本的编辑中分享你的代码变化。根据你的第二个附加文件和你添加的代码,你修复了第二个问题,但不是第一个问题:在签名中,文档哈希的十六进制编码不是哈希,而是文档哈希的散列。这一定是文档散列本身。我没有得到你提到的第一个问题,请简要说明。根据你的建议,我修改了代码并上传了相同的内容。
byte[] cmsSignature = sign(externalSigning.getContent());
public String genrateDigitalCertificateSign() {
    ...
    InputStream dataToSign = externalSigning.getContent();
    hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
    return hashdocument;
    ...
}

byte[] signedHash = sign(hash);
hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;