SSPCPP-616 - clean up concatenated string literals
[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 using boost::ptr_vector;
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 }
189
190 bool AbstractPKIXTrustEngine::checkEntityNames(
191     X509* certEE, const CredentialResolver& credResolver, const CredentialCriteria& criteria
192     ) const
193 {
194     Category& log=Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX");
195
196     // We resolve to a set of trusted credentials.
197     vector<const Credential*> creds;
198     credResolver.resolve(creds,&criteria);
199
200     // Build a list of acceptable names.
201     set<string> trustednames = m_trustedNames;
202     if (log.isDebugEnabled()) {
203         for (set<string>::const_iterator n=m_trustedNames.begin(); n!=m_trustedNames.end(); n++) {
204             log.debug("adding to list of trusted names (%s)", n->c_str());
205         }
206     }
207     if (criteria.getPeerName()) {
208         trustednames.insert(criteria.getPeerName());
209         log.debug("adding to list of trusted names (%s)", criteria.getPeerName());
210     }
211     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
212         trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end());
213         if (log.isDebugEnabled()) {
214             for (set<string>::const_iterator n=(*cred)->getKeyNames().begin(); n!=(*cred)->getKeyNames().end(); n++) {
215                 log.debug("adding to list of trusted names (%s)", n->c_str());
216             }
217         }
218     }
219
220     X509_NAME* subject=X509_get_subject_name(certEE);
221     if (subject) {
222         // One way is a direct match to the subject DN.
223         // Seems that the way to do the compare is to write the X509_NAME into a BIO.
224         BIO* b = BIO_new(BIO_s_mem());
225         BIO* b2 = BIO_new(BIO_s_mem());
226         // The flags give us LDAP order instead of X.500, with a comma separator.
227         X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
228         BIO_flush(b);
229         // The flags give us LDAP order instead of X.500, with a comma plus space separator.
230         X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
231         BIO_flush(b2);
232
233         BUF_MEM* bptr=nullptr;
234         BUF_MEM* bptr2=nullptr;
235         BIO_get_mem_ptr(b, &bptr);
236         BIO_get_mem_ptr(b2, &bptr2);
237
238         if (bptr && bptr->length > 0 && log.isDebugEnabled()) {
239             string subjectstr(bptr->data, bptr->length);
240             log.debug("certificate subject: %s", subjectstr.c_str());
241         }
242         
243         // Check each keyname.
244         for (set<string>::const_iterator n=trustednames.begin(); bptr && bptr2 && n!=trustednames.end(); n++) {
245 #ifdef HAVE_STRCASECMP
246             if ((n->length() == bptr->length && !strncasecmp(n->c_str(), bptr->data, bptr->length)) ||
247                 (n->length() == bptr2->length && !strncasecmp(n->c_str(), bptr2->data, bptr2->length))) {
248 #else
249             if ((n->length() == bptr->length && !strnicmp(n->c_str(), bptr->data, bptr->length)) ||
250                 (n->length() == bptr2->length && !strnicmp(n->c_str(), bptr2->data, bptr2->length))) {
251 #endif
252                 log.debug("matched full subject DN to a key name (%s)", n->c_str());
253                 BIO_free(b);
254                 BIO_free(b2);
255                 return true;
256             }
257         }
258         BIO_free(b);
259         BIO_free(b2);
260
261         log.debug("unable to match DN, trying TLS subjectAltName match");
262         STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, nullptr, nullptr);
263         if (altnames) {
264             int numalts = sk_GENERAL_NAME_num(altnames);
265             for (int an=0; an<numalts; an++) {
266                 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
267                 if (check->type==GEN_DNS || check->type==GEN_URI) {
268                     const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
269                     const int altlen = ASN1_STRING_length(check->d.ia5);
270                     for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
271 #ifdef HAVE_STRCASECMP
272                         if ((check->type==GEN_DNS && n->length()==altlen && !strncasecmp(altptr,n->c_str(),altlen))
273 #else
274                         if ((check->type==GEN_DNS && n->length()==altlen && !strnicmp(altptr,n->c_str(),altlen))
275 #endif
276                                 || (check->type==GEN_URI && n->length()==altlen && !strncmp(altptr,n->c_str(),altlen))) {
277                             log.debug("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
278                             GENERAL_NAMES_free(altnames);
279                             return true;
280                         }
281                     }
282                 }
283             }
284         }
285         GENERAL_NAMES_free(altnames);
286             
287         log.debug("unable to match subjectAltName, trying TLS CN match");
288
289         // Fetch the last CN RDN.
290         char* peer_CN = nullptr;
291         int j,i = -1;
292         while ((j=X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0)
293             i = j;
294         if (i >= 0) {
295             ASN1_STRING* tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
296             // Copied in from libcurl.
297             /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
298                is already UTF-8 encoded. We check for this case and copy the raw
299                string manually to avoid the problem. */
300             if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
301                 j = ASN1_STRING_length(tmp);
302                 if(j >= 0) {
303                     peer_CN = (char*)OPENSSL_malloc(j + 1);
304                     memcpy(peer_CN, ASN1_STRING_data(tmp), j);
305                     peer_CN[j] = '\0';
306                 }
307             }
308             else /* not a UTF8 name */ {
309                 j = ASN1_STRING_to_UTF8(reinterpret_cast<unsigned char**>(&peer_CN), tmp);
310             }
311
312             for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
313 #ifdef HAVE_STRCASECMP
314                 if (n->length() == j && !strncasecmp(peer_CN, n->c_str(), j)) {
315 #else
316                 if (n->length() == j && !strnicmp(peer_CN, n->c_str(), j)) {
317 #endif
318                     log.debug("matched subject CN to a key name (%s)", n->c_str());
319                     if(peer_CN)
320                         OPENSSL_free(peer_CN);
321                     return true;
322                 }
323             }
324             if(peer_CN)
325                 OPENSSL_free(peer_CN);
326         }
327         else {
328             log.warn("no common name in certificate subject");
329         }
330     }
331     else {
332         log.error("certificate has no subject?!");
333     }
334     
335     return false;
336 }
337
338 bool AbstractPKIXTrustEngine::validateWithCRLs(
339     X509* certEE,
340     STACK_OF(X509)* certChain,
341     const CredentialResolver& credResolver,
342     CredentialCriteria* criteria,
343     const vector<XSECCryptoX509CRL*>* inlineCRLs
344     ) const
345 {
346 #ifdef _DEBUG
347     NDC ndc("validateWithCRLs");
348 #endif
349     Category& log=Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX");
350
351     if (!certEE) {
352         log.error("X.509 credential was NULL, unable to perform validation");
353         return false;
354     }
355
356     if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
357         log.debug("checking that the certificate name is acceptable");
358         if (criteria && criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
359             criteria->setUsage(Credential::SIGNING_CREDENTIAL);
360         if (!checkEntityNames(certEE,credResolver,*criteria)) {
361             log.error("certificate name was not acceptable");
362             return false;
363         }
364     }
365     else if (!m_trustedNames.empty()) {
366         log.debug("checking that the certificate name is acceptable");
367         CredentialCriteria cc;
368         cc.setUsage(Credential::SIGNING_CREDENTIAL);
369         if (!checkEntityNames(certEE,credResolver,cc)) {
370             log.error("certificate name was not acceptable");
371             return false;
372         }
373     }
374     
375     log.debug("performing certificate path validation...");
376
377     auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(credResolver, criteria));
378     while (pkix->next()) {
379         PKIXParams params(*this, *pkix.get(), inlineCRLs);
380         for (ptr_vector<OpenSSLPathValidator>::const_iterator v = m_pathValidators.begin(); v != m_pathValidators.end(); ++v) {
381             if (v->validate(certEE, certChain, params)) {
382                 return true;
383             }
384         }
385     }
386
387     log.debug("failed to validate certificate chain using supplied PKIX information");
388     return false;
389 }
390
391 bool AbstractPKIXTrustEngine::validate(
392     X509* certEE,
393     STACK_OF(X509)* certChain,
394     const CredentialResolver& credResolver,
395     CredentialCriteria* criteria
396     ) const
397 {
398     return validateWithCRLs(certEE,certChain,credResolver,criteria);
399 }
400
401 bool AbstractPKIXTrustEngine::validate(
402     XSECCryptoX509* certEE,
403     const vector<XSECCryptoX509*>& certChain,
404     const CredentialResolver& credResolver,
405     CredentialCriteria* criteria
406     ) const
407 {
408 #ifdef _DEBUG
409         NDC ndc("validate");
410 #endif
411     if (!certEE) {
412         Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX").error("X.509 credential was NULL, unable to perform validation");
413         return false;
414     }
415     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
416         Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX").error("only the OpenSSL XSEC provider is supported");
417         return false;
418     }
419
420     STACK_OF(X509)* untrusted=sk_X509_new_null();
421     for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i)
422         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
423
424     bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria);
425     sk_X509_free(untrusted);
426     return ret;
427 }
428
429 bool AbstractPKIXTrustEngine::validate(
430     Signature& sig,
431     const CredentialResolver& credResolver,
432     CredentialCriteria* criteria
433     ) const
434 {
435 #ifdef _DEBUG
436     NDC ndc("validate");
437 #endif
438     Category& log=Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX");
439
440     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
441     if (!inlineResolver)
442         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
443     if (!inlineResolver) {
444         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
445         return false;
446     }
447
448     // Pull the certificate chain out of the signature.
449     X509Credential* x509cred;
450     auto_ptr<Credential> cred(inlineResolver->resolve(&sig,X509Credential::RESOLVE_CERTS|X509Credential::RESOLVE_CRLS));
451     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
452         log.error("unable to perform PKIX validation, signature does not contain any certificates");
453         return false;
454     }
455     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
456     if (certs.empty()) {
457         log.error("unable to perform PKIX validation, signature does not contain any certificates");
458         return false;
459     }
460
461     log.debug("validating signature using certificate from within the signature");
462
463     // Find and save off a pointer to the certificate that unlocks the object.
464     // Most of the time, this will be the first one anyway.
465     XSECCryptoX509* certEE=nullptr;
466     SignatureValidator keyValidator;
467     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
468         try {
469             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
470             keyValidator.setKey(key.get());
471             keyValidator.validate(&sig);
472             log.debug("signature verified with key inside signature, attempting certificate validation...");
473             certEE=(*i);
474         }
475         catch (ValidationException& ex) {
476             log.debug(ex.what());
477         }
478     }
479     
480     if (!certEE) {
481         log.debug("failed to verify signature with embedded certificates");
482         return false;
483     }
484     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
485         log.error("only the OpenSSL XSEC provider is supported");
486         return false;
487     }
488
489     STACK_OF(X509)* untrusted=sk_X509_new_null();
490     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
491         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
492     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
493     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
494     sk_X509_free(untrusted);
495     return ret;
496 }
497
498 bool AbstractPKIXTrustEngine::validate(
499     const XMLCh* sigAlgorithm,
500     const char* sig,
501     KeyInfo* keyInfo,
502     const char* in,
503     unsigned int in_len,
504     const CredentialResolver& credResolver,
505     CredentialCriteria* criteria
506     ) const
507 {
508 #ifdef _DEBUG
509     NDC ndc("validate");
510 #endif
511     Category& log=Category::getInstance(XMLTOOLING_LOGCAT ".TrustEngine.PKIX");
512
513     if (!keyInfo) {
514         log.error("unable to perform PKIX validation, KeyInfo not present");
515         return false;
516     }
517
518     const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
519     if (!inlineResolver)
520         inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
521     if (!inlineResolver) {
522         log.error("unable to perform PKIX validation, no KeyInfoResolver available");
523         return false;
524     }
525
526     // Pull the certificate chain out of the signature.
527     X509Credential* x509cred;
528     auto_ptr<Credential> cred(inlineResolver->resolve(keyInfo,X509Credential::RESOLVE_CERTS));
529     if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
530         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
531         return false;
532     }
533     const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
534     if (certs.empty()) {
535         log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
536         return false;
537     }
538
539     log.debug("validating signature using certificate from within KeyInfo");
540
541     // Find and save off a pointer to the certificate that unlocks the object.
542     // Most of the time, this will be the first one anyway.
543     XSECCryptoX509* certEE=nullptr;
544     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
545         try {
546             auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
547             if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
548                 log.debug("signature verified with key inside signature, attempting certificate validation...");
549                 certEE=(*i);
550             }
551         }
552         catch (SignatureException& ex) {
553             log.debug(ex.what());
554         }
555     }
556
557     if (!certEE) {
558         log.debug("failed to verify signature with embedded certificates");
559         return false;
560     }
561     else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
562         log.error("only the OpenSSL XSEC provider is supported");
563         return false;
564     }
565
566     STACK_OF(X509)* untrusted=sk_X509_new_null();
567     for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
568         sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
569     const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
570     bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
571     sk_X509_free(untrusted);
572     return ret;
573 }