Botan  2.15.0
Crypto and TLS for C++11
Functions
Botan::PKIX Namespace Reference

Functions

Certificate_Status_Code build_all_certificate_paths (std::vector< std::vector< std::shared_ptr< const X509_Certificate >>> &cert_paths, const std::vector< Certificate_Store *> &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate >> &end_entity_extra)
 
Certificate_Status_Code build_certificate_path (std::vector< std::shared_ptr< const X509_Certificate >> &cert_path_out, const std::vector< Certificate_Store *> &trusted_certstores, const std::shared_ptr< const X509_Certificate > &end_entity, const std::vector< std::shared_ptr< const X509_Certificate >> &end_entity_extra)
 
CertificatePathStatusCodes check_chain (const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, std::chrono::system_clock::time_point ref_time, const std::string &hostname, Usage_Type usage, size_t min_signature_algo_strength, const std::set< std::string > &trusted_hashes)
 
CertificatePathStatusCodes check_crl (const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< std::shared_ptr< const X509_CRL >> &crls, std::chrono::system_clock::time_point ref_time)
 
CertificatePathStatusCodes check_crl (const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< Certificate_Store *> &certstores, std::chrono::system_clock::time_point ref_time)
 
CertificatePathStatusCodes check_ocsp (const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< std::shared_ptr< const OCSP::Response >> &ocsp_responses, const std::vector< Certificate_Store *> &certstores, std::chrono::system_clock::time_point ref_time, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero())
 
void merge_revocation_status (CertificatePathStatusCodes &chain_status, const CertificatePathStatusCodes &crl_status, const CertificatePathStatusCodes &ocsp_status, bool require_rev_on_end_entity, bool require_rev_on_intermediates)
 
Certificate_Status_Code overall_status (const CertificatePathStatusCodes &cert_status)
 

Detailed Description

namespace PKIX holds the building blocks that are called by x509_path_validate. This allows custom validation logic to be written by applications and makes for easier testing, but unless you're positive you know what you're doing you probably want to just call x509_path_validate instead.

Function Documentation

◆ build_all_certificate_paths()

Certificate_Status_Code Botan::PKIX::build_all_certificate_paths ( std::vector< std::vector< std::shared_ptr< const X509_Certificate >>> &  cert_paths_out,
const std::vector< Certificate_Store *> &  trusted_certstores,
const std::shared_ptr< const X509_Certificate > &  end_entity,
const std::vector< std::shared_ptr< const X509_Certificate >> &  end_entity_extra 
)

Build all possible certificate paths from the end certificate to self-signed trusted roots.

All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found, one of the encountered errors is returned arbitrarily.

todo add a path building function that returns detailed information on errors encountered while building the potentially numerous path candidates.

Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS. At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN + authority key id need not be unique among the certificates used for building the path. In such a case, we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.

Definition at line 629 of file x509path.cpp.

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_all_certs(), and Botan::OK.

Referenced by Botan::x509_path_validate().

