9#include <botan/x509path.h>
10#include <botan/x509_ext.h>
11#include <botan/pk_keys.h>
12#include <botan/ocsp.h>
13#include <botan/oids.h>
21#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
23 #include <botan/http_util.h>
32PKIX::check_chain(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
33 std::chrono::system_clock::time_point ref_time,
34 const std::string& hostname,
36 size_t min_signature_algo_strength,
37 const std::set<std::string>& trusted_hashes)
40 throw Invalid_Argument(
"PKIX::check_chain cert_path empty");
42 const bool self_signed_ee_cert = (cert_path.size() == 1);
48 if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
51 if(!cert_path[0]->allowed_usage(usage))
54 for(
size_t i = 0; i != cert_path.size(); ++i)
56 std::set<Certificate_Status_Code>& status = cert_status.at(i);
58 const bool at_self_signed_root = (i == cert_path.size() - 1);
60 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
62 const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
64 std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
83 status.insert(sig_status);
85 if(issuer_key->estimated_strength() < min_signature_algo_strength)
90 if(trusted_hashes.size() > 0 && !at_self_signed_root)
92 if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
105 for(
size_t i = 0; i != cert_path.size(); ++i)
107 for(
auto status : cert_status.at(i))
111 if(
static_cast<uint32_t
>(status) >= 5000)
118 if(cert_path[0]->is_CA_cert() ==
false &&
132 for(
size_t i = 0; i != cert_path.size(); ++i)
134 std::set<Certificate_Status_Code>& status = cert_status.at(i);
136 const bool at_self_signed_root = (i == cert_path.size() - 1);
138 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
140 const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
142 if(at_self_signed_root && (issuer->is_self_signed() ==
false))
147 if(subject->issuer_dn() != issuer->subject_dn())
153 if(subject->is_serial_negative())
160 for(
const auto& dn_pair : subject->subject_dn().dn_info())
164 if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
171 if(validation_time < subject->not_before())
174 if(validation_time > subject->not_after())
178 if(!issuer->is_CA_cert() && !self_signed_ee_cert)
183 if(subject->x509_version() == 1)
185 if(subject->v2_issuer_key_id().empty() ==
false ||
186 subject->v2_subject_key_id().empty() ==
false)
192 Extensions extensions = subject->v3_extensions();
193 const auto& extensions_vec = extensions.extensions();
194 if(subject->x509_version() < 3 && !extensions_vec.empty())
198 for(
auto& extension : extensions_vec)
200 extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
202 if(extensions.extensions().size() != extensions.get_extension_oids().size())
209 size_t max_path_length = cert_path.size();
210 for(
size_t i = cert_path.size() - 1; i > 0 ; --i)
212 std::set<Certificate_Status_Code>& status = cert_status.at(i);
213 const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
219 if(subject->subject_dn() != subject->issuer_dn())
221 if(max_path_length > 0)
235 if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
237 max_path_length = subject->path_limit();
245PKIX::check_ocsp(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
246 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
247 const std::vector<Certificate_Store*>& trusted_certstores,
248 std::chrono::system_clock::time_point ref_time,
249 std::chrono::seconds max_ocsp_age)
251 if(cert_path.empty())
252 throw Invalid_Argument(
"PKIX::check_ocsp cert_path empty");
256 for(
size_t i = 0; i != cert_path.size() - 1; ++i)
258 std::set<Certificate_Status_Code>& status = cert_status.at(i);
260 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
261 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
263 if(i < ocsp_responses.size() && (ocsp_responses.at(i) !=
nullptr)
271 std::vector<std::shared_ptr<const X509_Certificate>> ocsp_cert_path(cert_path.begin() + i, cert_path.end());
272 Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, ocsp_cert_path);
278 status.insert(ocsp_status);
283 status.insert(ocsp_signature_status);
293 while(cert_status.size() > 0 && cert_status.back().empty())
294 cert_status.pop_back();
300PKIX::check_crl(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
301 const std::vector<std::shared_ptr<const X509_CRL>>& crls,
302 std::chrono::system_clock::time_point ref_time)
304 if(cert_path.empty())
305 throw Invalid_Argument(
"PKIX::check_crl cert_path empty");
308 const X509_Time validation_time(ref_time);
310 for(
size_t i = 0; i != cert_path.size() - 1; ++i)
312 std::set<Certificate_Status_Code>& status = cert_status.at(i);
314 if(i < crls.size() && crls.at(i))
316 std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
317 std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
322 if(validation_time < crls[i]->this_update())
325 if(validation_time > crls[i]->next_update())
328 if(crls[i]->check_signature(ca->subject_public_key()) ==
false)
333 if(crls[i]->is_revoked(*subject))
336 std::string dp = subject->crl_distribution_point();
339 if(dp != crls[i]->crl_issuing_distribution_point())
345 for(
const auto& extension : crls[i]->extensions().extensions())
363 while(cert_status.size() > 0 && cert_status.back().empty())
364 cert_status.pop_back();
370PKIX::check_crl(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
371 const std::vector<Certificate_Store*>& certstores,
372 std::chrono::system_clock::time_point ref_time)
374 if(cert_path.empty())
375 throw Invalid_Argument(
"PKIX::check_crl cert_path empty");
377 if(certstores.empty())
378 throw Invalid_Argument(
"PKIX::check_crl certstores empty");
380 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
382 for(
size_t i = 0; i != cert_path.size(); ++i)
385 for(
size_t c = 0; c != certstores.size(); ++c)
387 crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
393 return PKIX::check_crl(cert_path, crls, ref_time);
396#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
399PKIX::check_ocsp_online(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
400 const std::vector<Certificate_Store*>& trusted_certstores,
401 std::chrono::system_clock::time_point ref_time,
402 std::chrono::milliseconds timeout,
403 bool ocsp_check_intermediate_CAs,
404 std::chrono::seconds max_ocsp_age)
406 if(cert_path.empty())
407 throw Invalid_Argument(
"PKIX::check_ocsp_online cert_path empty");
409 std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
413 if(ocsp_check_intermediate_CAs)
414 to_ocsp = cert_path.size() - 1;
415 if(cert_path.size() == 1)
418 for(
size_t i = 0; i < to_ocsp; ++i)
420 const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
421 const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
423 if(subject->ocsp_responder() ==
"")
425 ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
431 ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
432 OCSP::Request req(*issuer,
BigInt::decode(subject->serial_number()));
438 "application/ocsp-request",
443 catch(std::exception&)
447 if (http.status_code() != 200)
451 return std::make_shared<const OCSP::Response>(http.body());
456 std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
458 for(
size_t i = 0; i < ocsp_response_futures.size(); ++i)
460 ocsp_responses.push_back(ocsp_response_futures[i].get());
463 return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
467PKIX::check_crl_online(
const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
468 const std::vector<Certificate_Store*>& certstores,
469 Certificate_Store_In_Memory* crl_store,
470 std::chrono::system_clock::time_point ref_time,
471 std::chrono::milliseconds timeout)
473 if(cert_path.empty())
474 throw Invalid_Argument(
"PKIX::check_crl_online cert_path empty");
475 if(certstores.empty())
476 throw Invalid_Argument(
"PKIX::check_crl_online certstores empty");
478 std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
479 std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
481 for(
size_t i = 0; i != cert_path.size(); ++i)
483 const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
484 for(
size_t c = 0; c != certstores.size(); ++c)
486 crls[i] = certstores[c]->find_crl_for(*cert);
500 future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
502 else if(cert->crl_distribution_point() ==
"")
505 future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
506 throw Not_Implemented(
"No CRL distribution point for this certificate");
511 future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
515 http.throw_unless_ok();
517 return std::make_shared<const X509_CRL>(http.body());
522 for(
size_t i = 0; i != future_crls.size(); ++i)
524 if(future_crls[i].valid())
528 crls[i] = future_crls[i].get();
530 catch(std::exception&)
542 for(
size_t i = 0; i != crl_status.size(); ++i)
548 crl_store->add_crl(crls[i]);
559PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
560 const std::vector<Certificate_Store*>& trusted_certstores,
561 const std::shared_ptr<const X509_Certificate>& end_entity,
562 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
564 if(end_entity->is_self_signed())
575 std::set<std::string> certs_seen;
577 cert_path.push_back(end_entity);
578 certs_seen.insert(end_entity->fingerprint(
"SHA-256"));
580 Certificate_Store_In_Memory ee_extras;
581 for(
size_t i = 0; i != end_entity_extra.size(); ++i)
582 ee_extras.add_certificate(end_entity_extra[i]);
587 const X509_Certificate& last = *cert_path.back();
588 const X509_DN issuer_dn = last.issuer_dn();
589 const std::vector<uint8_t> auth_key_id = last.authority_key_id();
591 std::shared_ptr<const X509_Certificate> issuer;
592 bool trusted_issuer =
false;
594 for(Certificate_Store* store : trusted_certstores)
596 issuer = store->find_cert(issuer_dn, auth_key_id);
599 trusted_issuer =
true;
607 issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
613 const std::string fprint = issuer->fingerprint(
"SHA-256");
615 if(certs_seen.count(fprint) > 0)
620 certs_seen.insert(fprint);
621 cert_path.push_back(issuer);
623 if(issuer->is_self_signed())
643using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,
bool>;
664PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
665 const std::vector<Certificate_Store*>& trusted_certstores,
666 const std::shared_ptr<const X509_Certificate>& end_entity,
667 const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
669 if(!cert_paths_out.empty())
671 throw Invalid_Argument(
"PKIX::build_all_certificate_paths: cert_paths_out must be empty");
674 if(end_entity->is_self_signed())
682 std::vector<Certificate_Status_Code> stats;
684 Certificate_Store_In_Memory ee_extras;
685 for(
size_t i = 0; i != end_entity_extra.size(); ++i)
687 ee_extras.add_certificate(end_entity_extra[i]);
696 std::set<std::string> certs_seen;
700 std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
703 std::vector<cert_maybe_trusted> stack = { {end_entity,
false} };
705 while(!stack.empty())
708 if(stack.back().first ==
nullptr)
711 std::string fprint = path_so_far.back()->fingerprint(
"SHA-256");
712 certs_seen.erase(fprint);
713 path_so_far.pop_back();
718 std::shared_ptr<const X509_Certificate> last = stack.back().first;
719 bool trusted = stack.back().second;
723 const std::string fprint = last->fingerprint(
"SHA-256");
724 if(certs_seen.count(fprint) == 1)
732 if(last->is_self_signed())
737 cert_paths_out.push_back(path_so_far);
738 cert_paths_out.back().push_back(last);
750 const X509_DN issuer_dn = last->issuer_dn();
751 const std::vector<uint8_t> auth_key_id = last->authority_key_id();
754 std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
755 for(Certificate_Store* store : trusted_certstores)
757 auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
758 trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
762 std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
763 ee_extras.find_all_certs(issuer_dn, auth_key_id);
766 if(trusted_issuers.size() + misc_issuers.size() == 0)
773 path_so_far.push_back(last);
774 certs_seen.insert(fprint);
777 stack.push_back({std::shared_ptr<const X509_Certificate>(
nullptr),
false});
779 for(
const auto& trusted_cert : trusted_issuers)
781 stack.push_back({trusted_cert,
true});
784 for(
const auto& misc : misc_issuers)
786 stack.push_back({misc,
false});
792 if(cert_paths_out.empty())
795 throw Internal_Error(
"X509 path building failed for unknown reasons");
810 bool require_rev_on_end_entity,
811 bool require_rev_on_intermediates)
813 if(chain_status.empty())
814 throw Invalid_Argument(
"PKIX::merge_revocation_status chain_status was empty");
816 for(
size_t i = 0; i != chain_status.size() - 1; ++i)
818 bool had_crl =
false, had_ocsp =
false;
820 if(i < crl.size() && crl[i].size() > 0)
822 for(
auto&& code : crl[i])
828 chain_status[i].insert(code);
832 if(i < ocsp.size() && ocsp[i].size() > 0)
834 for(
auto&& code : ocsp[i])
843 chain_status[i].insert(code);
847 if(had_crl ==
false && had_ocsp ==
false)
849 if((require_rev_on_end_entity && i == 0) ||
850 (require_rev_on_intermediates && i > 0))
860 if(cert_status.empty())
861 throw Invalid_Argument(
"PKIX::overall_status empty cert status");
866 for(
const std::set<Certificate_Status_Code>& s : cert_status)
870 auto worst = *s.rbegin();
874 overall_status = worst;
878 return overall_status;
882 const std::vector<X509_Certificate>& end_certs,
884 const std::vector<Certificate_Store*>& trusted_roots,
885 const std::string& hostname,
887 std::chrono::system_clock::time_point ref_time,
888 std::chrono::milliseconds ocsp_timeout,
889 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
891 if(end_certs.empty())
896 std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
897 std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
898 for(
size_t i = 1; i < end_certs.size(); ++i)
900 end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
903 std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
904 Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
912 std::vector<Path_Validation_Result> error_results;
914 for(
auto cert_path : cert_paths)
917 PKIX::check_chain(cert_path, ref_time,
923 PKIX::check_crl(cert_path, trusted_roots, ref_time);
927 if(ocsp_resp.size() > 0)
929 ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.
max_ocsp_age());
932 if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
934#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
935 ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
938 ocsp_status.resize(1);
943 PKIX::merge_revocation_status(status, crl_status, ocsp_status,
954 error_results.push_back(std::move(pvd));
957 return error_results[0];
963 const std::vector<Certificate_Store*>& trusted_roots,
964 const std::string& hostname,
966 std::chrono::system_clock::time_point when,
967 std::chrono::milliseconds ocsp_timeout,
968 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
970 std::vector<X509_Certificate> certs;
971 certs.push_back(end_cert);
972 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
976 const std::vector<X509_Certificate>& end_certs,
979 const std::string& hostname,
981 std::chrono::system_clock::time_point when,
982 std::chrono::milliseconds ocsp_timeout,
983 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
985 std::vector<Certificate_Store*> trusted_roots;
988 return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
995 const std::string& hostname,
997 std::chrono::system_clock::time_point when,
998 std::chrono::milliseconds ocsp_timeout,
999 const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
1001 std::vector<X509_Certificate> certs;
1002 certs.push_back(end_cert);
1004 std::vector<Certificate_Store*> trusted_roots;
1007 return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
1011 size_t key_strength,
1012 bool ocsp_intermediates,
1013 std::chrono::seconds max_ocsp_age) :
1014 m_require_revocation_information(require_rev),
1015 m_ocsp_all_intermediates(ocsp_intermediates),
1016 m_minimum_key_strength(key_strength),
1017 m_max_ocsp_age(max_ocsp_age)
1019 if(key_strength <= 80)
1020 { m_trusted_hashes.insert(
"SHA-160"); }
1022 m_trusted_hashes.insert(
"SHA-224");
1023 m_trusted_hashes.insert(
"SHA-256");
1024 m_trusted_hashes.insert(
"SHA-384");
1025 m_trusted_hashes.insert(
"SHA-512");
1032 for(
const auto& status_set_i : all_statuses)
1034 std::set<Certificate_Status_Code> warning_set_i;
1035 for(
const auto& code : status_set_i)
1040 warning_set_i.insert(code);
1043 warnings.push_back(warning_set_i);
1050 std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
1051 m_all_status(status),
1052 m_warnings(find_warnings(m_all_status)),
1053 m_cert_path(cert_chain),
1054 m_overall(PKIX::overall_status(m_all_status))
1060 if(m_cert_path.empty())
1061 throw Invalid_State(
"Path_Validation_Result::trust_root no path set");
1063 throw Invalid_State(
"Path_Validation_Result::trust_root meaningless with invalid status");
1065 return *m_cert_path[m_cert_path.size()-1];
1070 std::set<std::string> hashes;
1071 for(
size_t i = 0; i != m_cert_path.size(); ++i)
1072 hashes.insert(m_cert_path[i]->hash_used_for_signature());
1085 for(
auto status_set_i : m_warnings)
1086 if(!status_set_i.empty())
1106 return "Unknown error";
1111 const std::string sep(
", ");
1113 for(
size_t i = 0; i < m_warnings.size(); i++)
1115 for(
auto code : m_warnings[i])
1116 res +=
"[" + std::to_string(i) +
"] " +
status_string(code) + sep;
1119 if(res.size() >= sep.size())
1120 res = res.substr(0, res.size() - sep.size());
#define BOTAN_ASSERT_NONNULL(ptr)
static BigInt decode(const uint8_t buf[], size_t length)
bool require_revocation_information() const
bool ocsp_all_intermediates() const
const std::set< std::string > & trusted_hashes() const
std::chrono::seconds max_ocsp_age() const
Path_Validation_Restrictions(bool require_rev=false, size_t minimum_key_strength=110, bool ocsp_all_intermediates=false, std::chrono::seconds max_ocsp_age=std::chrono::seconds::zero())
size_t minimum_key_strength() const
Certificate_Status_Code result() const
Path_Validation_Result(CertificatePathStatusCodes status, std::vector< std::shared_ptr< const X509_Certificate > > &&cert_chain)
bool successful_validation() const
static const char * status_string(Certificate_Status_Code code)
const X509_Certificate & trust_root() const
std::string result_string() const
std::set< std::string > trusted_hashes() const
std::string warnings_string() const
CertificatePathStatusCodes warnings() const
static size_t lookup_ub(const OID &oid)
Response GET_sync(const std::string &url, size_t allowable_redirects, std::chrono::milliseconds timeout)
Response POST_sync(const std::string &url, const std::string &content_type, const std::vector< uint8_t > &body, size_t allowable_redirects, std::chrono::milliseconds timeout)
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
std::vector< std::set< Certificate_Status_Code > > CertificatePathStatusCodes
Path_Validation_Result x509_path_validate(const std::vector< X509_Certificate > &end_certs, const Path_Validation_Restrictions &restrictions, const std::vector< Certificate_Store * > &trusted_roots, const std::string &hostname, Usage_Type usage, std::chrono::system_clock::time_point ref_time, std::chrono::milliseconds ocsp_timeout, const std::vector< std::shared_ptr< const OCSP::Response > > &ocsp_resp)
std::string to_string(ErrorType type)
Convert an ErrorType to string.
@ DUPLICATE_CERT_EXTENSION
@ CA_CERT_NOT_FOR_CRL_ISSUER
@ CA_CERT_NOT_FOR_CERT_ISSUER
@ OCSP_SERVER_NOT_AVAILABLE
@ V2_IDENTIFIERS_IN_V1_CERT
@ SIGNATURE_METHOD_TOO_WEAK