我正在使用itext库验证数字签名的pdf,以下是https://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-5站点上提供的示例。我在验证数字签名文档时遇到以下错误。任何人都可以帮助解决这个问题。
Exception in thread "main" ExceptionConverter: java.security.NoSuchAlgorithmException: SHA256with1.2.840.10045.4.3.2 Signature not available
at java.security.Signature.getInstance(Signature.java:229)
at com.itextpdf.text.pdf.security.PdfPKCS7.initSignature(PdfPKCS7.java:697)
at com.itextpdf.text.pdf.security.PdfPKCS7.<init>(PdfPKCS7.java:459)
at com.itextpdf.text.pdf.AcroFields.verifySignature(AcroFields.java:2420)
at com.itextpdf.text.pdf.AcroFields.verifySignature(AcroFields.java:2373)
at nic.test.C5_01_SignatureIntegrity.verifySignature(C5_01_SignatureIntegrity.java:24)
at test.ExtractSignInfor.inspectSignature(ExtractSignInfor.java:95)
at test.ExtractSignInfor.inspectSignatures(ExtractSignInfor.java:135)
at test.ExtractSignInfor.main(ExtractSignInfor.java:63)
答案 0 :(得分:5)
如果您搜索iText窒息的OID,您会看到这是“ecdsa-with-sha256”:
http://www.oid-info.com/get/1.2.840.10045.4.3.2
这在iText 5中不受支持。但我们确实在iText 7中添加了对此的支持。尝试使用最新的iText 7版本运行此代码示例:
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
PdfReader reader = new PdfReader(INPUT);
PdfDocument pdfDocument = new PdfDocument(reader);
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDocument, false);
for ( String name : acroForm.getFormFields().keySet() ) {
PdfFormField formField = acroForm.getField(name);
if (formField != null && formField instanceof PdfSignatureFormField) {
SignatureUtil signatureUtil = new SignatureUtil(pdfDocument);
PdfPKCS7 pkcs7 = signatureUtil.verifySignature(name);
pkcs7.verify();
[...]
答案 1 :(得分:4)
正如MichaëlDemey在答案中已经解释的那样,iText 5.x目前不支持有问题的算法OID。
幸运的是,iText 5.x在签名验证过程中主要依赖底层的BouncyCastle库来提供安全算法支持,而且本身只做很少的事情,所有缺失的是iText正确找到“加密算法”的名称ECDSA (在引号中,因为它不是真正的加密,因为没有并行解密)OID。
您可以在iText 5.x中添加该OID的验证支持(在当前的SNAPSHOT for 5.5.13中测试),如下所示:
java.lang.reflect.Field algorithmNamesField = com.itextpdf.text.pdf.security.EncryptionAlgorithms.class.getDeclaredField("algorithmNames");
algorithmNamesField.setAccessible(true);
@SuppressWarnings("unchecked")
HashMap<String, String> algorithmNames = (HashMap<String, String>) algorithmNamesField.get(null);
algorithmNames.put("1.2.840.10045.4.3.2", "ECDSA");
执行此添加后,iText验证
PdfReader reader = new PdfReader(resource);
AcroFields acroFields = reader.getAcroFields();
List<String> names = acroFields.getSignatureNames();
for (String name : names) {
System.out.println("Signature name: " + name);
System.out.println("Signature covers whole document: " + acroFields.signatureCoversWholeDocument(name));
System.out.println("Document revision: " + acroFields.getRevision(name) + " of " + acroFields.getTotalRevisions());
PdfPKCS7 pk = acroFields.verifySignature(name);
System.out.println("Subject: " + CertificateInfo.getSubjectFields(pk.getSigningCertificate()));
System.out.println("Document verifies: " + pk.verify());
}
显示
Signature name: Signature1 Signature covers whole document: true Document revision: 1 of 1 Subject: {ST=[Rajasthan], C=[India], CN=[Devi Singh Pilwal], O=[Personal]} Document verifies: true
(VerifySignature.java方法testVerifyTestDsp
中的测试代码)
上面的代码使用了反射,因为有问题的Map
不公开。根据您的环境,可能不允许使用此类解决方案。在这种情况下,您必须相应地修补iText。此外,由于访问的Map
不公开,因此解决方案可能在将来的版本中失败。 (这不太可能,因为iText 5处于维护模式,但人们永远不知道。)
这只会增加对此OID 的支持。很可能同样可以添加对许多其他OID的支持,但不一定适用于所有OID。
此外,我仅在验证用例中对此进行了测试。 ECDSA签名可能需要完全不同的更改。