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

Ran*_*jan 5 pdf verification bouncycastle digital-signature pdfbox

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

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

byte[] signatureAsBytes = pdsignature.getContents(new FileInputStream(this.INPUT_FILE));
byte[] signedContentAsBytes = pdsignature.getSignedContent(new FileInputStream(this.INPUT_FILE));
Run Code Online (Sandbox Code Playgroud)

//数字签名验证

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);
Run Code Online (Sandbox Code Playgroud)

以下是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]
Run Code Online (Sandbox Code Playgroud)

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

mkl*_*mkl 8

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

可互操作的签名类型

正如标题所示,以下列表包含"可互操作的签名类型",这些类型或多或少都是严格定义的.在PDF规范指定的方式还包括完全自定义的签名方案.但让我们假设我们处于可互操作的状态.签名类型的集合归结为:

  • adbe.x509.rsa_sha1ISO 32000-1第12.8.3.2节PKCS#1签名中定义 ; 签名值Contents包含DER编码的PKCS#1二进制数据对象 ; 如果RSA是包含填充文档散列和散列算法的加密结构,则此数据对象是相当裸的签名.

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

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

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

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

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


Jai*_*zel 5

adbe.pkcs7.detached使用 Apache PDFBox 1.8.16 和 Bouncy Castle 1.44验证PDF 签名(最常见的 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.");
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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