2014-09-21 474 views
2

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

//获取数字签名和签名内容从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] 

随着a博韦代码我总是得到答案为“假”。我不知道如何解决这个问题。请帮忙

+0

据我所知,需要一份“未签名”版本的pdf文件作为比较。我如何从签名副本中提取PDF文件的原始副本?任何帮助将不胜感激 – Ranjan 2014-09-21 18:48:07

+0

*我如何从签名副本中提取PDF文件的原始副本?* - 这就是'pdsignature.getSignedContent'的用途。 – mkl 2014-09-21 20:01:20

+0

pdsignature.getSignedContent返回原始内容 - 这也是我的理解。但是,验证永远不会返回true。正如我前面所说,我对数字签名不熟悉,因此无法解决问题。 – Ranjan 2014-09-22 08:09:13

回答

4

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

可互操作的签名类型

正如标题已经说明了,下面的列表中包含“可互操作的签名类型”,这或多或少严格的定义。 PDF specification指定了一种包含完全自定义签名方案的方法。但让我们假设我们处于可互操作的状态。签名类型的收集焚烧到:

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

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

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

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

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

我会建议研究我指定的规范和从那里引用的文档,大多是RFC。根据这些知识,您可以轻松找到合适的BouncyCastle类来分析不同的签名内容

+0

您应该将此答案发布到其他问题。这对于对PDF数字签名内部感兴趣的人非常有用。 – 2017-05-26 10:27:32

+0

谢谢。我将它作为[栈溢出文档示例](https://stackoverflow.com/documentation/pdf/5161/integrated-pdf-signatures/30860/interoperable-signature-types#t=201705262024169202313)供以后参考。 – mkl 2017-05-26 20:24:32

1

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

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