633  {
634  if(!cert_paths_out.empty())
635  {
636  throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
637  }
638 
639  if(end_entity->is_self_signed())
640  {
641  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
642  }
643 
644  /*
645  * Pile up error messages
646  */
647  std::vector<Certificate_Status_Code> stats;
648 
649  Certificate_Store_In_Memory ee_extras;
650  for(size_t i = 0; i != end_entity_extra.size(); ++i)
651  {
652  ee_extras.add_certificate(end_entity_extra[i]);
653  }
654 
655  /*
656  * This is an inelegant but functional way of preventing path loops
657  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
658  * fingerprints in the path. If there is a duplicate, we error out.
659  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
660  */
661  std::set<std::string> certs_seen;
662 
663  // new certs are added and removed from the path during the DFS
664  // it is copied into cert_paths_out when we encounter a trusted root
665  std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
666 
667  // todo can we assume that the end certificate is not trusted?
668  std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
669 
670  while(!stack.empty())
671  {
672  // found a deletion marker that guides the DFS, backtracing
673  if(stack.back().first == nullptr)
674  {
675  stack.pop_back();
676  std::string fprint = path_so_far.back()->fingerprint("SHA-256");
677  certs_seen.erase(fprint);
678  path_so_far.pop_back();
679  }
680  // process next cert on the path
681  else
682  {
683  std::shared_ptr<const X509_Certificate> last = stack.back().first;
684  bool trusted = stack.back().second;
685  stack.pop_back();
686 
687  // certificate already seen?
688  const std::string fprint = last->fingerprint("SHA-256");
689  if(certs_seen.count(fprint) == 1)
690  {
691  stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
692  // the current path ended in a loop
693  continue;
694  }
695 
696  // the current path ends here
697  if(last->is_self_signed())
698  {
699  // found a trust anchor
700  if(trusted)
701  {
702  cert_paths_out.push_back(path_so_far);
703  cert_paths_out.back().push_back(last);
704 
705  continue;
706  }
707  // found an untrustworthy root
708  else
709  {
710  stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
711  continue;
712  }
713  }
714 
715  const X509_DN issuer_dn = last->issuer_dn();
716  const std::vector<uint8_t> auth_key_id = last->authority_key_id();
717 
718  // search for trusted issuers
719  std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
720  for(Certificate_Store* store : trusted_certstores)
721  {
722  auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
723  trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
724  }
725 
726  // search the supplemental certs
727  std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
728  ee_extras.find_all_certs(issuer_dn, auth_key_id);
729 
730  // if we could not find any issuers, the current path ends here
731  if(trusted_issuers.size() + misc_issuers.size() == 0)
732  {
733  stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
734  continue;
735  }
736 
737  // push the latest certificate onto the path_so_far
738  path_so_far.push_back(last);
739  certs_seen.emplace(fprint);
740 
741  // push a deletion marker on the stack for backtracing later
742  stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
743 
744  for(const auto& trusted_cert : trusted_issuers)
745  {
746  stack.push_back({trusted_cert,true});
747  }
748 
749  for(const auto& misc : misc_issuers)
750  {
751  stack.push_back({misc,false});
752  }
753  }
754  }
755 
756  // could not construct any potentially valid path
757  if(cert_paths_out.empty())
758  {
759  if(stats.empty())
760  throw Internal_Error("X509 path building failed for unknown reasons");
761  else
762  // arbitrarily return the first error
763  return stats[0];
764  }
765  else
766  {
767  return Certificate_Status_Code::OK;
768  }
769  }

◆ build_certificate_path()

Certificate_Status_Code Botan::PKIX::build_certificate_path ( std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path_out,
const std::vector< Certificate_Store *> &  trusted_certstores,
const std::shared_ptr< const X509_Certificate > &  end_entity,
const std::vector< std::shared_ptr< const X509_Certificate >> &  end_entity_extra 
)

Build certificate path

Parameters
cert_path_outoutput parameter, cert_path will be appended to this vector
trusted_certstoreslist of certificate stores that contain trusted certificates
end_entitythe cert to be validated
end_entity_extraoptional list of additional untrusted certs for path building
Returns
result of the path building operation (OK or error)

Definition at line 524 of file x509path.cpp.

References Botan::Certificate_Store_In_Memory::add_certificate(), Botan::X509_Certificate::authority_key_id(), Botan::CANNOT_ESTABLISH_TRUST, Botan::CERT_CHAIN_LOOP, Botan::CERT_ISSUER_NOT_FOUND, Botan::Certificate_Store_In_Memory::find_cert(), Botan::X509_Certificate::issuer_dn(), and Botan::OK.

