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

无法使用Apache PDFBOX验证数字签名

梁丘琛
2023-03-14

我是使用数字签名的新手。在其中一个项目中,我们使用Apache PdfBox来处理数字签名的pdf文件。虽然我们可以测试所有功能,但签名pdf文件的验证是我们无法破解的。我们使用BouncyCastle作为提供程序。下面是代码:

从pdf文件获取数字签名和签名内容:

byte[] signatureAsBytes = pdsignature.getContents(new FileInputStream(this.INPUT_FILE));
byte[] signedContentAsBytes = pdsignature.getSignedContent(new FileInputStream(this.INPUT_FILE));

数字签名验证:

Security.addProvider(new BouncyCastleProvider());
Signature signer = Signature.getInstance("RSA","BC");

//Get PublicKey from p7b file
X509Certificate cert509=null;
File file = new File("C:\\certificate_file.p7b");
FileInputStream fis = new FileInputStream(file);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Collection c = cf.generateCertificates(fis);
Iterator it = c.iterator();
PublicKey pubkey;

while (it.hasNext()) 
{
   cert509 = (X509Certificate) it.next();
   pubkey = cert509.getPublicKey();
}

boolean VERIFIED=false;
Security.addProvider(new BouncyCastleProvider());
Signature signer = Signature.getInstance("RSA","BC");
PublicKey key=this.getPublicKey(false);
signer.initVerify(key);

List<PDSignature> allsigs = this.PDFDOC.getSignatureDictionaries();
Iterator<PDSignature> i = allsigs.iterator();
    
while(i.hasNext())
{
        PDSignature sig = (PDSignature) i.next();
        byte[] signatureAsBytes = sig.getContents(new FileInputStream(this.INPUT_FILE));
        byte[] signedContentAsBytes = sig.getSignedContent(new FileInputStream(this.INPUT_FILE));
        signer.update(signedContentAsBytes);
        VERIFIED=signer.verify(signatureAsBytes);
}
    
System.out.println("Verified="+VERIFIED);

以下是p7b格式证书的相关摘录-我使用BouncyCastle作为安全提供程序:

  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
  Key:  Sun RSA public key, 2048 bits
  Validity: [From: Tue Aug 06 12:26:47 IST 2013,
  To: Wed Aug 05 12:26:47 IST 2015]
  Algorithm: [SHA256withRSA]

使用上面的代码,我总是得到“false”的响应。我不知道如何解决这个问题。请帮忙

共有2个答案

郑和泰
2023-03-14

使用Apache PDFBox 1.8.16和Bouncy Castle 1.44验证adbe.pkcs7.detachedPDF签名(最常见的PDF签名)的工作示例:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;

import java.io.File;
import java.io.FileInputStream;
import java.security.cert.CertStore;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class PDFBoxValidateSignature {
    public static void main(String[] args) throws Exception {
        File signedFile = new File("sample-signed.pdf");
        // We load the signed document.
        PDDocument document = PDDocument.load(signedFile);
        List<PDSignature> signatureDictionaries = document.getSignatureDictionaries();
        // Then we validate signatures one at the time.
        for (PDSignature signatureDictionary : signatureDictionaries) {
            // NOTE that this code currently supports only "adbe.pkcs7.detached", the most common signature /SubFilter anyway.
            byte[] signatureContent = signatureDictionary.getContents(new FileInputStream(signedFile));
            byte[] signedContent = signatureDictionary.getSignedContent(new FileInputStream(signedFile));
            // Now we construct a PKCS #7 or CMS.
            CMSProcessable cmsProcessableInputStream = new CMSProcessableByteArray(signedContent);
            CMSSignedData cmsSignedData = new CMSSignedData(cmsProcessableInputStream, signatureContent);
            SignerInformationStore signerInformationStore = cmsSignedData.getSignerInfos();
            Collection signers = signerInformationStore.getSigners();
            CertStore certs = cmsSignedData.getCertificatesAndCRLs("Collection", (String) null);
            Iterator signersIterator = signers.iterator();
            while (signersIterator.hasNext()) {
                SignerInformation signerInformation = (SignerInformation) signersIterator.next();
                Collection certificates = certs.getCertificates(signerInformation.getSID());
                Iterator certIt = certificates.iterator();
                X509Certificate signerCertificate = (X509Certificate) certIt.next();
                // And here we validate the document signature.
                if (signerInformation.verify(signerCertificate.getPublicKey(), (String) null)) {
                    System.out.println("PDF signature verification is correct.");
                    // IMPORTANT: Note that you should usually validate the signing certificate in this phase, e.g. trust, validity, revocation, etc. See http://www.nakov.com/blog/2009/12/01/x509-certificate-validation-in-java-build-and-verify-chain-and-verify-clr-with-bouncy-castle/.
                } else {
                    System.out.println("PDF signature verification failed.");
                }
            }
        }
    }
}

不确定是否有官方示例,我已经检查了PDFBox 1.8.4的官方示例,但没有找到任何东西。

公西嘉玉
2023-03-14

