2 * Copyright 2001-2010 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * AbstractPKIXTrustEngine.cpp
20 * A trust engine that uses X.509 trust anchors and CRLs associated with a KeyInfoSource
21 * to perform PKIX validation of signatures and certificates.
26 #include "security/AbstractPKIXTrustEngine.h"
27 #include "signature/KeyInfo.h"
28 #include "signature/Signature.h"
29 #include "security/CredentialCriteria.h"
30 #include "security/CredentialResolver.h"
31 #include "security/KeyInfoResolver.h"
32 #include "security/OpenSSLCryptoX509CRL.h"
33 #include "security/SecurityHelper.h"
34 #include "security/X509Credential.h"
35 #include "signature/SignatureValidator.h"
37 #include "util/PathResolver.h"
40 #include <openssl/x509_vfy.h>
41 #include <openssl/x509v3.h>
42 #include <xercesc/util/XMLUniDefs.hpp>
43 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
45 using namespace xmlsignature;
46 using namespace xmltooling::logging;
47 using namespace xmltooling;
52 static int XMLTOOL_DLLLOCAL error_callback(int ok, X509_STORE_CTX* ctx)
55 Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
59 static string XMLTOOL_DLLLOCAL X509_NAME_to_string(X509_NAME* n)
62 BIO* b = BIO_new(BIO_s_mem());
63 X509_NAME_print_ex(b,n,0,XN_FLAG_RFC2253);
65 BUF_MEM* bptr=nullptr;
66 BIO_get_mem_ptr(b, &bptr);
67 if (bptr && bptr->length > 0) {
68 s.append(bptr->data, bptr->length);
74 static void XMLTOOL_DLLLOCAL getRemoteCRLs(vector<XSECCryptoX509CRL*>& crls, const char* cdpuri, Category& log) {
75 // This is a temporary CRL cache implementation to avoid breaking binary compatibility
76 // for the library. Caching can't rely on any member objects within the TrustEngine,
77 // including locks, so we're using the global library lock for the time being.
78 // All other state is kept in the file system.
80 // The filenames for the CRL cache are based on a hash of the CRL location.
81 string cdpfile = SecurityHelper::doHash("SHA1", cdpuri, strlen(cdpuri)) + ".crl";
82 XMLToolingConfig::getConfig().getPathResolver()->resolve(cdpfile, PathResolver::XMLTOOLING_RUN_FILE);
83 string cdpstaging = cdpfile + ".bak";
84 string counterfile = cdpfile + ".cnt";
86 // Need to move this to a configurable parameter once we can break binary compatibility.
87 // Ideally this would be based on a percentage of the original CRL window, but OpenSSL
88 // doesn't provide much in the way of ASN1_TIME parsing functions. It does support adding
89 // a fixed time value and comparing against known times.
90 #define MIN_SECS_REMAINING 86400;
93 // While holding the lock, check for a cached copy of the CRL, and check its validity.
94 Locker glock(&XMLToolingConfig::getConfig());
96 struct _stat stat_buf;
97 if (_stat(cdpfile.c_str(), &stat_buf) == 0) {
100 if (stat(cdpfile.c_str(), &stat_buf) == 0) {
102 SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str());
103 if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
104 X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), nullptr) < 0) {
105 for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
107 remove(cdpfile.c_str()); // may as well delete the local copy
108 remove(counterfile.c_str());
109 log.info("cached CRL(s) from (%s) have expired", cdpuri);
112 // Look for a file containing the allowable time remaining on the CRL.
113 // We store this counter in the file system because of the binary compatibility issue.
115 ifstream countersrc(counterfile.c_str());
117 countersrc >> counter;
123 counter = MIN_SECS_REMAINING;
125 // See if the time left is under the counter threshold.
126 time_t exp = time(nullptr) + counter;
127 if (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &exp) < 0) {
128 for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
130 log.info("cached CRL(s) from (%s) will expire within %ld seconds, attempting to update them", cdpuri, counter);
135 catch (exception& ex) {
136 log.error("exception loading cached copy of CRL at (%s): %s", cdpuri, ex.what());
141 // If we get here, the cached copy didn't exist yet, or it's time to refresh.
142 SOAPTransport::Address addr("AbstractPKIXTrustEngine", cdpuri, cdpuri);
143 string scheme(addr.m_endpoint, strchr(addr.m_endpoint,':') - addr.m_endpoint);
144 auto_ptr<SOAPTransport> soap(XMLToolingConfig::getConfig().SOAPTransportManager.newPlugin(scheme.c_str(), addr));
146 istream& msg = soap->receive();
147 Locker glock(&XMLToolingConfig::getConfig());
148 ofstream out(cdpstaging.c_str(), fstream::trunc|fstream::binary);
151 SecurityHelper::loadCRLsFromFile(crls, cdpstaging.c_str());
152 if (crls.empty() || crls.front()->getProviderName() != DSIGConstants::s_unicodeStrPROVOpenSSL ||
153 X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), nullptr) < 0) {
154 // The "new" CRLs weren't usable, so get rid of them.
155 for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
157 remove(cdpstaging.c_str());
158 log.error("updated CRL(s) from (%s) have already expired", cdpuri);
160 // If counter isn't 0, then we were attempting an update of still-valid CRLs, so reload the old ones
161 // and cut the counter in half for next time.
163 SecurityHelper::loadCRLsFromFile(crls, cdpfile.c_str());
164 ofstream countersink(counterfile.c_str(), fstream::trunc);
166 countersink << counter;
167 log.info("failed CRL update attempt, reducing threshold to %ld seconds", counter);
171 // If counter isn't zero, we reloaded; see if the new CRLs are "more" valid than before.
173 time_t exp = time(nullptr) + counter;
174 if (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(crls.front())->getOpenSSLX509CRL()), &exp) < 0) {
175 // Still invalid past the acceptable interval, so they're the same as what we had.
176 // Remove the extra copy, and cut the counter in half for next time.
177 remove(cdpstaging.c_str());
178 ofstream countersink(counterfile.c_str(), fstream::trunc);
180 countersink << counter;
181 log.info("remote CRL(s) unchanged, reducing threshold to %ld seconds", counter);
185 log.info("remote CRL(s) updated");
190 // "Commit" the new CRLs.
191 remove(cdpfile.c_str());
192 remove(counterfile.c_str());
193 if (rename(cdpstaging.c_str(), cdpfile.c_str()) != 0)
194 log.error("unable to rename CRL staging file");
198 catch (exception& ex) {
199 log.error("exception downloading/caching CRL at (%s): %s", cdpuri, ex.what());
204 static bool XMLTOOL_DLLLOCAL validate(
206 STACK_OF(X509)* untrusted,
207 AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo,
210 const vector<XSECCryptoX509CRL*>* inlineCRLs=nullptr
213 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
215 // First we build a stack of CA certs. These objects are all referenced in place.
216 log.debug("supplying PKIX Validation information");
218 // We need this for CRL support.
219 X509_STORE* store=X509_STORE_new();
225 // This contains the state of the validate operation.
229 // AFAICT, EE and untrusted are passed in but not owned by the ctx.
230 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
231 if (X509_STORE_CTX_init(&ctx,store,EE,untrusted)!=1) {
233 log.error("unable to initialize X509_STORE_CTX");
234 X509_STORE_free(store);
238 X509_STORE_CTX_init(&ctx,store,EE,untrusted);
241 STACK_OF(X509)* CAstack = sk_X509_new_null();
242 const vector<XSECCryptoX509*>& CAcerts = pkixInfo->getTrustAnchors();
243 for (vector<XSECCryptoX509*>::const_iterator i=CAcerts.begin(); i!=CAcerts.end(); ++i) {
244 if ((*i)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL) {
245 sk_X509_push(CAstack,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
249 log.debug("supplied (%d) CA certificate(s)", count);
251 // Seems to be most efficient to just pass in the CA stack.
252 X509_STORE_CTX_trusted_stack(&ctx,CAstack);
253 X509_STORE_CTX_set_depth(&ctx,100); // we check the depth down below
254 X509_STORE_CTX_set_verify_cb(&ctx,error_callback);
256 // Do a first pass verify. If CRLs aren't used, this is the only pass.
257 int ret=X509_verify_cert(&ctx);
259 // Now see if the depth was acceptable by counting the number of intermediates.
260 int depth=sk_X509_num(ctx.chain)-2;
261 if (pkixInfo->getVerificationDepth() < depth) {
263 "certificate chain was too long (%d intermediates, only %d allowed)",
264 (depth==-1) ? 0 : depth,
265 pkixInfo->getVerificationDepth()
272 #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
273 // When we add CRLs, we have to be sure the nextUpdate hasn't passed, because OpenSSL won't accept
274 // the CRL in that case. If we end up not adding a CRL for a particular link in the chain, the
275 // validation will fail (if the fullChain option was set).
276 set<string> crlissuers;
278 for (vector<XSECCryptoX509CRL*>::const_iterator j=inlineCRLs->begin(); j!=inlineCRLs->end(); ++j) {
279 if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
280 (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), nullptr)==1)) {
282 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
283 string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
284 if (!crlissuer.empty()) {
285 log.debug("added CRL issued by (%s)", crlissuer.c_str());
286 crlissuers.insert(crlissuer);
291 const vector<XSECCryptoX509CRL*>& crls = pkixInfo->getCRLs();
292 for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
293 if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
294 (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), nullptr)==1)) {
296 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
297 string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
298 if (!crlissuer.empty()) {
299 log.debug("added CRL issued by (%s)", crlissuer.c_str());
300 crlissuers.insert(crlissuer);
305 for (int i = 0; i < sk_X509_num(untrusted); ++i) {
306 X509 *cert = sk_X509_value(untrusted, i);
307 string crlissuer(X509_NAME_to_string(X509_get_issuer_name(cert)));
308 if (crlissuers.count(crlissuer)) {
309 // We already have a CRL for this cert, so skip it.
313 bool foundUsableCDP = false;
314 STACK_OF(DIST_POINT)* dps = (STACK_OF(DIST_POINT)*)X509_get_ext_d2i(cert, NID_crl_distribution_points, nullptr, nullptr);
315 for (int ii = 0; !foundUsableCDP && ii < sk_DIST_POINT_num(dps); ++ii) {
316 DIST_POINT* dp = sk_DIST_POINT_value(dps, ii);
317 if (!dp->distpoint || dp->distpoint->type != 0)
319 for (int iii = 0; !foundUsableCDP && iii < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); ++iii) {
320 GENERAL_NAME* gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, iii);
321 // Only consider HTTP URIs, and stop after the first one we find.
322 #ifdef HAVE_STRCASECMP
323 if (gen->type == GEN_URI && (!strncasecmp((const char*)gen->d.ia5->data, "http:", 5) ||
324 !strncasecmp((const char*)gen->d.ia5->data, "https:", 6))) {
326 if (gen->type == GEN_URI && (!strnicmp((const char*)gen->d.ia5->data, "http:", 5) ||
327 !strnicmp((const char*)gen->d.ia5->data, "https:", 6))) {
329 const char* cdpuri = (const char*)gen->d.ia5->data;
330 vector<XSECCryptoX509CRL*> crls;
331 getRemoteCRLs(crls, cdpuri, log);
332 for (vector<XSECCryptoX509CRL*>::const_iterator j = crls.begin(); j != crls.end(); ++j) {
333 if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
334 X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), nullptr) > 0) {
336 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
337 log.debug("added CRL issued by (%s)", crlissuer.c_str());
338 crlissuers.insert(crlissuer);
339 foundUsableCDP = true;
342 for_each(crls.begin(), crls.end(), xmltooling::cleanup<XSECCryptoX509CRL>());
346 sk_DIST_POINT_free(dps);
350 X509_STORE_set_flags(store, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK));
353 log.warn("CRL checking is enabled, but none were supplied");
354 X509_STORE_CTX_cleanup(&ctx);
355 X509_STORE_free(store);
356 sk_X509_free(CAstack);
360 log.warn("CRL checking is enabled, but OpenSSL version is too old");
361 X509_STORE_CTX_cleanup(&ctx);
362 X509_STORE_free(store);
363 sk_X509_free(CAstack);
366 // Do a second pass verify with CRLs in place.
367 ret=X509_verify_cert(&ctx);
371 X509_STORE_CTX_cleanup(&ctx);
372 X509_STORE_free(store);
373 sk_X509_free(CAstack);
376 log.debug("successfully validated certificate chain");
383 static XMLCh fullCRLChain[] = UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n);
384 static XMLCh checkRevocation[] = UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n);
387 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::PKIXValidationInfoIterator()
391 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::~PKIXValidationInfoIterator()
395 AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e)
397 m_fullCRLChain(XMLHelper::getAttrBool(e, false, fullCRLChain)),
398 m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation))
400 if (m_fullCRLChain) {
401 Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").warn(
402 "fullCRLChain option is deprecated, setting checkRevocation to \"fullChain\""
404 m_checkRevocation = "fullChain";
406 else if (m_checkRevocation == "fullChain") {
407 m_fullCRLChain = true; // in case anything's using this
411 AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
415 bool AbstractPKIXTrustEngine::checkEntityNames(
416 X509* certEE, const CredentialResolver& credResolver, const CredentialCriteria& criteria
419 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
421 // We resolve to a set of trusted credentials.
422 vector<const Credential*> creds;
423 credResolver.resolve(creds,&criteria);
425 // Build a list of acceptable names.
426 set<string> trustednames;
427 trustednames.insert(criteria.getPeerName());
428 for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred)
429 trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end());
431 X509_NAME* subject=X509_get_subject_name(certEE);
433 // One way is a direct match to the subject DN.
434 // Seems that the way to do the compare is to write the X509_NAME into a BIO.
435 BIO* b = BIO_new(BIO_s_mem());
436 BIO* b2 = BIO_new(BIO_s_mem());
437 // The flags give us LDAP order instead of X.500, with a comma separator.
438 X509_NAME_print_ex(b,subject,0,XN_FLAG_RFC2253);
440 // The flags give us LDAP order instead of X.500, with a comma plus space separator.
441 X509_NAME_print_ex(b2,subject,0,XN_FLAG_RFC2253 + XN_FLAG_SEP_CPLUS_SPC - XN_FLAG_SEP_COMMA_PLUS);
444 BUF_MEM* bptr=nullptr;
445 BUF_MEM* bptr2=nullptr;
446 BIO_get_mem_ptr(b, &bptr);
447 BIO_get_mem_ptr(b2, &bptr2);
449 if (bptr && bptr->length > 0 && log.isDebugEnabled()) {
450 string subjectstr(bptr->data, bptr->length);
451 log.debug("certificate subject: %s", subjectstr.c_str());
454 // Check each keyname.
455 for (set<string>::const_iterator n=trustednames.begin(); bptr && bptr2 && n!=trustednames.end(); n++) {
456 #ifdef HAVE_STRCASECMP
457 if ((n->length() == bptr->length && !strncasecmp(n->c_str(), bptr->data, bptr->length)) ||
458 (n->length() == bptr2->length && !strncasecmp(n->c_str(), bptr2->data, bptr2->length))) {
460 if ((n->length() == bptr->length && !strnicmp(n->c_str(), bptr->data, bptr->length)) ||
461 (n->length() == bptr2->length && !strnicmp(n->c_str(), bptr2->data, bptr2->length))) {
463 log.debug("matched full subject DN to a key name (%s)", n->c_str());
472 log.debug("unable to match DN, trying TLS subjectAltName match");
473 STACK_OF(GENERAL_NAME)* altnames=(STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(certEE, NID_subject_alt_name, nullptr, nullptr);
475 int numalts = sk_GENERAL_NAME_num(altnames);
476 for (int an=0; an<numalts; an++) {
477 const GENERAL_NAME* check = sk_GENERAL_NAME_value(altnames, an);
478 if (check->type==GEN_DNS || check->type==GEN_URI) {
479 const char* altptr = (char*)ASN1_STRING_data(check->d.ia5);
480 const int altlen = ASN1_STRING_length(check->d.ia5);
481 for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
482 #ifdef HAVE_STRCASECMP
483 if ((check->type==GEN_DNS && n->length()==altlen && !strncasecmp(altptr,n->c_str(),altlen))
485 if ((check->type==GEN_DNS && n->length()==altlen && !strnicmp(altptr,n->c_str(),altlen))
487 || (check->type==GEN_URI && n->length()==altlen && !strncmp(altptr,n->c_str(),altlen))) {
488 log.debug("matched DNS/URI subjectAltName to a key name (%s)", n->c_str());
489 GENERAL_NAMES_free(altnames);
496 GENERAL_NAMES_free(altnames);
498 log.debug("unable to match subjectAltName, trying TLS CN match");
500 // Fetch the last CN RDN.
501 char* peer_CN = nullptr;
503 while ((j=X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0)
506 ASN1_STRING* tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject, i));
507 // Copied in from libcurl.
508 /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
509 is already UTF-8 encoded. We check for this case and copy the raw
510 string manually to avoid the problem. */
511 if(tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
512 j = ASN1_STRING_length(tmp);
514 peer_CN = (char*)OPENSSL_malloc(j + 1);
515 memcpy(peer_CN, ASN1_STRING_data(tmp), j);
519 else /* not a UTF8 name */ {
520 j = ASN1_STRING_to_UTF8(reinterpret_cast<unsigned char**>(&peer_CN), tmp);
523 for (set<string>::const_iterator n=trustednames.begin(); n!=trustednames.end(); n++) {
524 #ifdef HAVE_STRCASECMP
525 if (n->length() == j && !strncasecmp(peer_CN, n->c_str(), j)) {
527 if (n->length() == j && !strnicmp(peer_CN, n->c_str(), j)) {
529 log.debug("matched subject CN to a key name (%s)", n->c_str());
531 OPENSSL_free(peer_CN);
536 OPENSSL_free(peer_CN);
539 log.warn("no common name in certificate subject");
543 log.error("certificate has no subject?!");
549 bool AbstractPKIXTrustEngine::validateWithCRLs(
551 STACK_OF(X509)* certChain,
552 const CredentialResolver& credResolver,
553 CredentialCriteria* criteria,
554 const std::vector<XSECCryptoX509CRL*>* inlineCRLs
558 NDC ndc("validateWithCRLs");
560 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
563 log.error("X.509 credential was NULL, unable to perform validation");
567 if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
568 log.debug("checking that the certificate name is acceptable");
569 if (criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
570 criteria->setUsage(Credential::SIGNING_CREDENTIAL);
571 if (!checkEntityNames(certEE,credResolver,*criteria)) {
572 log.error("certificate name was not acceptable");
577 log.debug("performing certificate path validation...");
579 auto_ptr<PKIXValidationInfoIterator> pkix(getPKIXValidationInfoIterator(credResolver, criteria));
580 while (pkix->next()) {
585 (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain"),
586 (m_checkRevocation=="fullChain"),
587 (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain") ? inlineCRLs : nullptr
593 log.debug("failed to validate certificate chain using supplied PKIX information");
597 bool AbstractPKIXTrustEngine::validate(
599 STACK_OF(X509)* certChain,
600 const CredentialResolver& credResolver,
601 CredentialCriteria* criteria
604 return validateWithCRLs(certEE,certChain,credResolver,criteria);
607 bool AbstractPKIXTrustEngine::validate(
608 XSECCryptoX509* certEE,
609 const vector<XSECCryptoX509*>& certChain,
610 const CredentialResolver& credResolver,
611 CredentialCriteria* criteria
618 Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("X.509 credential was NULL, unable to perform validation");
621 else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
622 Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").error("only the OpenSSL XSEC provider is supported");
626 STACK_OF(X509)* untrusted=sk_X509_new_null();
627 for (vector<XSECCryptoX509*>::const_iterator i=certChain.begin(); i!=certChain.end(); ++i)
628 sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
630 bool ret = validate(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria);
631 sk_X509_free(untrusted);
635 bool AbstractPKIXTrustEngine::validate(
637 const CredentialResolver& credResolver,
638 CredentialCriteria* criteria
644 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
646 const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
648 inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
649 if (!inlineResolver) {
650 log.error("unable to perform PKIX validation, no KeyInfoResolver available");
654 // Pull the certificate chain out of the signature.
655 X509Credential* x509cred;
656 auto_ptr<Credential> cred(inlineResolver->resolve(&sig,X509Credential::RESOLVE_CERTS|X509Credential::RESOLVE_CRLS));
657 if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
658 log.error("unable to perform PKIX validation, signature does not contain any certificates");
661 const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
663 log.error("unable to perform PKIX validation, signature does not contain any certificates");
667 log.debug("validating signature using certificate from within the signature");
669 // Find and save off a pointer to the certificate that unlocks the object.
670 // Most of the time, this will be the first one anyway.
671 XSECCryptoX509* certEE=nullptr;
672 SignatureValidator keyValidator;
673 for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
675 auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
676 keyValidator.setKey(key.get());
677 keyValidator.validate(&sig);
678 log.debug("signature verified with key inside signature, attempting certificate validation...");
681 catch (ValidationException& ex) {
682 log.debug(ex.what());
687 log.debug("failed to verify signature with embedded certificates");
690 else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
691 log.error("only the OpenSSL XSEC provider is supported");
695 STACK_OF(X509)* untrusted=sk_X509_new_null();
696 for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
697 sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
698 const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
699 bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
700 sk_X509_free(untrusted);
704 bool AbstractPKIXTrustEngine::validate(
705 const XMLCh* sigAlgorithm,
710 const CredentialResolver& credResolver,
711 CredentialCriteria* criteria
717 Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX");
720 log.error("unable to perform PKIX validation, KeyInfo not present");
724 const KeyInfoResolver* inlineResolver = m_keyInfoResolver;
726 inlineResolver = XMLToolingConfig::getConfig().getKeyInfoResolver();
727 if (!inlineResolver) {
728 log.error("unable to perform PKIX validation, no KeyInfoResolver available");
732 // Pull the certificate chain out of the signature.
733 X509Credential* x509cred;
734 auto_ptr<Credential> cred(inlineResolver->resolve(keyInfo,X509Credential::RESOLVE_CERTS));
735 if (!cred.get() || !(x509cred=dynamic_cast<X509Credential*>(cred.get()))) {
736 log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
739 const vector<XSECCryptoX509*>& certs = x509cred->getEntityCertificateChain();
741 log.error("unable to perform PKIX validation, KeyInfo does not contain any certificates");
745 log.debug("validating signature using certificate from within KeyInfo");
747 // Find and save off a pointer to the certificate that unlocks the object.
748 // Most of the time, this will be the first one anyway.
749 XSECCryptoX509* certEE=nullptr;
750 for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); !certEE && i!=certs.end(); ++i) {
752 auto_ptr<XSECCryptoKey> key((*i)->clonePublicKey());
753 if (Signature::verifyRawSignature(key.get(), sigAlgorithm, sig, in, in_len)) {
754 log.debug("signature verified with key inside signature, attempting certificate validation...");
758 catch (SignatureException& ex) {
759 log.debug(ex.what());
764 log.debug("failed to verify signature with embedded certificates");
767 else if (certEE->getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
768 log.error("only the OpenSSL XSEC provider is supported");
772 STACK_OF(X509)* untrusted=sk_X509_new_null();
773 for (vector<XSECCryptoX509*>::const_iterator i=certs.begin(); i!=certs.end(); ++i)
774 sk_X509_push(untrusted,static_cast<OpenSSLCryptoX509*>(*i)->getOpenSSLX509());
775 const vector<XSECCryptoX509CRL*>& crls = x509cred->getCRLs();
776 bool ret = validateWithCRLs(static_cast<OpenSSLCryptoX509*>(certEE)->getOpenSSLX509(), untrusted, credResolver, criteria, &crls);
777 sk_X509_free(untrusted);