X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=xmltooling%2Fsecurity%2Fimpl%2FAbstractPKIXTrustEngine.cpp;h=8d838f5449b20f28639a2c5a03bc2957bfbcc9e5;hb=81b488b2790e7bdeb2f43560b1d4a7d22c3dfdf5;hp=e718cb83f041e0c218df993224a4a9955149f76d;hpb=a6a8b4411814eae523cfcd2db56df25865f5b67a;p=shibboleth%2Fcpp-xmltooling.git diff --git a/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp b/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp index e718cb8..8d838f5 100644 --- a/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp +++ b/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp @@ -1,17 +1,21 @@ -/* - * Copyright 2001-2009 Internet2 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the + * License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. */ /** @@ -25,16 +29,20 @@ #include "logging.h" #include "security/AbstractPKIXTrustEngine.h" #include "signature/KeyInfo.h" - +#include "signature/Signature.h" +#include "security/CredentialCriteria.h" +#include "security/CredentialResolver.h" +#include "security/KeyInfoResolver.h" +#include "security/OpenSSLCryptoX509CRL.h" +#include "security/SecurityHelper.h" +#include "security/X509Credential.h" +#include "signature/SignatureValidator.h" +#include "util/NDC.h" +#include "util/PathResolver.h" + +#include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include @@ -52,12 +60,206 @@ namespace { return ok; } + static string XMLTOOL_DLLLOCAL X509_NAME_to_string(X509_NAME* n) + { + string s; + BIO* b = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(b,n,0,XN_FLAG_RFC2253); + BIO_flush(b); + BUF_MEM* bptr=nullptr; + BIO_get_mem_ptr(b, &bptr); + if (bptr && bptr->length > 0) { + s.append(bptr->data, bptr->length); + } + BIO_free(b); + return s; + } + + static time_t XMLTOOL_DLLLOCAL getCRLTime(const ASN1_TIME *a) + { + struct tm t; + memset(&t, 0, sizeof(t)); + // RFC 5280, sections 5.1.2.4 and 5.1.2.5 require thisUpdate and nextUpdate + // to be encoded as UTCTime until 2049, and RFC 5280 section 4.1.2.5.1 + // further restricts the format to "YYMMDDHHMMSSZ" ("even where the number + // of seconds is zero"). + // As long as OpenSSL doesn't provide any API to convert ASN1_TIME values + // time_t, we therefore have to parse it ourselves, unfortunately. + if (sscanf((const char*)a->data, "%2d%2d%2d%2d%2d%2dZ", + &t.tm_year, &t.tm_mon, &t.tm_mday, + &t.tm_hour, &t.tm_min, &t.tm_sec) == 6) { + if (t.tm_year <= 50) { + // RFC 5280, section 4.1.2.5.1 + t.tm_year += 100; + } + t.tm_mon--; +#if defined(HAVE_TIMEGM) + return timegm(&t); +#else + // Windows, and hopefully most others...? + return mktime(&t) - timezone; +#endif + } + return (time_t)-1; + } + + static bool XMLTOOL_DLLLOCAL isFreshCRL(XSECCryptoX509CRL *c, Category* log=nullptr) + { + // eventually, these should be made configurable + #define MIN_SECS_REMAINING 86400 + #define MIN_PERCENT_REMAINING 10 + if (c) { + const X509_CRL* crl = static_cast(c)->getOpenSSLX509CRL(); + time_t thisUpdate = getCRLTime(X509_CRL_get_lastUpdate(crl)); + time_t nextUpdate = getCRLTime(X509_CRL_get_nextUpdate(crl)); + time_t now = time(nullptr); + + if (thisUpdate < 0 || nextUpdate < 0) { + // we failed to parse at least one of the fields (they were not encoded + // as required by RFC 5280, actually) + time_t exp = now + MIN_SECS_REMAINING; + if (log) { + log->warn("isFreshCRL (issuer '%s'): improperly encoded thisUpdate or nextUpdate field - falling back to simple time comparison", + (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str()); + } + return (X509_cmp_time(X509_CRL_get_nextUpdate(crl), &exp) > 0) ? true : false; + } + else { + if (log && log->isDebugEnabled()) { + log->debug("isFreshCRL (issuer '%s'): %.0f seconds until nextUpdate (%3.2f%% elapsed since thisUpdate)", + (X509_NAME_to_string(X509_CRL_get_issuer(crl))).c_str(), + difftime(nextUpdate, now), (difftime(now, thisUpdate) * 100) / difftime(nextUpdate, thisUpdate)); + } + + // consider it recent enough if there are at least MIN_SECS_REMAINING + // to the nextUpdate, and at least MIN_PERCENT_REMAINING of its + // overall "validity" are remaining to the nextUpdate + return (now + MIN_SECS_REMAINING < nextUpdate) && + ((difftime(nextUpdate, now) * 100) / difftime(nextUpdate, thisUpdate) > MIN_PERCENT_REMAINING); + } + } + return false; + } + + static XSECCryptoX509CRL* XMLTOOL_DLLLOCAL getRemoteCRLs(const char* cdpuri, Category& log) { + // This is a temporary CRL cache implementation to avoid breaking binary compatibility + // for the library. Caching can't rely on any member objects within the TrustEngine, + // including locks, so we're using the global library lock for the time being. + // All other state is kept in the file system. + + // minimum number of seconds between re-attempting a download from one particular CRLDP + #define MIN_RETRY_WAIT 60 + + // The filenames for the CRL cache are based on a hash of the CRL location. + string cdpfile = SecurityHelper::doHash("SHA1", cdpuri, strlen(cdpuri)) + ".crl"; + XMLToolingConfig::getConfig().getPathResolver()->resolve(cdpfile, PathResolver::XMLTOOLING_RUN_FILE); + string cdpstaging = cdpfile + ".tmp"; + string tsfile = cdpfile + ".ts"; + + time_t now = time(nullptr); + vector crls; + + try { + // While holding the lock, check for a cached copy of the CRL, and remove "expired" ones. + Locker glock(&XMLToolingConfig::getConfig()); +#ifdef WIN32 + struct _stat stat_buf; + if (_stat(cdpfile.c_str(), &stat_buf) == 0) { +#else + struct stat stat_buf; + if (stat(cdpfile.c_str(), &stat_buf) == 0) { +#endif + SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str()); + if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL || + X509_cmp_time(X509_CRL_get_nextUpdate(static_cast(crls.front())->getOpenSSLX509CRL()), &now) < 0) { + for_each(crls.begin(), crls.end(), xmltooling::cleanup()); + crls.clear(); + remove(cdpfile.c_str()); // may as well delete the local copy + remove(tsfile.c_str()); + log.info("deleting cached CRL from %s with nextUpdate field in the past", cdpuri); + } + } + } + catch (exception& ex) { + log.error("exception loading cached copy of CRL from %s: %s", cdpuri, ex.what()); + } + + if (crls.empty() || !isFreshCRL(crls.front(), &log)) { + bool updateTimestamp = true; + try { + // If we get here, the cached copy didn't exist yet, or it's time to refresh. + // To limit the rate of unsuccessful attempts when a CRLDP is unreachable, + // we remember the timestamp of the last attempt (both successful/unsuccessful). + // We store this in the file system because of the binary compatibility issue. + time_t ts = 0; + try { + Locker glock(&XMLToolingConfig::getConfig()); + ifstream tssrc(tsfile.c_str()); + if (tssrc) + tssrc >> ts; + } + catch (exception&) { + ts = 0; + } + + if (difftime(now, ts) > MIN_RETRY_WAIT) { + SOAPTransport::Address addr("AbstractPKIXTrustEngine", cdpuri, cdpuri); + string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint); + auto_ptr soap(XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr)); + soap->send(); + istream& msg = soap->receive(); + Locker glock(&XMLToolingConfig::getConfig()); + ofstream out(cdpstaging.c_str(), fstream::trunc|fstream::binary); + out << msg.rdbuf(); + out.close(); + SecurityHelper::loadCRLsFromFile(crls, cdpstaging.c_str()); + if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL || + X509_cmp_time(X509_CRL_get_nextUpdate(static_cast(crls.front())->getOpenSSLX509CRL()), &now) < 0) { + // The "new" CRL wasn't usable, so get rid of it. + for_each(crls.begin(), crls.end(), xmltooling::cleanup()); + crls.clear(); + remove(cdpstaging.c_str()); + log.error("ignoring CRL retrieved from %s with nextUpdate field in the past", cdpuri); + } + else { + // "Commit" the new CRL. Note that we might add a CRL which doesn't pass + // isFreshCRL, but that's preferrable over adding none at all. + log.info("CRL refreshed from %s", cdpuri); + remove(cdpfile.c_str()); + if (rename(cdpstaging.c_str(), cdpfile.c_str()) != 0) + log.error("unable to rename CRL staging file"); + } + } + else { + updateTimestamp = false; // don't update if we're within the backoff window + } + } + catch (exception& ex) { + log.error("exception downloading/caching CRL from %s: %s", cdpuri, ex.what()); + } + + if (updateTimestamp) { + // update the timestamp file + Locker glock(&XMLToolingConfig::getConfig()); + ofstream tssink(tsfile.c_str(), fstream::trunc); + tssink << now; + tssink.close(); + } + } + + if (crls.empty()) + return nullptr; + for_each(crls.begin() + 1, crls.end(), xmltooling::cleanup()); + return crls.front(); + } + static bool XMLTOOL_DLLLOCAL validate( X509* EE, STACK_OF(X509)* untrusted, AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo, + bool useCRL, bool fullCRLChain, - const vector* inlineCRLs=NULL + const vector* inlineCRLs=nullptr ) { Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine"); @@ -72,68 +274,38 @@ namespace { return false; } - STACK_OF(X509)* CAstack = sk_X509_new_null(); - // This contains the state of the validate operation. int count=0; X509_STORE_CTX ctx; - - const vector& CAcerts = pkixInfo->getTrustAnchors(); - for (vector::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) { - if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) { - sk_X509_push(CAstack,static_cast(*i)->getOpenSSLX509()); - ++count; - } - } - - log.debug("supplied (%d) CA certificate(s)", count); - -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) - count=0; - if (inlineCRLs) { - for (vector::const_iterator j=inlineCRLs->begin(); j!=inlineCRLs->end(); ++j) { - if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) { - // owned by store - X509_STORE_add_crl(store, X509_CRL_dup(static_cast(*j)->getOpenSSLX509CRL())); - ++count; - } - } - } - const vector& crls = pkixInfo->getCRLs(); - for (vector::const_iterator j=crls.begin(); j!=crls.end(); ++j) { - if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) { - // owned by store - X509_STORE_add_crl(store, X509_CRL_dup(static_cast(*j)->getOpenSSLX509CRL())); - ++count; - } - } - log.debug("supplied (%d) CRL(s)", count); - if (count > 0) - X509_STORE_set_flags(store, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK)); -#else - if ((inlineCRLs && !inlineCRLs->empty()) || !pkixInfo->getCRLs().empty()) { - log.warn("OpenSSL versions < 0.9.7 do not support CRL checking"); - } -#endif // AFAICT, EE and untrusted are passed in but not owned by the ctx. #if (OPENSSL_VERSION_NUMBER >= 0x00907000L) if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) { log_openssl(); log.error("unable to initialize X509_STORE_CTX"); - sk_X509_free(CAstack); X509_STORE_free(store); return false; } #else X509_STORE_CTX_init(&ctx,store,EE,untrusted); #endif - + + STACK_OF(X509)* CAstack = sk_X509_new_null(); + const vector& CAcerts = pkixInfo->getTrustAnchors(); + for (vector::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) { + if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) { + sk_X509_push(CAstack,static_cast(*i)->getOpenSSLX509()); + ++count; + } + } + log.debug("supplied (%d) CA certificate(s)", count); + // Seems to be most efficient to just pass in the CA stack. X509_STORE_CTX_trusted_stack(&ctx,CAstack); X509_STORE_CTX_set_depth(&ctx,100); // we check the depth down below X509_STORE_CTX_set_verify_cb(&ctx,error_callback); - + + // Do a first pass verify. If CRLs aren't used, this is the only pass. int ret=X509_verify_cert(&ctx); if (ret==1) { // Now see if the depth was acceptable by counting the number of intermediates. @@ -147,7 +319,89 @@ namespace { ret=0; } } - + + if (useCRL) { +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) + // When we add CRLs, we have to be sure the nextUpdate hasn't passed, because OpenSSL won't accept + // the CRL in that case. If we end up not adding a CRL for a particular link in the chain, the + // validation will fail (if the fullChain option was set). + set crlissuers; + time_t now = time(nullptr); + if (inlineCRLs) { + for (vector::const_iterator j=inlineCRLs->begin(); j!=inlineCRLs->end(); ++j) { + if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL && + (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast(*j)->getOpenSSLX509CRL()), &now) > 0)) { + // owned by store + X509_STORE_add_crl(store, X509_CRL_dup(static_cast(*j)->getOpenSSLX509CRL())); + string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast(*j)->getOpenSSLX509CRL()))); + if (!crlissuer.empty()) { + log.debug("added inline CRL issued by (%s)", crlissuer.c_str()); + crlissuers.insert(crlissuer); + } + } + } + } + const vector& crls = pkixInfo->getCRLs(); + for (vector::const_iterator j=crls.begin(); j!=crls.end(); ++j) { + if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL && + (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast(*j)->getOpenSSLX509CRL()), &now) > 0)) { + // owned by store + X509_STORE_add_crl(store, X509_CRL_dup(static_cast(*j)->getOpenSSLX509CRL())); + string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast(*j)->getOpenSSLX509CRL()))); + if (!crlissuer.empty()) { + log.debug("added CRL issued by (%s)", crlissuer.c_str()); + crlissuers.insert(crlissuer); + } + } + } + + for (int i = 0; i < sk_X509_num(untrusted); ++i) { + X509 *cert = sk_X509_value(untrusted, i); + string crlissuer(X509_NAME_to_string(X509_get_issuer_name(cert))); + if (crlissuers.count(crlissuer)) { + // We already have a CRL for this cert, so skip CRLDP processing for this one. + continue; + } + + bool foundUsableCDP = false; + STACK_OF(DIST_POINT)* dps = (STACK_OF(DIST_POINT)*)X509_get_ext_d2i(cert, NID_crl_distribution_points, nullptr, nullptr); + for (int ii = 0; !foundUsableCDP && ii < sk_DIST_POINT_num(dps); ++ii) { + DIST_POINT* dp = sk_DIST_POINT_value(dps, ii); + if (!dp->distpoint || dp->distpoint->type != 0) + continue; + for (int iii = 0; !foundUsableCDP && iii < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); ++iii) { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, iii); + // Only consider HTTP URIs, and stop after the first one we find. +#ifdef HAVE_STRCASECMP + if (gen->type == GEN_URI && (!strncasecmp((const char*)gen->d.ia5->data, "http:", 5))) { +#else + if (gen->type == GEN_URI && (!strnicmp((const char*)gen->d.ia5->data, "http:", 5))) { +#endif + const char* cdpuri = (const char*)gen->d.ia5->data; + auto_ptr crl(getRemoteCRLs(cdpuri, log)); + if (crl.get() && crl->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL && + (isFreshCRL(crl.get()) || (ii == sk_DIST_POINT_num(dps)-1 && iii == sk_GENERAL_NAME_num(dp->distpoint->name.fullname)-1))) { + // owned by store + X509_STORE_add_crl(store, X509_CRL_dup(static_cast(crl.get())->getOpenSSLX509CRL())); + log.debug("added CRL issued by (%s)", crlissuer.c_str()); + crlissuers.insert(crlissuer); + foundUsableCDP = true; + } + } + } + } + sk_DIST_POINT_free(dps); + } + + // Do a second pass verify with CRLs in place. + X509_STORE_CTX_set_flags(&ctx, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK)); + ret=X509_verify_cert(&ctx); +#else + log.warn("CRL checking is enabled, but OpenSSL version is too old"); + ret = 0; +#endif + } + // Clean up... X509_STORE_CTX_cleanup(&ctx); X509_STORE_free(store); @@ -160,13 +414,37 @@ namespace { return false; } + + static XMLCh fullCRLChain[] = UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n); + static XMLCh checkRevocation[] = UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n); }; -AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e) : TrustEngine(e), m_fullCRLChain(false) +AbstractPKIXTrustEngine::PKIXValidationInfoIterator::PKIXValidationInfoIterator() +{ +} + +AbstractPKIXTrustEngine::PKIXValidationInfoIterator::~PKIXValidationInfoIterator() +{ +} + +AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e) + : TrustEngine(e), + m_fullCRLChain(XMLHelper::getAttrBool(e, false, fullCRLChain)), + m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation)) +{ + if (m_fullCRLChain) { + Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").warn( + "fullCRLChain option is deprecated, setting checkRevocation to \"fullChain\"" + ); + m_checkRevocation = "fullChain"; + } + else if (m_checkRevocation == "fullChain") { + m_fullCRLChain = true; // in case anything's using this + } +} + +AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine() { - static XMLCh fullCRLChain[] = UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n); - const XMLCh* flag = e ? e->getAttributeNS(NULL, fullCRLChain) : NULL; - m_fullCRLChain = (flag && (*flag == xercesc::chLatin_t || *flag == xercesc::chDigit_1)); } bool AbstractPKIXTrustEngine::checkEntityNames( @@ -198,8 +476,8 @@ bool AbstractPKIXTrustEngine::checkEntityNames( X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS); BIO_flush(b2); - BUF_MEM* bptr=NULL; - BUF_MEM* bptr2=NULL; + BUF_MEM* bptr=nullptr; + BUF_MEM* bptr2=nullptr; BIO_get_mem_ptr(b, &bptr); BIO_get_mem_ptr(b2, &bptr2); @@ -227,7 +505,7 @@ bool AbstractPKIXTrustEngine::checkEntityNames( BIO_free(b2); log.debug("unable to match DN, trying TLS subjectAltName match"); - STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, NULL, NULL); + STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, nullptr, nullptr); if (altnames) { int numalts = sk_GENERAL_NAME_num(altnames); for (int an=0; an= 0) i = j; @@ -335,7 +613,14 @@ bool AbstractPKIXTrustEngine::validateWithCRLs( auto_ptr pkix(getPKIXValidationInfoIterator(credResolver, criteria)); while (pkix->next()) { - if (::validate(certEE,certChain,pkix.get(),m_fullCRLChain,inlineCRLs)) { + if (::validate( + certEE, + certChain, + pkix.get(), + (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain"), + (m_checkRevocation=="fullChain"), + (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain") ? inlineCRLs : nullptr + )) { return true; } } @@ -418,7 +703,7 @@ bool AbstractPKIXTrustEngine::validate( // Find and save off a pointer to the certificate that unlocks the object. // Most of the time, this will be the first one anyway. - XSECCryptoX509* certEE=NULL; + XSECCryptoX509* certEE=nullptr; SignatureValidator keyValidator; for (vector::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) { try { @@ -496,7 +781,7 @@ bool AbstractPKIXTrustEngine::validate( // Find and save off a pointer to the certificate that unlocks the object. // Most of the time, this will be the first one anyway. - XSECCryptoX509* certEE=NULL; + XSECCryptoX509* certEE=nullptr; for (vector::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) { try { auto_ptr key((*i)->clonePublicKey()); @@ -509,10 +794,21 @@ bool AbstractPKIXTrustEngine::validate( log.debug(ex.what()); } } - - if (certEE) - return validate(certEE,certs,credResolver,criteria); - - log.debug("failed to verify signature with embedded certificates"); - return false; + + if (!certEE) { + log.debug("failed to verify signature with embedded certificates"); + return false; + } + else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) { + log.error("only the OpenSSL XSEC provider is supported"); + return false; + } + + STACK_OF(X509)* untrusted=sk_X509_new_null(); + for (vector::const_iterator i=certs.begin(); i!=certs.end(); ++i) + sk_X509_push(untrusted,static_cast(*i)->getOpenSSLX509()); + const vector& crls = x509cred->getCRLs(); + bool ret = validateWithCRLs(static_cast(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls); + sk_X509_free(untrusted); + return ret; }