您的主要问题是,存在多种类型的PDF签名,这些签名在签名容器的格式以及签名字节的实际内容上有所不同。另一方面,BC代码只能验证上述签名容器中包含的裸签名字节序列。

正如标题所述,下面的列表包含或多或少严格定义的“可互操作的签名类型”。PDF规范还指定了一种包含完全自定义签名方案的方法。但让我们假设我们处于一种可互操作的状态。签名类型的集合可分解为:

>

  • adbe.x509.rsa_sha1在ISO 32000-1部分中定义12.8.3.2PKCS#1签名;签名值Contents包含一个DER编码的PKCS#1二进制数据对象;该数据对象是一个相当裸的签名,在RSA的情况下是一个包含填充文档散列和散列算法的加密结构。

    阿德贝。pkcs7.sha1定义见ISO 32000-1第12.8.3.3节PKCS#7签名;签名值内容包含DER编码的PKCS#7二进制数据对象;此数据对象是一个大容器对象,它还可以包含元信息,例如,它可能包含用于构建证书链的证书、用于证书吊销检查的吊销信息、用于固定签名时间的数字时间戳等。。。文件字节范围的SHA1摘要应使用数据类型的ContentInfo封装在PKCS#7 SignedData字段中。该签名数据的摘要应合并为普通PKCS#7摘要。

    阿德贝。pkcs7.ISO 32000-1第12.8.3.3节PKCS#7签名中定义的分离;签名值内容包含DER编码的PKCS#7二进制数据对象,请参见上文。文件字节范围内的原始签名消息摘要应合并为普通PKCS#7 SignedData字段。PKCS#7 SignedData字段中不得封装任何数据。

    ETSI. CADES. detated在ETSI TS 102 778-3中定义,并将集成在ISO 32000-2中;签名值Contents包含CMS中指定的DER编码的SignedData对象;CMS签名容器与PKCS#7签名容器密切相关,见上文。这本质上是adbe.pkcs7.detached.的不同配置和更严格定义的变体

    ETSI。RFC3161在ETSI TS 102 778-4中定义,并将集成在ISO 32000-2中;签名值内容包含RFC 3161中指定的时间戳标记;时间戳令牌同样与PKCS#7签名容器密切相关,请参见上文,但它们包含一个特殊的数据子结构,其中包含文档哈希、创建戳的时间以及发布时间服务器上的信息。

    我建议研究我命名的规范和引用的文件,主要是RFC。基于这些知识,您可以轻松找到适当的BouncyCastle类来分析不同的签名内容。

    PS(2021):与此同时,ISO 32000-2已经发布,并且确实包含ETSI规范。凯德斯。分离和ETSI。RFC3161。此外,用于PADE的ETSI技术规范TS 102 778-*已被实际规范ETSI EN 319 142-*取代。

  •  类似资料:
    • 我在iText web上遵循了这个示例(http://gitlab.itextsupport.com/itextsharp/tutorial/blob/master/signatures/chapter5/C5_03_CertificateValidation/C5_03_CertificateValidation.cs),但结果与我预期的不一样。具体地说,当试图通过OCSP或CRL验证签名时,结

    • 为什么我的代码返回奇怪的结果? 我的代码给出了以下结果,它看起来不像是正确的计算结果:

    • 我有字节数组格式的公钥。在我的数据库里。像这样 在此处输入图像描述 总是suc=false。我确信bytetoverify的值与方法符号中的输入值相同。 我不知道我用这种方式生成公钥是问题还是符号有问题。在符号方法中,我使用sh1和pkcs1,但在验证中,我只找到sh1。 每个人都能帮我吗?

    • 在我的应用程序中,我有一个公钥(表示为字符串),普通消息和数字签名,表示为base64编码字符串,用SHA256散列,用RSA加密)。现在,我需要验证数字签名。我试图做如下操作: 从创建(取自此处) 根据原始消息创建SHA256摘要 使用函数验证签名 (我正在努力避免使用OpenSSL函数) 此外,我的数字签名是使用Java的SHA256withRSA方法创建的。我在这里读到SHA256WithR

    • 根据从Linux命令行创建DSA签名时的说明,我创建了一条DSA签名消息: directions实际上使用了foo.sha1而不是foo.txt,其中foo.sha1是由生成的,但签名哈希似乎有点多余,因为DSA本身应该进行哈希运算。 所以,不管怎样,我做到了。以下是我使用的私钥(我专门为测试目的生成它): 下面是sigfile.bin的十六进制编码输出: 我现在正试图用BouncyCastle在

    • 问题内容: 我正在使用node.js request.js到达api。我收到这个错误 [错误:UNABLE_TO_VERIFY_LEAF_SIGNATURE] 我所有的凭据都是准确有效的,并且服务器还不错。我对邮递员提出了同样的要求。 此代码仅在可执行脚本ex中运行。,那是为什么?是否需要在服务器上运行? 问题答案: 注意 :以下情况很危险,并且将允许在客户端和服务器之间截取和修改API内容。 这