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

时间:2014-09-21 09:01:09

标签: pdf digital-signature bouncycastle pdfbox verification

我是使用数字签名的新手。在其中一个项目中,我们使用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]

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

2 个答案:

答案 0 :(得分:6)

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

可互操作的签名类型

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

    {li>

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

    ISO 32000-1部分12.8.3.3 PKCS#7签名中定义的
  • adbe.pkcs7.sha1 ;签名值内容包含 DER编码的PKCS#7二进制数据对象;该数据对象是一个大的容器对象,它也可以包含元信息,例如,它可能包含用于构建证书链的证书,用于证书撤销检查的吊销信息,用于修复签名时间的数字时间戳,... 文档字节范围的SHA1摘要应封装在PKCS#7 SignedData字段中DataInfo的ContentInfo。该SignedData的摘要应作为正常的PKCS#7摘要合并。

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

  • ETSI.CAdES.detached ETSI TS 102 778-3中定义,并将集成到ISO 32000-2中;签名值内容包含CMS中指定的 DER编码的SignedData对象; CMS签名容器是PKCS#7签名容器的近亲,见上文。这基本上是adbe.pkcs7.detached的不同轮廓和更严格的定义变体。

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

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

答案 1 :(得分:1)

使用Apache PDFBox 1.8.x验证adbe.pkcs7.detached PDF签名(最常见的PDF签名)的工作示例:

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的官方示例,但我没有找到任何内容。