我是使用数字签名的新手。在其中一个项目中,我们使用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]
使用上面的代码我总是得到“假”的回应。我不知道如何解决这个问题。请帮忙
答案 0 :(得分:6)
您的主要问题是签名容器的格式和签名字节实际上有多种类型的PDF签名不同。另一方面,您的BC代码只能验证包含在上述签名容器中的裸签名字节序列。
正如标题所示,以下列表包含或多或少严格定义的“可互操作的签名类型”。 PDF specification指定了一种包含完全自定义签名方案的方法。但让我们假设我们处于可互操作的状态。签名类型的集合归结为:
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摘要合并。
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的官方示例,但我没有找到任何内容。