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

Functions

Certificate_Status_Code BOTAN_DLL 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 BOTAN_DLL 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 BOTAN_DLL 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 BOTAN_DLL 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 BOTAN_DLL 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 BOTAN_DLL 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 BOTAN_DLL 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

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 414 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.

Referenced by Botan::x509_path_validate().

418  {
419  if(end_entity->is_self_signed())
420  {
421  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
422  }
423 
424  /*
425  * This is an inelegant but functional way of preventing path loops
426  * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
427  * fingerprints in the path. If there is a duplicate, we error out.
428  * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
429  */
430  std::set<std::string> certs_seen;
431 
432  cert_path.push_back(end_entity);
433  certs_seen.insert(end_entity->fingerprint("SHA-256"));
434 
435  Certificate_Store_In_Memory ee_extras;
436  for(size_t i = 0; i != end_entity_extra.size(); ++i)
437  ee_extras.add_certificate(end_entity_extra[i]);
438 
439  // iterate until we reach a root or cannot find the issuer
440  for(;;)
441  {
442  const X509_Certificate& last = *cert_path.back();
443  const X509_DN issuer_dn = last.issuer_dn();
444  const std::vector<uint8_t> auth_key_id = last.authority_key_id();
445 
446  std::shared_ptr<const X509_Certificate> issuer;
447  bool trusted_issuer = false;
448 
449  for(Certificate_Store* store : trusted_certstores)
450  {
451  issuer = store->find_cert(issuer_dn, auth_key_id);
452  if(issuer)
453  {
454  trusted_issuer = true;
455  break;
456  }
457  }
458 
459  if(!issuer)
460  {
461  // fall back to searching supplemental certs
462  issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
463  }
464 
465  if(!issuer)
466  return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
467 
468  const std::string fprint = issuer->fingerprint("SHA-256");
469 
470  if(certs_seen.count(fprint) > 0) // already seen?
471  return Certificate_Status_Code::CERT_CHAIN_LOOP;
472 
473  certs_seen.insert(fprint);
474  cert_path.push_back(issuer);
475 
476  if(issuer->is_self_signed())
477  {
478  if(trusted_issuer)
479  {
480  return Certificate_Status_Code::OK;
481  }
482  else
483  {
484  return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
485  }
486  }
487  }
488  }
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 29 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::CHAIN_LACKS_TRUST_ROOT, Botan::CHAIN_NAME_MISMATCH, Botan::Extensions::extensions(), Botan::INVALID_USAGE, Botan::SIGNATURE_ERROR, Botan::SIGNATURE_METHOD_TOO_WEAK, Botan::UNTRUSTED_HASH, and Botan::UTC_OR_GENERALIZED_TIME.

Referenced by Botan::x509_path_validate().

