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

使用PDFBox 2.0.12的外部签名

鲁靖
2023-03-14

签署修改的问题。

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

Iam收到错误,例如:“文档自签名以来已被更改或损坏”获取文档哈希:-

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

外部签名代码:-

public byte[] sign(byte[] hash)
            throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        PrivateKey privKey = pk;
        try {
            List<Certificate> certList = new ArrayList<>();
            certList.addAll(Arrays.asList(chain));
            Store certs = new JcaCertStore(certList);
            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
            org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate
                    .getInstance(chain[0].getEncoded());
            ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privKey);
            gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
                            .build(sha1Signer, new X509CertificateHolder(cert)));
            gen.addCertificates(certs);
            CMSProcessableInputStream msg = new CMSProcessableInputStream(new ByteArrayInputStream(hash));
            CMSSignedData signedData = gen.generate(msg, false);
            return signedData.getEncoded();
        } catch (GeneralSecurityException e) {
            throw new IOException(e);
        } catch (CMSException e) {
            throw new IOException(e);
        } catch (OperatorCreationException e) {
            throw new IOException(e);
        }
    }

签名附加代码:-

public void signedPDF(byte[] hash)
            throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException {
        byte[] signedHash = sign(hash);
        externalSigning.setSignature(signedHash);
        IOUtils.closeQuietly(src);
        pdDocument.close();
    } 

此签名附加后,但在打开签名的PDF时会出现如下图所示的错误。

PDF文件链接:https://drive.google.com/file/d/1qRT2CVgET8Ds1fu0b5psii3j8ytPKaLH/view?usp=sharing

[编辑]

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

最近签署的PDF文件链接:-https://drive.google.com/file/d/1mZ8Wqppx3EylI1aLYB9Fl8NzibBXZxzR/view?usp=sharing

[编辑2]这是我根据您的建议尝试的流程<在这篇文章中,你指出的第一个问题是什么?我想不出来,请帮忙。

externalSigning = pdDocument.saveIncrementalForExternalSigning(dest);
            InputStream dataToSign = externalSigning.getContent();
            hashdocument = DigestUtils.sha256(dataToSign); // hash is generated 
            byte[] signedhash = sign(hashdocument);
            externalSigning.setSignature(signedhash);
            pdDocument.close();

共有1个答案

澹台欣怿
2023-03-14

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

您的代码创建了一个签名,该签名包含文档数据哈希的哈希,其中只需要文档数据的哈希。

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

byte[] cmsSignature = sign(externalSigning.getContent());

(来自扩展了CreateSignatureBase的CreateSignature)

但是,在代码中,首先对要签名的内容进行哈希运算,然后将该哈希转发给sign方法:

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

byte[] signedHash = sign(hash);

就创建CMS签名容器而言,您的符号方法使用与PDFBox示例相同的代码。

因此,您的代码过于频繁地对文档数据进行散列。要解决这个问题,你必须

  • 要么放弃散列步骤,将文档数据转发给sign
  • 或者更改sign方法,使其不再散列输入

我假设您的意图是计算哈希并将其转发到单独的签名服务,因此您的选择是后者。

您的哈希生成方法genrateDigitalcerateSign返回的哈希值不是实际的byte[],而是十六进制对其进行编码并返回十六进制字符串

hashdocument = DigestUtils.sha256Hex(dataToSign); // hash is generated
return hashdocument;

不过,您的其他方法希望获取并操作实际的字节[]。

要解决这个问题,你必须

  • 开始时不是十六进制编码,即使用DigestUtils。sha256而不是DigestUtils。sha256Hex,并从genrateDigitalCertificateSign返回字节[],而不是字符串

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

 类似资料:
  • 需要通过使用外部webservice对文档哈希进行签名来签署PDF,该过程必须在2个步骤中完成,并使用临时空签名。 在Priyanka问题和Grazina问题之后,阅读了那些帖子上的mkl答案,我现在有一个无效的签名,即使像Grazina那样添加了哈希前缀。 iTextSharp版本:5.5.13.1 这个节目是我上一个问题的另一个问题。当前代码(编译并开始调用SignPDF方法): 获得的结果

  • 首先,虽然我关注StackOverflow已经有相当一段时间了,但这是我第一次发布一些东西,所以如果我做错了或者不按规则做的话,请随时为我指出正确的方向。 我正在开发一个PDF数字签名应用程序,使用iText5,它依赖于一个外部服务,在我准备好PDF签名后提供一个签名哈希。 如iText文档中所述,在第一阶段,我准备了PDF(在最终实现中,所有PDF都可能是多签名的,因此我使用追加模式),如下所示

  • 我已经玩弄了一段时间的iTextSharp 5.5.7,找不到正确的方法从智能卡为PDF制作一个有效的数字签名-Adobe Reader总是说它的签名人和未知,并且不能解码签名的DER数据。 我查看了Makesignature.cs代码以供参考,以及is的功能: 然后,根据iExternalSignature.cs中的“Sign”方法 “@param message要进行散列和签名的邮件” 所以我

  • Client=My application,Server=MSSP(移动签名服务提供商) 服务器仅对哈希值进行签名。 要签名数据: *Base64编码的SHA-384待签名数据摘要。(64个字符) *Base64编码的SHA-512待签名数据摘要。(88个字符) *要签名的Base64编码的DER编码的PKCS#1摘要信息。 注意:我使用MSSP(移动签名服务提供商)架构。对于SHA256算法,D

  • 第一个选项是不可行的,因为PDF的签名没有时间戳(这是一个非常重要的必要条件),所以选择了第二个选项。 这是我们的代码,首先,我们得到签名外观,并计算散列: 在这一点上,我们有了文档的哈希代码。然后我们将散列发送到webservice,我们得到签名的散列代码。 按照mkl的建议,我遵循了这本书的4.3.3部分PDF文档的数字签名,现在我的代码如下: 第一部分,当我们计算散列时: 在第二部分,我们得

  • 我正在实现一个应用程序,以便在服务器中对PDF文件进行签名,并使用以下场景(使历史变长,变短): 客户端开始签名发送到服务器,日期/时间和水印 服务器将签名字典添加到文件中并发送要签名的数据 客户端签名内容 服务器完成签名 我正在使用PDFBox 2.0.15,并使用新的功能,如下面的代码所示: 在“IF”语句中,我正在生成要签名的数据。在“ELSE”语句中,添加签名,这是通过发布请求(这就是所做