Botan  2.4.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)
 
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 605 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().

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

◆ 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 500 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.

504  {
505  if(end_entity->is_self_signed())
506  {
507  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
508  }
509 
510  /*
511  * This is an inelegant but functional way of preventing path loops
512  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
513  * fingerprints in the path. If there is a duplicate, we error out.
514  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
515  */
516  std::set<std::string> certs_seen;
517 
518  cert_path.push_back(end_entity);
519  certs_seen.insert(end_entity->fingerprint("SHA-256"));
520 
521  Certificate_Store_In_Memory ee_extras;
522  for(size_t i = 0; i != end_entity_extra.size(); ++i)
523  ee_extras.add_certificate(end_entity_extra[i]);
524 
525  // iterate until we reach a root or cannot find the issuer
526  for(;;)
527  {
528  const X509_Certificate& last = *cert_path.back();
529  const X509_DN issuer_dn = last.issuer_dn();
530  const std::vector<uint8_t> auth_key_id = last.authority_key_id();
531 
532  std::shared_ptr<const X509_Certificate> issuer;
533  bool trusted_issuer = false;
534 
535  for(Certificate_Store* store : trusted_certstores)
536  {
537  issuer = store->find_cert(issuer_dn, auth_key_id);
538  if(issuer)
539  {
540  trusted_issuer = true;
541  break;
542  }
543  }
544 
545  if(!issuer)
546  {
547  // fall back to searching supplemental certs
548  issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
549  }
550 
551  if(!issuer)
552  return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
553 
554  const std::string fprint = issuer->fingerprint("SHA-256");
555 
556  if(certs_seen.count(fprint) > 0) // already seen?
557  {
558  return Certificate_Status_Code::CERT_CHAIN_LOOP;
559  }
560 
561  certs_seen.insert(fprint);
562  cert_path.push_back(issuer);
563 
564  if(issuer->is_self_signed())
565  {
566  if(trusted_issuer)
567  {
568  return Certificate_Status_Code::OK;
569  }
570  else
571  {
572  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
573  }
574  }
575  }
576  }

◆ 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::OIDS::lookup(), Botan::X509_DN::lookup_ub(), Botan::SIGNATURE_ALGO_UNKNOWN, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, 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  for(const auto& dn_pair : subject->subject_dn().get_attributes())
96  {
97  const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
98  // dn_pair = <OID,str>
99  if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
100  {
101  status.insert(Certificate_Status_Code::DN_TOO_LONG);
102  }
103  }
104 
105  // Check all certs for valid time range
106  if(validation_time < subject->not_before())
107  status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
108 
109  if(validation_time > subject->not_after())
110  status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
111 
112  // Check issuer constraints
113  if(!issuer->is_CA_cert() && !self_signed_ee_cert)
114  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
115 
116  std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
117 
118  // Check the signature algorithm
119  if(OIDS::lookup(subject->signature_algorithm().oid).empty())
120  {
121  status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
122  }
123  // only perform the following checks if the signature algorithm is known
124  else
125  {
126  if(!issuer_key)
127  {
128  status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
129  }
130  else
131  {
132  const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
133 
134  if(sig_status != Certificate_Status_Code::VERIFIED)
135  status.insert(sig_status);
136 
137  if(issuer_key->estimated_strength() < min_signature_algo_strength)
138  status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
139  }
140 
141  // Ignore untrusted hashes on self-signed roots
142  if(trusted_hashes.size() > 0 && !at_self_signed_root)
143  {
144  if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
145  status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
146  }
147  }
148 
149  // Check cert extensions
150  Extensions extensions = subject->v3_extensions();
151  const auto& extensions_vec = extensions.extensions();
152  if(subject->x509_version() < 3 && !extensions_vec.empty())
153  {
154  status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
155  }
156  for(auto& extension : extensions_vec)
157  {
158  extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
159  }
160  if(extensions.extensions().size() != extensions.get_extension_oids().size())
161  {
162  status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
163  }
164  }
165 
166  // path len check
167  size_t max_path_length = cert_path.size();
168  for(size_t i = cert_path.size() - 1; i > 0 ; --i)
169  {
170  std::set<Certificate_Status_Code>& status = cert_status.at(i);
171  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
172 
173  /*
174  * If the certificate was not self-issued, verify that max_path_length is
175  * greater than zero and decrement max_path_length by 1.
176  */
177  if(subject->subject_dn() != subject->issuer_dn())
178  {
179  if(max_path_length > 0)
180  {
181  --max_path_length;
182  }
183  else
184  {
185  status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
186  }
187  }
188 
189  /*
190  * If pathLenConstraint is present in the certificate and is less than max_path_length,
191  * set max_path_length to the value of pathLenConstraint.
192  */
193  if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
194  {
195  max_path_length = subject->path_limit();
196  }
197  }
198 
199  return cert_status;
200  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18
std::string lookup(const OID &oid)
Definition: oids.cpp: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 252 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::OIDS::lookup(), Botan::NO_MATCHING_CRLDP, and Botan::VALID_CRL_CHECKED.

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

