c# - Pades LTV verification in iTextSharp throws Public key presented not for certificate signature for root CA certificate -
i'm getting org.bouncycastle.security.invalidkeyexception
error message public key presented not certificate signature when validating pdf ltvverifier
.
this problem has arisen after circumventing issue crl ldap uris. code used perform verification same previous post:
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 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; }
auxiliary function builds list of x509certificates certificate chain:
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(); }
a custom verifier, copied sample:
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 + ": verifications done"); return new list<verificationok>(); } }
i have re-implemented ltvverifier
, crlverifier
explained in previous question. crl validation done ok.
the certificate chain includes certificate used sign pdf , ca root certificate. function crlverifier.verify
throwing mentioned exception when calling next line:
if (verifier != null) result.addrange(verifier.verify(signcert, issuercert, signdate)); // verify using previous verifier in chain (if any) return result;
and relevant stack trace of org.bouncycastle.security.invalidkeyexception
:
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
after debugging turns out that
itext(sharp) 5.5.10 ltvverifier
fails in observed manner when verifying certificates certificate chains not ending in self-signed certificate.
the cause
the reason pretty simple: ltvverifier
establishes sequence of verifier
instances (ocspverifier
, crlverifier
, rootstoreverifier
, certificateverifier
; last 1 chained via base class calls). requests certificate chain of signing certificate of signature in question , each certificate of chain calls verifier
sequence certificate couple consisting of certificate , issuer; in case of final certificate in chain, null
forwarded issuer certificate.
unfortunately final verifier
, certificateverifier
, assumes in case of null
issuer certificate certificate verify self-signed:
// check if signature valid if (issuercert != null) { signcert.verify(issuercert.getpublickey()); } // in case, certificate self-signed else { signcert.verify(signcert.getpublickey()); }
(from certificateverifier
method verify
)
if certificate chain ltvverifier
requested not end in self-signed certificate, final test correctly results in observed
org.bouncycastle.security.invalidkeyexception
error message public key presented not certificate signature
the op's example
in case @ hand have document timestamp issued by
cn=autoridad de sellado de tiempo fnmt-rcm, ou=ceres, o=fnmt-rcm, c=es
issued by
cn=ac administración pública, serialnumber=q2826004j, ou=ceres, o=fnmt-rcm, c=es
issued by
ou=ac raiz fnmt-rcm, o=fnmt-rcm, c=es
which self-signed.
in case intermediary certificate, ac administración pública, on european trusted list (cf. the tl manager spain, "trust service provider", "fábrica nacional de moneda y timbre - real casa de la moneda (fnmt-rcm)", "trust service", "certificados reconocidos para su uso en el ámbito de... ", "digital identity").
thus, 1 not need more first 2 certificates establish trust, self signed root certificate not needed. consequence these first 2 certificates embedded in time stamp , returned certificate chain ltvverifier
, not self signed root.
the result observed error in ltvverifier
.
what do?
well, started creating our own copies of involved classes in previous question, changing them bit more should option.
in case 1 should additionally change rootstoreverifier
. verify
method looks this:
override public list<verificationok> verify(x509certificate signcert, x509certificate issuercert, datetime signdate) { logger.info("root store verification: " + signcert.subjectdn); // verify using certificateverifier if root store missing if (certificates == null) return base.verify(signcert, issuercert, signdate); try { list<verificationok> result = new list<verificationok>(); // loop on trusted anchors in 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); } }
we merely have remove marked line
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;
in inner try
block. here have established certificate signcert
signed trust anchor, there no need base.verify
anyways.
Comments
Post a Comment