528  {
529  if(end_entity->is_self_signed())
530  {
531  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
532  }
533 
534  /*
535  * This is an inelegant but functional way of preventing path loops
536  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
537  * fingerprints in the path. If there is a duplicate, we error out.
538  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
539  */
540  std::set<std::string> certs_seen;
541 
542  cert_path.push_back(end_entity);
543  certs_seen.insert(end_entity->fingerprint("SHA-256"));
544 
545  Certificate_Store_In_Memory ee_extras;
546  for(size_t i = 0; i != end_entity_extra.size(); ++i)
547  ee_extras.add_certificate(end_entity_extra[i]);
548 
549  // iterate until we reach a root or cannot find the issuer
550  for(;;)
551  {
552  const X509_Certificate& last = *cert_path.back();
553  const X509_DN issuer_dn = last.issuer_dn();
554  const std::vector<uint8_t> auth_key_id = last.authority_key_id();
555 
556  std::shared_ptr<const X509_Certificate> issuer;
557  bool trusted_issuer = false;
558 
559  for(Certificate_Store* store : trusted_certstores)
560  {
561  issuer = store->find_cert(issuer_dn, auth_key_id);
562  if(issuer)
563  {
564  trusted_issuer = true;
565  break;
566  }
567  }
568 
569  if(!issuer)
570  {
571  // fall back to searching supplemental certs
572  issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
573  }
574 
575  if(!issuer)
576  return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
577 
578  const std::string fprint = issuer->fingerprint("SHA-256");
579 
580  if(certs_seen.count(fprint) > 0) // already seen?
581  {
582  return Certificate_Status_Code::CERT_CHAIN_LOOP;
583  }
584 
585  certs_seen.insert(fprint);
586  cert_path.push_back(issuer);
587 
588  if(issuer->is_self_signed())
589  {
590  if(trusted_issuer)
591  {
592  return Certificate_Status_Code::OK;
593  }
594  else
595  {
596  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
597  }
598  }
599  }
600  }

◆ check_chain()

CertificatePathStatusCodes Botan::PKIX::check_chain ( const std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path,
std::chrono::system_clock::time_point  ref_time,
const std::string &  hostname,
Usage_Type  usage,
size_t  min_signature_algo_strength,
const std::set< std::string > &  trusted_hashes 
)

Check the certificate chain, but not any revocation data

Parameters
cert_pathpath built by build_certificate_path with OK result
ref_timewhatever time you want to perform the validation against (normally current system clock)
hostnamethe hostname
usageend entity usage checks
min_signature_algo_strength80 or 110 typically Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2. Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
trusted_hashesset of trusted hash functions, empty means accept any hash we have an OID for
Returns
vector of results on per certificate in the path, each containing a set of results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS, then the result for that certificate is successful. If all results are

Definition at line 32 of file x509path.cpp.