35  {
36  if(cert_path.empty())
37  throw Invalid_Argument("PKIX::check_chain cert_path empty");
38 
39  const bool self_signed_ee_cert = (cert_path.size() == 1);
40 
41  X509_Time validation_time(ref_time);
42 
43  CertificatePathStatusCodes cert_status(cert_path.size());
44 
45  if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
46  cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
47 
48  if(!cert_path[0]->allowed_usage(usage))
49  cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
50 
51  for(size_t i = 0; i != cert_path.size(); ++i)
52  {
53  std::set<Certificate_Status_Code>& status = cert_status.at(i);
54 
55  const bool at_self_signed_root = (i == cert_path.size() - 1);
56 
57  const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
58 
59  const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
60 
61  if(at_self_signed_root && (issuer->is_self_signed() == false))
62  {
63  status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
64  }
65 
66  if(subject->issuer_dn() != issuer->subject_dn())
67  {
68  status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
69  }
70 
71  // Check all certs for valid time range
72  if(validation_time < X509_Time(subject->start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
73  status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
74 
75  if(validation_time > X509_Time(subject->end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
76  status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
77 
78  // Check issuer constraints
79 
80  if(!issuer->is_CA_cert() && !self_signed_ee_cert)
81  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
82 
83  if(issuer->path_limit() < i)
84  status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
85 
86  std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
87 
88  if(!issuer_key)
89  {
90  status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
91  }
92  else
93  {
94  if(subject->check_signature(*issuer_key) == false)
95  {
96  status.insert(Certificate_Status_Code::SIGNATURE_ERROR);
97  }
98 
99  if(issuer_key->estimated_strength() < min_signature_algo_strength)
100  status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
101  }
102 
103  // Ignore untrusted hashes on self-signed roots
104  if(trusted_hashes.size() > 0 && !at_self_signed_root)
105  {
106  if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
107  status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
108  }
109 
110  // Check cert extensions
111  Extensions extensions = subject->v3_extensions();
112  for(auto& extension : extensions.extensions())
113  {
114  extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
115  }
116  }
117 
118  return cert_status;
119  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
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 infomration

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 171 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, and Botan::VALID_CRL_CHECKED.

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

174  {
175  if(cert_path.empty())
176  throw Invalid_Argument("PKIX::check_crl cert_path empty");
177 
178  CertificatePathStatusCodes cert_status(cert_path.size());
179  const X509_Time validation_time(ref_time);
180 
181  for(size_t i = 0; i != cert_path.size() - 1; ++i)
182  {
183  std::set<Certificate_Status_Code>& status = cert_status.at(i);
184 
185  if(i < crls.size() && crls.at(i))
186  {
187  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
188  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
189 
190  if(!ca->allowed_usage(CRL_SIGN))
191  status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
192 
193  if(validation_time < X509_Time(crls[i]->this_update()))
194  status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
195 
196  if(validation_time > X509_Time(crls[i]->next_update()))
197  status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
198 
199  if(crls[i]->check_signature(ca->subject_public_key()) == false)
200  status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
201 
202  status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
203 
204  if(crls[i]->is_revoked(*subject))
205  status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
206  }
207  }
208 
209  while(cert_status.size() > 0 && cert_status.back().empty())
210  cert_status.pop_back();
211 
212  return cert_status;
213  }
Definition: ffi.h:934
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
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 infomration

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

References BOTAN_ASSERT_NONNULL, and check_crl().

219  {
220  if(cert_path.empty())
221  throw Invalid_Argument("PKIX::check_crl cert_path empty");
222 
223  if(certstores.empty())
224  throw Invalid_Argument("PKIX::check_crl certstores empty");
225 
226  std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
227 
228  for(size_t i = 0; i != cert_path.size(); ++i)
229  {
230  BOTAN_ASSERT_NONNULL(cert_path[i]);
231  for(size_t c = 0; c != certstores.size(); ++c)
232  {
233  crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
234  if(crls[i])
235  break;
236  }
237  }
238 
239  return PKIX::check_crl(cert_path, crls, ref_time);
240  }
#define BOTAN_ASSERT_NONNULL(ptr)
Definition: assert.h:79
CertificatePathStatusCodes BOTAN_DLL 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:171
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 122 of file x509path.cpp.

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

Referenced by Botan::x509_path_validate().

126  {
127  if(cert_path.empty())
128  throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
129 
130  CertificatePathStatusCodes cert_status(cert_path.size() - 1);
131 
132  for(size_t i = 0; i != cert_path.size() - 1; ++i)
133  {
134  std::set<Certificate_Status_Code>& status = cert_status.at(i);
135 
136  std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
137  std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
138 
139  if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr))
140  {
141  try
142  {
143  Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
144 
145  if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
146  {
147  // Signature ok, so check the claimed status
148  Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time);
149  status.insert(ocsp_status);
150  }
151  else
152  {
153  // Some signature problem
154  status.insert(ocsp_signature_status);
155  }
156  }
157  catch(Exception& e)
158  {
159  status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
160  }
161  }
162  }
163 
164  while(cert_status.size() > 0 && cert_status.back().empty())
165  cert_status.pop_back();
166 
167  return cert_status;
168  }
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Definition: x509path.h:29
Certificate_Status_Code
Definition: cert_status.h:18
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 490 of file x509path.cpp.

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

Referenced by Botan::x509_path_validate().

495  {
496  if(chain_status.empty())
497  throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
498 
499  for(size_t i = 0; i != chain_status.size() - 1; ++i)
500  {
501  bool had_crl = false, had_ocsp = false;
502 
503  if(i < crl.size() && crl[i].size() > 0)
504  {
505  for(auto&& code : crl[i])
506  {
507  if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
508  {
509  had_crl = true;
510  }
511  chain_status[i].insert(code);
512  }
513  }
514 
515  if(i < ocsp.size() && ocsp[i].size() > 0)
516  {
517  for(auto&& code : ocsp[i])
518  {
519  if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
520  {
521  had_ocsp = true;
522  }
523 
524  chain_status[i].insert(code);
525  }
526  }
527 
528  if(had_crl == false && had_ocsp == false)
529  {
530  if((require_rev_on_end_entity && i == 0) ||
531  (require_rev_on_intermediates && i > 0))
532  {
533  chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
534  }
535  }
536  }
537  }
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 539 of file x509path.cpp.

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

540  {
541  if(cert_status.empty())
542  throw Invalid_Argument("PKIX::overall_status empty cert status");
543 
544  Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
545 
546  // take the "worst" error as overall
547  for(const std::set<Certificate_Status_Code>& s : cert_status)
548  {
549  if(!s.empty())
550  {
551  auto worst = *s.rbegin();
552  // Leave informative OCSP/CRL confirmations on cert-level status only
553  if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
554  {
555  overall_status = worst;
556  }
557  }
558  }
559  return overall_status;
560  }
Certificate_Status_Code BOTAN_DLL overall_status(const CertificatePathStatusCodes &cert_status)
Definition: x509path.cpp:539
Certificate_Status_Code
Definition: cert_status.h:18