255  {
256  if(cert_path.empty())
257  throw Invalid_Argument("PKIX::check_crl cert_path empty");
258 
259  CertificatePathStatusCodes cert_status(cert_path.size());
260  const X509_Time validation_time(ref_time);
261 
262  for(size_t i = 0; i != cert_path.size() - 1; ++i)
263  {
264  std::set<Certificate_Status_Code>& status = cert_status.at(i);
265 
266  if(i < crls.size() && crls.at(i))
267  {
268  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
269  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
270 
271  if(!ca->allowed_usage(CRL_SIGN))
272  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
273 
274  if(validation_time < crls[i]->this_update())
275  status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
276 
277  if(validation_time > crls[i]->next_update())
278  status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
279 
280  if(crls[i]->check_signature(ca->subject_public_key()) == false)
281  status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
282 
283  status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
284 
285  if(crls[i]->is_revoked(*subject))
286  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
287 
288  std::string dp = subject->crl_distribution_point();
289  if(!dp.empty())
290  {
291  if(dp != crls[i]->crl_issuing_distribution_point())
292  {
293  status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
294  }
295  }
296 
297  for(const auto& extension : crls[i]->extensions().extensions())
298  {
299  // is the extension critical and unknown?
300  if(extension.second && OIDS::lookup(extension.first->oid_of()) == "")
301  {
302  /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
303  * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
304  */
305  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
306  }
307  }
308 
309  }
310  }
311 
312  while(cert_status.size() > 0 && cert_status.back().empty())
313  cert_status.pop_back();
314 
315  return cert_status;
316  }
Definition: ffi.h:1147
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
std::string lookup(const OID &oid)
Definition: oids.cpp:18

◆ 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 319 of file x509path.cpp.

References Botan::Certificate_Store_In_Memory::add_crl(), Botan::OCSP::Request::BER_encode(), BOTAN_ASSERT_NONNULL, check_crl(), check_ocsp(), Botan::BigInt::decode(), Botan::HTTP::GET_sync(), Botan::HTTP::POST_sync(), Botan::HTTP::Response::throw_unless_ok(), and Botan::VALID_CRL_CHECKED.

322  {
323  if(cert_path.empty())
324  throw Invalid_Argument("PKIX::check_crl cert_path empty");
325 
326  if(certstores.empty())
327  throw Invalid_Argument("PKIX::check_crl certstores empty");
328 
329  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
330 
331  for(size_t i = 0; i != cert_path.size(); ++i)
332  {
333  BOTAN_ASSERT_NONNULL(cert_path[i]);
334  for(size_t c = 0; c != certstores.size(); ++c)
335  {
336  crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
337  if(crls[i])
338  break;
339  }
340  }
341 
342  return PKIX::check_crl(cert_path, crls, ref_time);
343  }
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:81
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:252

◆ 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 
)

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)
Returns
revocation status

Definition at line 203 of file x509path.cpp.

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

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

207  {
208  if(cert_path.empty())
209  throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
210 
211  CertificatePathStatusCodes cert_status(cert_path.size() - 1);
212 
213  for(size_t i = 0; i != cert_path.size() - 1; ++i)
214  {
215  std::set<Certificate_Status_Code>& status = cert_status.at(i);
216 
217  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
218  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
219 
220  if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr))
221  {
222  try
223  {
224  Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
225 
226  if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
227  {
228  // Signature ok, so check the claimed status
229  Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time);
230  status.insert(ocsp_status);
231  }
232  else
233  {
234  // Some signature problem
235  status.insert(ocsp_signature_status);
236  }
237  }
238  catch(Exception&)
239  {
240  status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
241  }
242  }
243  }
244 
245  while(cert_status.size() > 0 && cert_status.back().empty())
246  cert_status.pop_back();
247 
248  return cert_status;
249  }
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 748 of file x509path.cpp.

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

Referenced by Botan::x509_path_validate().

753  {
754  if(chain_status.empty())
755  throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
756 
757  for(size_t i = 0; i != chain_status.size() - 1; ++i)
758  {
759  bool had_crl = false, had_ocsp = false;
760 
761  if(i < crl.size() && crl[i].size() > 0)
762  {
763  for(auto&& code : crl[i])
764  {
765  if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
766  {
767  had_crl = true;
768  }
769  chain_status[i].insert(code);
770  }
771  }
772 
773  if(i < ocsp.size() && ocsp[i].size() > 0)
774  {
775  for(auto&& code : ocsp[i])
776  {
777  if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
778  {
779  had_ocsp = true;
780  }
781 
782  chain_status[i].insert(code);
783  }
784  }
785 
786  if(had_crl == false && had_ocsp == false)
787  {
788  if((require_rev_on_end_entity && i == 0) ||
789  (require_rev_on_intermediates && i > 0))
790  {
791  chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
792  }
793  }
794  }
795  }

◆ 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 797 of file x509path.cpp.

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

798  {
799  if(cert_status.empty())
800  throw Invalid_Argument("PKIX::overall_status empty cert status");
801 
802  Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
803 
804  // take the "worst" error as overall
805  for(const std::set<Certificate_Status_Code>& s : cert_status)
806  {
807  if(!s.empty())
808  {
809  auto worst = *s.rbegin();
810  // Leave informative OCSP/CRL confirmations on cert-level status only
811  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
812  {
813  overall_status = worst;
814  }
815  }
816  }
817  return overall_status;
818  }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:797
Certificate_Status_Code
Definition: cert_status.h:18