References Botan::CA_CERT_NOT_FOR_CERT_ISSUER, Botan::CERT_CHAIN_TOO_LONG, Botan::CERT_HAS_EXPIRED, Botan::CERT_NAME_NOMATCH, Botan::CERT_NOT_YET_VALID, Botan::CERT_PUBKEY_INVALID, Botan::CERT_SERIAL_NEGATIVE, Botan::CHAIN_LACKS_TRUST_ROOT, Botan::CHAIN_NAME_MISMATCH, Botan::DN_TOO_LONG, Botan::DUPLICATE_CERT_EXTENSION, Botan::EXT_IN_V1_V2_CERT, Botan::Extensions::extensions(), Botan::Extensions::get_extension_oids(), Botan::INVALID_USAGE, Botan::KEY_CERT_SIGN, Botan::X509_DN::lookup_ub(), Botan::OIDS::oid2str_or_empty(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, Botan::V2_IDENTIFIERS_IN_V1_CERT, and Botan::VERIFIED.

Referenced by Botan::x509_path_validate().

38  {
39  if(cert_path.empty())
40  throw Invalid_Argument("PKIX::check_chain cert_path empty");
41 
42  const bool self_signed_ee_cert = (cert_path.size() == 1);
43 
44  X509_Time validation_time(ref_time);
45 
46  CertificatePathStatusCodes cert_status(cert_path.size());
47 
48  if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
49  cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
50 
51  if(!cert_path[0]->allowed_usage(usage))
52  cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
53 
54  if(cert_path[0]->is_CA_cert() == false &&
55  cert_path[0]->has_constraints(KEY_CERT_SIGN))
56  {
57  /*
58  "If the keyCertSign bit is asserted, then the cA bit in the
59  basic constraints extension (Section 4.2.1.9) MUST also be
60  asserted." - RFC 5280
61 
62  We don't bother doing this check on the rest of the path since they
63  must have the cA bit asserted or the validation will fail anyway.
64  */
65  cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
66  }
67 
68  for(size_t i = 0; i != cert_path.size(); ++i)
69  {
70  std::set<Certificate_Status_Code>& status = cert_status.at(i);
71 
72  const bool at_self_signed_root = (i == cert_path.size() - 1);
73 
74  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
75 
76  const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
77 
78  if(at_self_signed_root && (issuer->is_self_signed() == false))
79  {
80  status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
81  }
82 
83  if(subject->issuer_dn() != issuer->subject_dn())
84  {
85  status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
86  }
87 
88  // Check the serial number
89  if(subject->is_serial_negative())
90  {
91  status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
92  }
93 
94  // Check the subject's DN components' length
95 
96  for(const auto& dn_pair : subject->subject_dn().dn_info())
97  {
98  const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
99  // dn_pair = <OID,str>
100  if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
101  {
102  status.insert(Certificate_Status_Code::DN_TOO_LONG);
103  }
104  }
105 
106  // Check all certs for valid time range
107  if(validation_time < subject->not_before())
108  status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
109 
110  if(validation_time > subject->not_after())
111  status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
112 
113  // Check issuer constraints
114  if(!issuer->is_CA_cert() && !self_signed_ee_cert)
115  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
116 
117  std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
118 
119  // Check the signature algorithm is known
120  if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
121  {
122  status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
123  }
124  else
125  {
126  // only perform the following checks if the signature algorithm is known
127  if(!issuer_key)
128  {
129  status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
130  }
131  else
132  {
133  const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
134 
135  if(sig_status != Certificate_Status_Code::VERIFIED)
136  status.insert(sig_status);
137 
138  if(issuer_key->estimated_strength() < min_signature_algo_strength)
139  status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
140  }
141 
142  // Ignore untrusted hashes on self-signed roots
143  if(trusted_hashes.size() > 0 && !at_self_signed_root)
144  {
145  if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
146  status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
147  }
148  }
149 
150  // Check cert extensions
151 
152  if(subject->x509_version() == 1)
153  {
154  if(subject->v2_issuer_key_id().empty() == false ||
155  subject->v2_subject_key_id().empty() == false)
156  {
157  status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
158  }
159  }
160 
161  Extensions extensions = subject->v3_extensions();
162  const auto& extensions_vec = extensions.extensions();
163  if(subject->x509_version() < 3 && !extensions_vec.empty())
164  {
165  status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
166  }
167  for(auto& extension : extensions_vec)
168  {
169  extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
170  }
171  if(extensions.extensions().size() != extensions.get_extension_oids().size())
172  {
173  status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
174  }
175  }
176 
177  // path len check
178  size_t max_path_length = cert_path.size();
179  for(size_t i = cert_path.size() - 1; i > 0 ; --i)
180  {
181  std::set<Certificate_Status_Code>& status = cert_status.at(i);
182  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
183 
184  /*
185  * If the certificate was not self-issued, verify that max_path_length is
186  * greater than zero and decrement max_path_length by 1.
187  */
188  if(subject->subject_dn() != subject->issuer_dn())
189  {
190  if(max_path_length > 0)
191  {
192  --max_path_length;
193  }
194  else
195  {
196  status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
197  }
198  }
199 
200  /*
201  * If pathLenConstraint is present in the certificate and is less than max_path_length,
202  * set max_path_length to the value of pathLenConstraint.
203  */
204  if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
205  {
206  max_path_length = subject->path_limit();
207  }
208  }
209 
210  return cert_status;
211  }
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18

◆ check_crl() [1/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path,
const std::vector< std::shared_ptr< const X509_CRL >> &  crls,
std::chrono::system_clock::time_point  ref_time 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
crlsthe list of CRLs to check, it is assumed that crls[i] (if not null) is the associated CRL for the subject in cert_path[i].
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 265 of file x509path.cpp.

References Botan::CA_CERT_NOT_FOR_CRL_ISSUER, Botan::CERT_IS_REVOKED, Botan::CRL_BAD_SIGNATURE, Botan::CRL_HAS_EXPIRED, Botan::CRL_NOT_YET_VALID, Botan::CRL_SIGN, Botan::NO_MATCHING_CRLDP, Botan::OIDS::oid2str_or_empty(), and Botan::VALID_CRL_CHECKED.

Referenced by check_crl(), and Botan::x509_path_validate().

268  {
269  if(cert_path.empty())
270  throw Invalid_Argument("PKIX::check_crl cert_path empty");
271 
272  CertificatePathStatusCodes cert_status(cert_path.size());
273  const X509_Time validation_time(ref_time);
274 
275  for(size_t i = 0; i != cert_path.size() - 1; ++i)
276  {
277  std::set<Certificate_Status_Code>& status = cert_status.at(i);
278 
279  if(i < crls.size() && crls.at(i))
280  {
281  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
282  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
283 
284  if(!ca->allowed_usage(CRL_SIGN))
285  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
286 
287  if(validation_time < crls[i]->this_update())
288  status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
289 
290  if(validation_time > crls[i]->next_update())
291  status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
292 
293  if(crls[i]->check_signature(ca->subject_public_key()) == false)
294  status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
295 
296  status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
297 
298  if(crls[i]->is_revoked(*subject))
299  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
300 
301  std::string dp = subject->crl_distribution_point();
302  if(!dp.empty())
303  {
304  if(dp != crls[i]->crl_issuing_distribution_point())
305  {
306  status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
307  }
308  }
309 
310  for(const auto& extension : crls[i]->extensions().extensions())
311  {
312  // XXX this is wrong - the OID might be defined but the extention not full parsed
313  // for example see #1652
314 
315  // is the extension critical and unknown?
316  if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
317  {
318  /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
319  * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
320  */
321  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
322  }
323  }
324 
325  }
326  }
327 
328  while(cert_status.size() > 0 && cert_status.back().empty())
329  cert_status.pop_back();
330 
331  return cert_status;
332  }
Definition: ffi.h:1557
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29

◆ check_crl() [2/2]

CertificatePathStatusCodes Botan::PKIX::check_crl ( const std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path,
const std::vector< Certificate_Store *> &  certstores,
std::chrono::system_clock::time_point  ref_time 
)

Check CRLs for revocation information

Parameters
cert_pathpath already validated by check_chain
certstoresa list of certificate stores to query for the CRL
ref_timewhatever time you want to perform the validation against (normally current system clock)
Returns
revocation status

Definition at line 335 of file x509path.cpp.

References BOTAN_ASSERT_NONNULL, and check_crl().

338  {
339  if(cert_path.empty())
340  throw Invalid_Argument("PKIX::check_crl cert_path empty");
341 
342  if(certstores.empty())
343  throw Invalid_Argument("PKIX::check_crl certstores empty");
344 
345  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
346 
347  for(size_t i = 0; i != cert_path.size(); ++i)
348  {
349  BOTAN_ASSERT_NONNULL(cert_path[i]);
350  for(size_t c = 0; c != certstores.size(); ++c)
351  {
352  crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
353  if(crls[i])
354  break;
355  }
356  }
357 
358  return PKIX::check_crl(cert_path, crls, ref_time);
359  }
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:107
CertificatePathStatusCodes check_crl(const std::vector< std::shared_ptr< const X509_Certificate >> &cert_path, const std::vector< std::shared_ptr< const X509_CRL >> &crls, std::chrono::system_clock::time_point ref_time)
Definition: x509path.cpp:265

◆ check_ocsp()

CertificatePathStatusCodes Botan::PKIX::check_ocsp ( const std::vector< std::shared_ptr< const X509_Certificate >> &  cert_path,
const std::vector< std::shared_ptr< const OCSP::Response >> &  ocsp_responses,
const std::vector< Certificate_Store *> &  certstores,
std::chrono::system_clock::time_point  ref_time,
std::chrono::seconds  max_ocsp_age = std::chrono::seconds::zero() 
)

Check OCSP responses for revocation information

Parameters
cert_pathpath already validated by check_chain
ocsp_responsesthe OCSP responses to consider
certstorestrusted roots
ref_timewhatever time you want to perform the validation against (normally current system clock)
max_ocsp_agemaximum age of OCSP responses w/o next_update. If zero, there is no maximum age
Returns
revocation status

Definition at line 214 of file x509path.cpp.

References Botan::OCSP_RESPONSE_INVALID, Botan::OCSP_SIGNATURE_OK, and Botan::OCSP::Successful.

Referenced by Botan::x509_path_validate().

219  {
220  if(cert_path.empty())
221  throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
222 
223  CertificatePathStatusCodes cert_status(cert_path.size() - 1);
224 
225  for(size_t i = 0; i != cert_path.size() - 1; ++i)
226  {
227  std::set<Certificate_Status_Code>& status = cert_status.at(i);
228 
229  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
230  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
231 
232  if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
233  && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
234  {
235  try
236  {
237  Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
238 
239  if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
240  {
241  // Signature ok, so check the claimed status
242  Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
243  status.insert(ocsp_status);
244  }
245  else
246  {
247  // Some signature problem
248  status.insert(ocsp_signature_status);
249  }
250  }
251  catch(Exception&)
252  {
253  status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
254  }
255  }
256  }
257 
258  while(cert_status.size() > 0 && cert_status.back().empty())
259  cert_status.pop_back();
260 
261  return cert_status;
262  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18

◆ merge_revocation_status()

void Botan::PKIX::merge_revocation_status ( CertificatePathStatusCodes chain_status,
const CertificatePathStatusCodes crl_status,
const CertificatePathStatusCodes ocsp_status,
bool  require_rev_on_end_entity,
bool  require_rev_on_intermediates 
)

Merge the results from CRL and/or OCSP checks into chain_status

Parameters
chain_statusthe certificate status
crl_statusresults from check_crl
ocsp_statusresults from check_ocsp
require_rev_on_end_entityrequire valid CRL or OCSP on end-entity cert
require_rev_on_intermediatesrequire valid CRL or OCSP on all intermediate certificates

Definition at line 772 of file x509path.cpp.

References Botan::NO_REVOCATION_DATA, Botan::OCSP_NO_REVOCATION_URL, Botan::OCSP_RESPONSE_GOOD, Botan::OCSP_SERVER_NOT_AVAILABLE, and Botan::VALID_CRL_CHECKED.

Referenced by Botan::x509_path_validate().

777  {
778  if(chain_status.empty())
779  throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
780 
781  for(size_t i = 0; i != chain_status.size() - 1; ++i)
782  {
783  bool had_crl = false, had_ocsp = false;
784 
785  if(i < crl.size() && crl[i].size() > 0)
786  {
787  for(auto&& code : crl[i])
788  {
789  if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
790  {
791  had_crl = true;
792  }
793  chain_status[i].insert(code);
794  }
795  }
796 
797  if(i < ocsp.size() && ocsp[i].size() > 0)
798  {
799  for(auto&& code : ocsp[i])
800  {
801  if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
802  code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
803  code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
804  {
805  had_ocsp = true;
806  }
807 
808  chain_status[i].insert(code);
809  }
810  }
811 
812  if(had_crl == false && had_ocsp == false)
813  {
814  if((require_rev_on_end_entity && i == 0) ||
815  (require_rev_on_intermediates && i > 0))
816  {
817  chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
818  }
819  }
820  }
821  }

◆ overall_status()

Certificate_Status_Code Botan::PKIX::overall_status ( const CertificatePathStatusCodes cert_status)

Find overall status (OK, error) of a validation

Parameters
cert_statusresult of merge_revocation_status or check_chain

Definition at line 823 of file x509path.cpp.

References Botan::FIRST_ERROR_STATUS, and Botan::OK.

824  {
825  if(cert_status.empty())
826  throw Invalid_Argument("PKIX::overall_status empty cert status");
827 
828  Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
829 
830  // take the "worst" error as overall
831  for(const std::set<Certificate_Status_Code>& s : cert_status)
832  {
833  if(!s.empty())
834  {
835  auto worst = *s.rbegin();
836  // Leave informative OCSP/CRL confirmations on cert-level status only
837  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
838  {
839  overall_status = worst;
840  }
841  }
842  }
843  return overall_status;
844  }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:823
Certificate_Status_Code
Definition: cert_status.h:18