Pades LTV verification in iTextSharp throws Public key presented not for certificate signature for root CA certificate
我得到了一个
此问题是在绕过了crl ldap uris的问题之后出现的。用于执行验证的代码与上一个日志相同:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public static bool Validate(byte[] pdfIn, X509Certificate2 cert) { using (var reader = new PdfReader(pdfIn)) { var fields = reader.AcroFields; var signames = fields.GetSignatureNames(); if (!signames.Any(n => fields.SignatureCoversWholeDocument(n))) throw new Exception("None signature covers all document"); var verifications = signames.Select(n => fields.VerifySignature(n)); var invalidSignature = verifications.Where(v => !v.Verify()); var invalidTimeStamp = verifications.Where(v => !v.VerifyTimestampImprint()); if (invalidSignature.Any()) throw new Exception("Invalid signature found"); } using (var reader = new PdfReader(pdfIn)) { var ltvVerifier = new LtvVerifier(reader) { OnlineCheckingAllowed = false, CertificateOption = LtvVerification.CertificateOption.WHOLE_CHAIN, Certificates = GetChain(cert).ToList(), VerifyRootCertificate = false, Verifier = new MyVerifier(null) }; var ltvResult = new List<VerificationOK> { }; ltvVerifier.Verify(ltvResult); if (!ltvResult.Any()) throw new Exception("Ltv verification failed"); } return true; } |
从证书链构建X509证书列表的辅助功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private static X509.X509Certificate[] GetChain(X509Certificate2 myCert) { var x509Chain = new X509Chain(); x509Chain.Build(myCert); var chain = new List<X509.X509Certificate>(); foreach(var cert in x509Chain.ChainElements) { chain.Add( DotNetUtilities.FromX509Certificate(cert.Certificate) ); } return chain.ToArray(); } |
自定义验证器,刚从示例复制:
1 2 3 4 5 6 7 8 9 10 11 | class MyVerifier : CertificateVerifier { public MyVerifier(CertificateVerifier verifier) : base(verifier) { } override public List<VerificationOK> Verify( X509.X509Certificate signCert, X509.X509Certificate issuerCert, DateTime signDate) { Console.WriteLine(signCert.SubjectDN +": ALL VERIFICATIONS DONE"); return new List<VerificationOK>(); } } |
如前一问题所述,我已经重新实施了
证书链包括用于签署PDF和CA根证书的证书。函数
1 2 3 4 | if (verifier != null) result.AddRange(verifier.Verify(signCert, issuerCert, signDate)); // verify using the previous verifier in the chain (if any) return result; |
这是
1 2 3 4 5 6 7 8 9 | in Org.BouncyCastle.X509.X509Certificate.CheckSignature(AsymmetricKeyParameter publicKey, ISigner signature) in Org.BouncyCastle.X509.X509Certificate.Verify(AsymmetricKeyParameter key) in iTextSharp.text.pdf.security.CertificateVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) in iTextSharp.text.pdf.security.RootStoreVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) in PdfCommon.CrlVerifierSkippingLdap.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) in c:\Projects\digit\Fuentes\PdfCommon\CrlVerifierSkippingLdap.cs:line 76 in iTextSharp.text.pdf.security.OcspVerifier.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) in PdfCommon.LtvVerifierSkippingLdap.Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime sigDate) in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 221 in PdfCommon.LtvVerifierSkippingLdap.VerifySignature() in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 148 in PdfCommon.LtvVerifierSkippingLdap.Verify(List`1 result) in c:\Projects\digit\Fuentes\PdfCommon\LtvVerifierSkippingLdap.cs:line 112 |
以及一个链接,指向我试图验证的PDF
经过调试,结果是
IText(sharp)5.5.10
原因很简单:
不幸的是,最终的
1 2 3 4 5 6 7 8 | // Check if the signature is valid if (issuerCert != null) { signCert.Verify(issuerCert.GetPublicKey()); } // Also in case, the certificate is self-signed else { signCert.Verify(signCert.GetPublicKey()); } |
(来源于
如果最初请求的
Org.BouncyCastle.Security.InvalidKeyException with error message Public key presented not for certificate signature
OP的例子
在手头的情况下,我们有一个由
cn=autoridad de sellado de tiempo fnmt-rcm,ou=ceres,o=fnmt-rcm,c=es
发布
c n=ac administraci_n p_blica,序列号=q2826004j,ou=ceres,o=fnmt-rcm,c=es
发布
ou=ac raiz fnmt-rcm,o=fnmt-rcm,c=es
它是自签名的。
在这种情况下,中介机构证书ac administraci_n p_blica已经在欧洲受信任名单上(参见西班牙的TL经理,"信托服务提供商","F_brica nacional de moneda y timbre-real casa de la moneda(fnmt-rcm)","信托服务","certificados reconocidos para su uso"我要去……""数字标识")。
因此,一个不需要超过前两个证书来建立信任,不需要自签名根证书。因此,只有前两个证书嵌入到时间戳中,并作为证书链返回到
结果是观察到的
好吧,因为我们已经开始在前面的问题中创建所涉及类的自己的副本,所以多更改它们应该是一个选项。
在这种情况下,应该另外更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | override public List<VerificationOK> Verify(X509Certificate signCert, X509Certificate issuerCert, DateTime signDate) { LOGGER.Info("Root store verification:" + signCert.SubjectDN); // verify using the CertificateVerifier if root store is missing if (certificates == null) return base.Verify(signCert, issuerCert, signDate); try { List<VerificationOK> result = new List<VerificationOK>(); // loop over the trusted anchors in the root store foreach (X509Certificate anchor in certificates) { try { signCert.Verify(anchor.GetPublicKey()); LOGGER.Info("Certificate verified against root store"); result.Add(new VerificationOK(signCert, this,"Certificate verified against root store.")); result.AddRange(base.Verify(signCert, issuerCert, signDate)); return result; } catch (GeneralSecurityException) {} } result.AddRange(base.Verify(signCert, issuerCert, signDate)); return result; } catch (GeneralSecurityException) { return base.Verify(signCert, issuerCert, signDate); } } |
我们只需去掉标记线
1 2 3 4 5 6 7 | signCert.Verify(anchor.GetPublicKey()); LOGGER.Info("Certificate verified against root store"); result.Add(new VerificationOK(signCert, this,"Certificate verified against root store.")); // vvv remove result.AddRange(base.Verify(signCert, issuerCert, signDate)); // ^^^ remove return result; |
在内