Botan  2.13.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 619 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().

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

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

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

◆ 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, 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  Extensions extensions = subject->v3_extensions();
152  const auto& extensions_vec = extensions.extensions();
153  if(subject->x509_version() < 3 && !extensions_vec.empty())
154  {
155  status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
156  }
157  for(auto& extension : extensions_vec)
158  {
159  extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
160  }
161  if(extensions.extensions().size() != extensions.get_extension_oids().size())
162  {
163  status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
164  }
165  }
166 
167  // path len check
168  size_t max_path_length = cert_path.size();
169  for(size_t i = cert_path.size() - 1; i > 0 ; --i)
170  {
171  std::set<Certificate_Status_Code>& status = cert_status.at(i);
172  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
173 
174  /*
175  * If the certificate was not self-issued, verify that max_path_length is
176  * greater than zero and decrement max_path_length by 1.
177  */
178  if(subject->subject_dn() != subject->issuer_dn())
179  {
180  if(max_path_length > 0)
181  {
182  --max_path_length;
183  }
184  else
185  {
186  status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
187  }
188  }
189 
190  /*
191  * If pathLenConstraint is present in the certificate and is less than max_path_length,
192  * set max_path_length to the value of pathLenConstraint.
193  */
194  if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
195  {
196  max_path_length = subject->path_limit();
197  }
198  }
199 
200  return cert_status;
201  }
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:19

◆ 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 255 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().

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

References BOTAN_ASSERT_NONNULL, and check_crl().

328  {
329  if(cert_path.empty())
330  throw Invalid_Argument("PKIX::check_crl cert_path empty");
331 
332  if(certstores.empty())
333  throw Invalid_Argument("PKIX::check_crl certstores empty");
334 
335  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
336 
337  for(size_t i = 0; i != cert_path.size(); ++i)
338  {
339  BOTAN_ASSERT_NONNULL(cert_path[i]);
340  for(size_t c = 0; c != certstores.size(); ++c)
341  {
342  crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
343  if(crls[i])
344  break;
345  }
346  }
347 
348  return PKIX::check_crl(cert_path, crls, ref_time);
349  }
#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:255

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

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

Referenced by Botan::x509_path_validate().

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

◆ 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 762 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().

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

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

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

814  {
815  if(cert_status.empty())
816  throw Invalid_Argument("PKIX::overall_status empty cert status");
817 
818  Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
819 
820  // take the "worst" error as overall
821  for(const std::set<Certificate_Status_Code>& s : cert_status)
822  {
823  if(!s.empty())
824  {
825  auto worst = *s.rbegin();
826  // Leave informative OCSP/CRL confirmations on cert-level status only
827  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
828  {
829  overall_status = worst;
830  }
831  }
832  }
833  return overall_status;
834  }
Certificate_Status_Code overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:813
Certificate_Status_Code
Definition: cert_status.h:19