dd450b6090fa19dc8cbea6ab94c3ac14c1132bc0
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / AbstractPKIXTrustEngine.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * AbstractPKIXTrustEngine.cpp
23  * 
24  * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
25  * to perform PKIX validation of signatures and certificates.
26  */
27
28 #include "internal.h"
29 #include "logging.h"
30 #include "security/AbstractPKIXTrustEngine.h"
31 #include "signature/KeyInfo.h"
32 #include "signature/Signature.h"
33 #include "security/CredentialCriteria.h"
34 #include "security/CredentialResolver.h"
35 #include "security/KeyInfoResolver.h"
36 #include "security/OpenSSLCryptoX509CRL.h"
37 #include "security/OpenSSLPathValidator.h"
38 #include "security/PKIXPathValidatorParams.h"
39 #include "security/SecurityHelper.h"
40 #include "security/X509Credential.h"
41 #include "signature/SignatureValidator.h"
42 #include "util/NDC.h"
43 #include "util/PathResolver.h"
44
45 #include <fstream>
46 #include <xercesc/util/XMLUniDefs.hpp>
47 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
48
49 using namespace xmlsignature;
50 using namespace xmltooling::logging;
51 using namespace xmltooling;
52 using namespace std;
53
54
55 namespace xmltooling {
56     // Adapter between TrustEngine and PathValidator
57     class XMLTOOL_DLLLOCAL PKIXParams : public PKIXPathValidatorParams
58     {
59         const AbstractPKIXTrustEngine& m_trust;
60         const AbstractPKIXTrustEngine::PKIXValidationInfoIterator& m_pkixInfo;
61         vector<XSECCryptoX509CRL*> m_crls;
62     public:
63         PKIXParams(
64             const AbstractPKIXTrustEngine& t,
65             const AbstractPKIXTrustEngine::PKIXValidationInfoIterator& pkixInfo,
66             const vector<XSECCryptoX509CRL*>* inlineCRLs
67             ) : m_trust(t), m_pkixInfo(pkixInfo) {
68             if (inlineCRLs && !inlineCRLs->empty()) {
69                 m_crls = *inlineCRLs;
70                 m_crls.insert(m_crls.end(), pkixInfo.getCRLs().begin(), pkixInfo.getCRLs().end());
71             }
72         }
73
74         virtual ~PKIXParams() {}
75
76         int getVerificationDepth() const {
77             return m_pkixInfo.getVerificationDepth();
78         }
79         bool isAnyPolicyInhibited() const {
80             return m_trust.m_anyPolicyInhibit;
81         }
82         bool isPolicyMappingInhibited() const {
83             return m_trust.m_policyMappingInhibit;
84         }
85         const set<string>& getPolicies() const {
86             return m_trust.m_policyOIDs;
87         }
88         const vector<XSECCryptoX509*>& getTrustAnchors() const {
89             return m_pkixInfo.getTrustAnchors();
90         }
91         PKIXPathValidatorParams::revocation_t getRevocationChecking() const {
92             if (m_trust.m_checkRevocation.empty() || m_trust.m_checkRevocation == "off")
93                 return PKIXPathValidatorParams::REVOCATION_OFF;
94             else if (m_trust.m_checkRevocation == "entityOnly")
95                 return PKIXPathValidatorParams::REVOCATION_ENTITYONLY;
96             else if (m_trust.m_checkRevocation == "fullChain")
97                 return PKIXPathValidatorParams::REVOCATION_FULLCHAIN;
98             return PKIXPathValidatorParams::REVOCATION_OFF;
99         }
100         const vector<XSECCryptoX509CRL*>& getCRLs() const {
101             return m_crls.empty() ? m_pkixInfo.getCRLs() : m_crls;
102         }
103     };
104
105
106     static XMLCh fullCRLChain[] =                   UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n);
107     static XMLCh checkRevocation[] =        UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n);
108     static XMLCh policyMappingInhibit[] =   UNICODE_LITERAL_20(p,o,l,i,c,y,M,a,p,p,i,n,g,I,n,h,i,b,i,t);
109     static XMLCh anyPolicyInhibit[] =       UNICODE_LITERAL_16(a,n,y,P,o,l,i,c,y,I,n,h,i,b,i,t);
110     static XMLCh _PathValidator[] =         UNICODE_LITERAL_13(P,a,t,h,V,a,l,i,d,a,t,o,r);
111     static XMLCh PolicyOID[] =                      UNICODE_LITERAL_9(P,o,l,i,c,y,O,I,D);
112     static XMLCh TrustedName[] =                    UNICODE_LITERAL_11(T,r,u,s,t,e,d,N,a,m,e);
113     static XMLCh type[] =                   UNICODE_LITERAL_4(t,y,p,e);
114 };
115
116 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::PKIXValidationInfoIterator()
117 {
118 }
119
120 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::~PKIXValidationInfoIterator()
121 {
122 }
123
124 AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e)
125         : TrustEngine(e),
126                 m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation)),
127                 m_fullCRLChain(XMLHelper::getAttrBool(e, false, fullCRLChain)),
128                 m_policyMappingInhibit(XMLHelper::getAttrBool(e, false, policyMappingInhibit)),
129                 m_anyPolicyInhibit(XMLHelper::getAttrBool(e, false, anyPolicyInhibit))
130 {
131     if (m_fullCRLChain) {
132         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").warn(
133             "fullCRLChain option is deprecated, setting checkRevocation to \"fullChain\""
134             );
135         m_checkRevocation = "fullChain";
136     }
137     else if (m_checkRevocation == "fullChain") {
138         m_fullCRLChain = true; // in case anything's using this
139     }
140
141     xercesc::DOMElement* c = XMLHelper::getFirstChildElement(e);
142     while (c) {
143         if (c->hasChildNodes()) {
144             auto_ptr_char v(c->getTextContent());
145             if (v.get() && *v.get()) {
146                 if (XMLString::equals(c->getLocalName(), PolicyOID))
147                     m_policyOIDs.insert(v.get());
148                 else if (XMLString::equals(c->getLocalName(), TrustedName))
149                     m_trustedNames.insert(v.get());
150             }
151         }
152         else if (XMLString::equals(c->getLocalName(), _PathValidator)) {
153             try {
154                 string t = XMLHelper::getAttrString(c, nullptr, type);
155                 if (!t.empty()) {
156                     Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").info(
157                         "building PathValidator of type %s", t.c_str()
158                         );
159                     PathValidator* pv = XMLToolingConfig::getConfig().PathValidatorManager.newPlugin(t.c_str(), c);
160                     OpenSSLPathValidator* ospv = dynamic_cast<OpenSSLPathValidator*>(pv);
161                     if (!ospv) {
162                         delete pv;
163                         throw XMLSecurityException("PathValidator doesn't support OpenSSL interface.");
164                     }
165                     m_pathValidators.push_back(ospv);
166                 }
167             }
168             catch (exception& ex) {
169                 Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error(
170                     "error building PathValidator: %s", ex.what()
171                     );
172             }
173         }
174         c = XMLHelper::getNextSiblingElement(c);
175     }
176
177     if (m_pathValidators.empty()) {
178         m_pathValidators.push_back(
179             dynamic_cast<OpenSSLPathValidator*>(
180                 XMLToolingConfig::getConfig().PathValidatorManager.newPlugin(PKIX_PATHVALIDATOR, e)
181                 )
182             );
183     }
184 }
185
186 AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
187 {
188     for_each(m_pathValidators.begin(), m_pathValidators.end(), xmltooling::cleanup<PathValidator>());
189 }
190
191 bool AbstractPKIXTrustEngine::checkEntityNames(
192     X509* certEE, const CredentialResolver& credResolver, const CredentialCriteria& criteria
193     ) const
194 {
195     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
196
197     // We resolve to a set of trusted credentials.
198     vector<const Credential*> creds;
199     credResolver.resolve(creds,&criteria);
200
201     // Build a list of acceptable names.
202     set<string> trustednames = m_trustedNames;
203     if (log.isDebugEnabled()) {
204         for (set<string>::const_iterator n=m_trustedNames.begin(); n!=m_trustedNames.end(); n++) {
205             log.debug("adding to list of trusted names (%s)", n->c_str());
206         }
207     }
208     if (criteria.getPeerName()) {
209         trustednames.insert(criteria.getPeerName());
210         log.debug("adding to list of trusted names (%s)", criteria.getPeerName());
211     }
212     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
213         trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end());
214         if (log.isDebugEnabled()) {
215             for (set<string>::const_iterator n=(*cred)->getKeyNames().begin(); n!=(*cred)->getKeyNames().end(); n++) {
216                 log.debug("adding to list of trusted names (%s)", n->c_str());
217             }
218         }
219     }
220
221     X509_NAME* subject=X509_get_subject_name(certEE);
222     if (subject) {
223         // One way is a direct match to the subject DN.
224         // Seems that the way to do the compare is to write the X509_NAME into a BIO.
225         BIO* b = BIO_new(BIO_s_mem());
226         BIO* b2 = BIO_new(BIO_s_mem());
227         // The flags give us LDAP order instead of X.500, with a comma separator.
228         X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
229         BIO_flush(b);
230         // The flags give us LDAP order instead of X.500, with a comma plus space separator.
231         X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
232         BIO_flush(b2);
233
234         BUF_MEM* bptr=nullptr;
235         BUF_MEM* bptr2=nullptr;
236         BIO_get_mem_ptr(b, &bptr);
237         BIO_get_mem_ptr(b2, &bptr2);
238
239         if (bptr && bptr->length > 0 && log.isDebugEnabled()) {
240             string subjectstr(bptr->data, bptr->length);
241             log.debug("certificate subject: %s", subjectstr.c_str());
242         }
243         
244         // Check each keyname.
245         for (set<string>::const_iterator n=trustednames.begin(); bptr && bptr2 && n!=trustednames.end(); n++) {
246 #ifdef HAVE_STRCASECMP
247             if ((n->length() == bptr->length && !strncasecmp(n->c_str(), bptr->data, bptr->length)) ||
248                 (n->length() == bptr2->length && !strncasecmp(n->c_str(), bptr2->data, bptr2->length))) {
249 #else
250             if ((n->length() == bptr->length && !strnicmp(n->c_str(), bptr->data, bptr->length)) ||
251                 (n->length() == bptr2->length && !strnicmp(n->c_str(), bptr2->data, bptr2->length))) {
252 #endif
253                 log.debug("matched full subject DN to a key name (%s)", n->c_str());
254                 BIO_free(b);
255                 BIO_free(b2);
256                 return true;
257             }
258         }
259         BIO_free(b);
260         BIO_free(b2);
261
262         log.debug("unable to match DN, trying TLS subjectAltName match");
263         STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, nullptr, nullptr);
264         if (altnames) {
265             int numalts = sk_GENERAL_NAME_num(altnames);
266             for (int an=0; an<numalts; an++) {
267                 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
268                 if (check->type==GEN_DNS || check->type==GEN_URI) {
269                     const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
270                     const int altlen = ASN1_STRING_length(check->d.ia5);
271                     for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
272 #ifdef HAVE_STRCASECMP
273                         if ((check->type==GEN_DNS && n->length()==altlen && !strncasecmp(altptr,n->c_str(),altlen))
274 #else
275                         if ((check->type==GEN_DNS && n->length()==altlen && !strnicmp(altptr,n->c_str(),altlen))
276 #endif
277                                 || (check->type==GEN_URI && n->length()==altlen && !strncmp(altptr,n->c_str(),altlen))) {
278                             log.debug("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
279                             GENERAL_NAMES_free(altnames);
280                             return true;
281                         }
282                     }
283                 }
284             }
285         }
286         GENERAL_NAMES_free(altnames);
287             
288         log.debug("unable to match subjectAltName, trying TLS CN match");
289
290         // Fetch the last CN RDN.
291         char* peer_CN = nullptr;
292         int j,i = -1;
293         while ((j=X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0)
294             i = j;
295         if (i >= 0) {
296             ASN1_STRING* tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
297             // Copied in from libcurl.
298             /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
299                is already UTF-8 encoded. We check for this case and copy the raw
300                string manually to avoid the problem. */
301             if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
302                 j = ASN1_STRING_length(tmp);
303                 if(j >= 0) {
304                     peer_CN = (char*)OPENSSL_malloc(j + 1);
305                     memcpy(peer_CN, ASN1_STRING_data(tmp), j);
306                     peer_CN[j] = '\0';
307                 }
308             }
309             else /* not a UTF8 name */ {
310                 j = ASN1_STRING_to_UTF8(reinterpret_cast<unsigned char**>(&peer_CN), tmp);
311             }
312
313             for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
314 #ifdef HAVE_STRCASECMP
315                 if (n->length() == j && !strncasecmp(peer_CN, n->c_str(), j)) {
316 #else
317                 if (n->length() == j && !strnicmp(peer_CN, n->c_str(), j)) {
318 #endif
319                     log.debug("matched subject CN to a key name (%s)", n->c_str());
320                     if(peer_CN)
321                         OPENSSL_free(peer_CN);
322                     return true;
323                 }
324             }
325             if(peer_CN)
326                 OPENSSL_free(peer_CN);
327         }
328         else {
329             log.warn("no common name in certificate subject");
330         }
331     }
332     else {
333         log.error("certificate has no subject?!");
334     }
335     
336     return false;
337 }
338
339 bool AbstractPKIXTrustEngine::validateWithCRLs(
340     X509* certEE,
341     STACK_OF(X509)* certChain,
342     const CredentialResolver& credResolver,
343     CredentialCriteria* criteria,
344     const vector<XSECCryptoX509CRL*>* inlineCRLs
345     ) const
346 {
347 #ifdef _DEBUG
348     NDC ndc("validateWithCRLs");
349 #endif
350     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
351
352     if (!certEE) {
353         log.error("X.509 credential was NULL, unable to perform validation");
354         return false;
355     }
356
357     if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
358         log.debug("checking that the certificate name is acceptable");
359         if (criteria && criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
360             criteria->setUsage(Credential::SIGNING_CREDENTIAL);
361         if (!checkEntityNames(certEE,credResolver,*criteria)) {
362             log.error("certificate name was not acceptable");
363             return false;
364         }
365     }
366     else if (!m_trustedNames.empty()) {
367         log.debug("checking that the certificate name is acceptable");
368         CredentialCriteria cc;
369         cc.setUsage(Credential::SIGNING_CREDENTIAL);
370         if (!checkEntityNames(certEE,credResolver,cc)) {
371             log.error("certificate name was not acceptable");
372             return false;
373         }
374     }
375     
376     log.debug("performing certificate path validation...");
377
378     auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(credResolver, criteria));
379     while (pkix->next()) {
380         PKIXParams params(*this, *pkix.get(), inlineCRLs);
381         for (vector<OpenSSLPathValidator*>::const_iterator v = m_pathValidators.begin(); v != m_pathValidators.end(); ++v) {
382             if ((*v)->validate(certEE, certChain, params)) {
383                 return true;
384             }
385         }
386     }
387
388     log.debug("failed to validate certificate chain using supplied PKIX information");
389     return false;
390 }
391
392 bool AbstractPKIXTrustEngine::validate(
393     X509* certEE,
394     STACK_OF(X509)* certChain,
395     const CredentialResolver& credResolver,
396     CredentialCriteria* criteria
397     ) const
398 {
399     return validateWithCRLs(certEE,certChain,credResolver,criteria);
400 }
401
402 bool AbstractPKIXTrustEngine::validate(
403     XSECCryptoX509* certEE,
404     const vector<XSECCryptoX509*>& certChain,
405     const CredentialResolver& credResolver,
406     CredentialCriteria* criteria
407     ) const
408 {
409 #ifdef _DEBUG
410         NDC ndc("validate");
411 #endif
412     if (!certEE) {
413         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("X.509 credential was NULL, unable to perform validation");
414         return false;
415     }
416     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
417         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("only the OpenSSL XSEC provider is supported");
418         return false;
419     }
420
421     STACK_OF(X509)* untrusted=sk_X509_new_null();
422     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i)
423         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
424
425     bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria);
426     sk_X509_free(untrusted);
427     return ret;
428 }
429
430 bool AbstractPKIXTrustEngine::validate(
431     Signature& sig,
432     const CredentialResolver& credResolver,
433     CredentialCriteria* criteria
434     ) const
435 {
436 #ifdef _DEBUG
437     NDC ndc("validate");
438 #endif
439     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
440
441     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
442     if (!inlineResolver)
443         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
444     if (!inlineResolver) {
445         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
446         return false;
447     }
448
449     // Pull the certificate chain out of the signature.
450     X509Credential* x509cred;
451     auto_ptr<Credential> cred(inlineResolver->resolve(&sig,X509Credential::RESOLVE_CERTS|X509Credential::RESOLVE_CRLS));
452     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
453         log.error("unable to perform PKIX validation, signature does not contain any certificates");
454         return false;
455     }
456     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
457     if (certs.empty()) {
458         log.error("unable to perform PKIX validation, signature does not contain any certificates");
459         return false;
460     }
461
462     log.debug("validating signature using certificate from within the signature");
463
464     // Find and save off a pointer to the certificate that unlocks the object.
465     // Most of the time, this will be the first one anyway.
466     XSECCryptoX509* certEE=nullptr;
467     SignatureValidator keyValidator;
468     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
469         try {
470             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
471             keyValidator.setKey(key.get());
472             keyValidator.validate(&sig);
473             log.debug("signature verified with key inside signature, attempting certificate validation...");
474             certEE=(*i);
475         }
476         catch (ValidationException& ex) {
477             log.debug(ex.what());
478         }
479     }
480     
481     if (!certEE) {
482         log.debug("failed to verify signature with embedded certificates");
483         return false;
484     }
485     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
486         log.error("only the OpenSSL XSEC provider is supported");
487         return false;
488     }
489
490     STACK_OF(X509)* untrusted=sk_X509_new_null();
491     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
492         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
493     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
494     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
495     sk_X509_free(untrusted);
496     return ret;
497 }
498
499 bool AbstractPKIXTrustEngine::validate(
500     const XMLCh* sigAlgorithm,
501     const char* sig,
502     KeyInfo* keyInfo,
503     const char* in,
504     unsigned int in_len,
505     const CredentialResolver& credResolver,
506     CredentialCriteria* criteria
507     ) const
508 {
509 #ifdef _DEBUG
510     NDC ndc("validate");
511 #endif
512     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
513
514     if (!keyInfo) {
515         log.error("unable to perform PKIX validation, KeyInfo not present");
516         return false;
517     }
518
519     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
520     if (!inlineResolver)
521         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
522     if (!inlineResolver) {
523         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
524         return false;
525     }
526
527     // Pull the certificate chain out of the signature.
528     X509Credential* x509cred;
529     auto_ptr<Credential> cred(inlineResolver->resolve(keyInfo,X509Credential::RESOLVE_CERTS));
530     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
531         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
532         return false;
533     }
534     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
535     if (certs.empty()) {
536         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
537         return false;
538     }
539
540     log.debug("validating signature using certificate from within KeyInfo");
541
542     // Find and save off a pointer to the certificate that unlocks the object.
543     // Most of the time, this will be the first one anyway.
544     XSECCryptoX509* certEE=nullptr;
545     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
546         try {
547             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
548             if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
549                 log.debug("signature verified with key inside signature, attempting certificate validation...");
550                 certEE=(*i);
551             }
552         }
553         catch (SignatureException& ex) {
554             log.debug(ex.what());
555         }
556     }
557
558     if (!certEE) {
559         log.debug("failed to verify signature with embedded certificates");
560         return false;
561     }
562     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
563         log.error("only the OpenSSL XSEC provider is supported");
564         return false;
565     }
566
567     STACK_OF(X509)* untrusted=sk_X509_new_null();
568     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
569         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
570     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
571     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
572     sk_X509_free(untrusted);
573     return ret;
574 }