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 

and link pdf try validate

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

Popular posts from this blog

python - How to insert QWidgets in the middle of a Layout? -

python - serve multiple gunicorn django instances under nginx ubuntu -

module - Prestashop displayPaymentReturn hook url -