aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp')
-rw-r--r--src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp879
1 files changed, 879 insertions, 0 deletions
diff --git a/src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp b/src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp
new file mode 100644
index 0000000000..ddfe5d5b2b
--- /dev/null
+++ b/src/libs/3rdparty/botan/src/lib/x509/x509cert.cpp
@@ -0,0 +1,879 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509cert.h>
+#include <botan/datastor.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_ext.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+struct X509_Certificate_Data
+ {
+ std::vector<uint8_t> m_serial;
+ AlgorithmIdentifier m_sig_algo_inner;
+ X509_DN m_issuer_dn;
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_issuer_dn_bits;
+ std::vector<uint8_t> m_subject_dn_bits;
+ X509_Time m_not_before;
+ X509_Time m_not_after;
+ std::vector<uint8_t> m_subject_public_key_bits;
+ std::vector<uint8_t> m_subject_public_key_bits_seq;
+ std::vector<uint8_t> m_subject_public_key_bitstring;
+ std::vector<uint8_t> m_subject_public_key_bitstring_sha1;
+ AlgorithmIdentifier m_subject_public_key_algid;
+
+ std::vector<uint8_t> m_v2_issuer_key_id;
+ std::vector<uint8_t> m_v2_subject_key_id;
+ Extensions m_v3_extensions;
+
+ std::vector<OID> m_extended_key_usage;
+ std::vector<uint8_t> m_authority_key_id;
+ std::vector<uint8_t> m_subject_key_id;
+ std::vector<OID> m_cert_policies;
+
+ std::vector<std::string> m_crl_distribution_points;
+ std::string m_ocsp_responder;
+ std::vector<std::string> m_ca_issuers;
+
+ AlternativeName m_subject_alt_name;
+ AlternativeName m_issuer_alt_name;
+ NameConstraints m_name_constraints;
+
+ Data_Store m_subject_ds;
+ Data_Store m_issuer_ds;
+
+ size_t m_version = 0;
+ size_t m_path_len_constraint = 0;
+ Key_Constraints m_key_constraints = NO_CONSTRAINTS;
+ bool m_self_signed = false;
+ bool m_is_ca_certificate = false;
+ bool m_serial_negative = false;
+ };
+
+std::string X509_Certificate::PEM_label() const
+ {
+ return "CERTIFICATE";
+ }
+
+std::vector<std::string> X509_Certificate::alternate_PEM_labels() const
+ {
+ return { "X509 CERTIFICATE" };
+ }
+
+X509_Certificate::X509_Certificate(DataSource& src)
+ {
+ load_data(src);
+ }
+
+X509_Certificate::X509_Certificate(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+X509_Certificate::X509_Certificate(const uint8_t data[], size_t len)
+ {
+ DataSource_Memory src(data, len);
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_Certificate::X509_Certificate(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+namespace {
+
+std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& obj)
+ {
+ std::unique_ptr<X509_Certificate_Data> data(new X509_Certificate_Data);
+
+ BigInt serial_bn;
+ BER_Object public_key;
+ BER_Object v3_exts_data;
+
+ BER_Decoder(obj.signed_body())
+ .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ .decode(serial_bn)
+ .decode(data->m_sig_algo_inner)
+ .decode(data->m_issuer_dn)
+ .start_cons(SEQUENCE)
+ .decode(data->m_not_before)
+ .decode(data->m_not_after)
+ .end_cons()
+ .decode(data->m_subject_dn)
+ .get_next(public_key)
+ .decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1)
+ .decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2)
+ .get_next(v3_exts_data)
+ .verify_end("TBSCertificate has extra data after extensions block");
+
+ if(data->m_version > 2)
+ throw Decoding_Error("Unknown X.509 cert version " + std::to_string(data->m_version));
+ if(obj.signature_algorithm() != data->m_sig_algo_inner)
+ throw Decoding_Error("X.509 Certificate had differing algorithm identifers in inner and outer ID fields");
+
+ public_key.assert_is_a(SEQUENCE, CONSTRUCTED, "X.509 certificate public key");
+
+ // crude method to save the serial's sign; will get lost during decoding, otherwise
+ data->m_serial_negative = serial_bn.is_negative();
+
+ // for general sanity convert wire version (0 based) to standards version (v1 .. v3)
+ data->m_version += 1;
+
+ data->m_serial = BigInt::encode(serial_bn);
+ data->m_subject_dn_bits = ASN1::put_in_sequence(data->m_subject_dn.get_bits());
+ data->m_issuer_dn_bits = ASN1::put_in_sequence(data->m_issuer_dn.get_bits());
+
+ // validate_public_key_params(public_key.value);
+ AlgorithmIdentifier public_key_alg_id;
+ BER_Decoder(public_key).decode(public_key_alg_id).discard_remaining();
+
+ std::vector<std::string> public_key_info =
+ split_on(OIDS::oid2str(public_key_alg_id.get_oid()), '/');
+
+ if(!public_key_info.empty() && public_key_info[0] == "RSA")
+ {
+ // RFC4055: If PublicKeyAlgo = PSS or OAEP: limit the use of the public key exclusively to either RSASSA - PSS or RSAES - OAEP
+ if(public_key_info.size() >= 2)
+ {
+ if(public_key_info[1] == "EMSA4")
+ {
+ /*
+ When the RSA private key owner wishes to limit the use of the public
+ key exclusively to RSASSA-PSS, then the id-RSASSA-PSS object
+ identifier MUST be used in the algorithm field within the subject
+ public key information, and, if present, the parameters field MUST
+ contain RSASSA-PSS-params.
+
+ All parameters in the signature structure algorithm identifier MUST
+ match the parameters in the key structure algorithm identifier
+ except the saltLength field. The saltLength field in the signature parameters
+ MUST be greater or equal to that in the key parameters field.
+
+ ToDo: Allow salt length to be greater
+ */
+ if(public_key_alg_id != obj.signature_algorithm())
+ {
+ throw Decoding_Error("Algorithm identifier mismatch");
+ }
+ }
+ if(public_key_info[1] == "OAEP")
+ {
+ throw Decoding_Error("Decoding subject public keys of type RSAES-OAEP is currently not supported");
+ }
+ }
+ else
+ {
+ // oid = rsaEncryption -> parameters field MUST contain NULL
+ if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.get_oid(), AlgorithmIdentifier::USE_NULL_PARAM))
+ {
+ throw Decoding_Error("Parameters field MUST contain NULL");
+ }
+ }
+ }
+
+ data->m_subject_public_key_bits.assign(public_key.bits(), public_key.bits() + public_key.length());
+
+ data->m_subject_public_key_bits_seq = ASN1::put_in_sequence(data->m_subject_public_key_bits);
+
+ BER_Decoder(data->m_subject_public_key_bits)
+ .decode(data->m_subject_public_key_algid)
+ .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+
+ if(v3_exts_data.is_a(3, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ BER_Decoder(v3_exts_data).decode(data->m_v3_extensions).verify_end();
+ }
+ else if(v3_exts_data.is_set())
+ throw BER_Bad_Tag("Unknown tag in X.509 cert", v3_exts_data.tagging());
+
+ // Now cache some fields from the extensions
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Key_Usage>())
+ {
+ data->m_key_constraints = ext->get_constraints();
+ }
+ else
+ {
+ data->m_key_constraints = NO_CONSTRAINTS;
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Key_ID>())
+ {
+ data->m_subject_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+ {
+ data->m_authority_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Name_Constraints>())
+ {
+ data->m_name_constraints = ext->get_name_constraints();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Basic_Constraints>())
+ {
+ if(ext->get_is_ca() == true)
+ {
+ if(data->m_key_constraints == NO_CONSTRAINTS ||
+ (data->m_key_constraints & KEY_CERT_SIGN))
+ {
+ data->m_is_ca_certificate = true;
+ data->m_path_len_constraint = ext->get_path_limit();
+ }
+ }
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Issuer_Alternative_Name>())
+ {
+ data->m_issuer_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_subject_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
+ {
+ data->m_extended_key_usage = ext->get_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Certificate_Policies>())
+ {
+ data->m_cert_policies = ext->get_policy_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>())
+ {
+ data->m_ocsp_responder = ext->ocsp_responder();
+ data->m_ca_issuers = ext->ca_issuers();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::CRL_Distribution_Points>())
+ {
+ data->m_crl_distribution_points = ext->crl_distribution_urls();
+ }
+
+ // Check for self-signed vs self-issued certificates
+ if(data->m_subject_dn == data->m_issuer_dn)
+ {
+ if(data->m_subject_key_id.empty() == false && data->m_authority_key_id.empty() == false)
+ {
+ data->m_self_signed = (data->m_subject_key_id == data->m_authority_key_id);
+ }
+ else
+ {
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(X509::load_key(data->m_subject_public_key_bits_seq));
+
+ Certificate_Status_Code sig_status = obj.verify_signature(*pub_key);
+
+ if(sig_status == Certificate_Status_Code::OK ||
+ sig_status == Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN)
+ {
+ data->m_self_signed = true;
+ }
+ }
+ catch(...)
+ {
+ // ignore errors here to allow parsing to continue
+ }
+ }
+ }
+
+ std::unique_ptr<HashFunction> sha1(HashFunction::create("SHA-1"));
+ if(sha1)
+ {
+ sha1->update(data->m_subject_public_key_bitstring);
+ data->m_subject_public_key_bitstring_sha1 = sha1->final_stdvec();
+ // otherwise left as empty, and we will throw if subject_public_key_bitstring_sha1 is called
+ }
+
+ data->m_subject_ds.add(data->m_subject_dn.contents());
+ data->m_issuer_ds.add(data->m_issuer_dn.contents());
+ data->m_v3_extensions.contents_to(data->m_subject_ds, data->m_issuer_ds);
+
+ return data;
+ }
+
+}
+
+/*
+* Decode the TBSCertificate data
+*/
+void X509_Certificate::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<X509_Certificate_Data> data = parse_x509_cert_body(*this);
+
+ m_data.reset(data.release());
+ }
+
+const X509_Certificate_Data& X509_Certificate::data() const
+ {
+ if(m_data == nullptr)
+ {
+ throw Invalid_State("X509_Certificate uninitialized");
+ }
+ return *m_data.get();
+ }
+
+uint32_t X509_Certificate::x509_version() const
+ {
+ return data().m_version;
+ }
+
+bool X509_Certificate::is_self_signed() const
+ {
+ return data().m_self_signed;
+ }
+
+const X509_Time& X509_Certificate::not_before() const
+ {
+ return data().m_not_before;
+ }
+
+const X509_Time& X509_Certificate::not_after() const
+ {
+ return data().m_not_after;
+ }
+
+const AlgorithmIdentifier& X509_Certificate::subject_public_key_algo() const
+ {
+ return data().m_subject_public_key_algid;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_issuer_key_id() const
+ {
+ return data().m_v2_issuer_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_subject_key_id() const
+ {
+ return data().m_v2_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bits() const
+ {
+ return data().m_subject_public_key_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_info() const
+ {
+ return data().m_subject_public_key_bits_seq;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring() const
+ {
+ return data().m_subject_public_key_bitstring;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring_sha1() const
+ {
+ if(data().m_subject_public_key_bitstring_sha1.empty())
+ throw Encoding_Error("X509_Certificate::subject_public_key_bitstring_sha1 called but SHA-1 disabled in build");
+
+ return data().m_subject_public_key_bitstring_sha1;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::authority_key_id() const
+ {
+ return data().m_authority_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_key_id() const
+ {
+ return data().m_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::serial_number() const
+ {
+ return data().m_serial;
+ }
+
+bool X509_Certificate::is_serial_negative() const
+ {
+ return data().m_serial_negative;
+ }
+
+
+const X509_DN& X509_Certificate::issuer_dn() const
+ {
+ return data().m_issuer_dn;
+ }
+
+const X509_DN& X509_Certificate::subject_dn() const
+ {
+ return data().m_subject_dn;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_issuer_dn() const
+ {
+ return data().m_issuer_dn_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_subject_dn() const
+ {
+ return data().m_subject_dn_bits;
+ }
+
+bool X509_Certificate::is_CA_cert() const
+ {
+ return data().m_is_ca_certificate;
+ }
+
+uint32_t X509_Certificate::path_limit() const
+ {
+ return data().m_path_len_constraint;
+ }
+
+Key_Constraints X509_Certificate::constraints() const
+ {
+ return data().m_key_constraints;
+ }
+
+const std::vector<OID>& X509_Certificate::extended_key_usage() const
+ {
+ return data().m_extended_key_usage;
+ }
+
+const std::vector<OID>& X509_Certificate::certificate_policy_oids() const
+ {
+ return data().m_cert_policies;
+ }
+
+const NameConstraints& X509_Certificate::name_constraints() const
+ {
+ return data().m_name_constraints;
+ }
+
+const Extensions& X509_Certificate::v3_extensions() const
+ {
+ return data().m_v3_extensions;
+ }
+
+bool X509_Certificate::allowed_usage(Key_Constraints usage) const
+ {
+ if(constraints() == NO_CONSTRAINTS)
+ return true;
+ return ((constraints() & usage) == usage);
+ }
+
+bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
+ {
+ return allowed_extended_usage(OIDS::str2oid(usage));
+ }
+
+bool X509_Certificate::allowed_extended_usage(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ if(ex.empty())
+ return true;
+
+ if(std::find(ex.begin(), ex.end(), usage) != ex.end())
+ return true;
+
+ return false;
+ }
+
+bool X509_Certificate::allowed_usage(Usage_Type usage) const
+ {
+ // These follow suggestions in RFC 5280 4.2.1.12
+
+ switch(usage)
+ {
+ case Usage_Type::UNSPECIFIED:
+ return true;
+
+ case Usage_Type::TLS_SERVER_AUTH:
+ return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+
+ case Usage_Type::TLS_CLIENT_AUTH:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+
+ case Usage_Type::OCSP_RESPONDER:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning");
+
+ case Usage_Type::CERTIFICATE_AUTHORITY:
+ return is_CA_cert();
+ }
+
+ return false;
+ }
+
+bool X509_Certificate::has_constraints(Key_Constraints constraints) const
+ {
+ if(this->constraints() == NO_CONSTRAINTS)
+ {
+ return false;
+ }
+
+ return ((this->constraints() & constraints) != 0);
+ }
+
+bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
+ {
+ return has_ex_constraint(OIDS::str2oid(ex_constraint));
+ }
+
+bool X509_Certificate::has_ex_constraint(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ return (std::find(ex.begin(), ex.end(), usage) != ex.end());
+ }
+
+/*
+* Return if a certificate extension is marked critical
+*/
+bool X509_Certificate::is_critical(const std::string& ex_name) const
+ {
+ return v3_extensions().critical_extension_set(OIDS::str2oid(ex_name));
+ }
+
+std::string X509_Certificate::ocsp_responder() const
+ {
+ return data().m_ocsp_responder;
+ }
+
+std::vector<std::string> X509_Certificate::ca_issuers() const
+ {
+ return data().m_ca_issuers;
+ }
+
+std::string X509_Certificate::crl_distribution_point() const
+ {
+ // just returns the first (arbitrarily)
+ if(data().m_crl_distribution_points.size() > 0)
+ return data().m_crl_distribution_points[0];
+ return "";
+ }
+
+const AlternativeName& X509_Certificate::subject_alt_name() const
+ {
+ return data().m_subject_alt_name;
+ }
+
+const AlternativeName& X509_Certificate::issuer_alt_name() const
+ {
+ return data().m_issuer_alt_name;
+ }
+
+/*
+* Return information about the subject
+*/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& req) const
+ {
+ if(req == "Email")
+ return this->subject_info("RFC822");
+
+ if(subject_dn().has_field(req))
+ return subject_dn().get_attribute(req);
+
+ if(subject_alt_name().has_field(req))
+ return subject_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_subject_key_id())};
+ if(req == "X509v3.SubjectKeyIdentifier")
+ return {hex_encode(this->subject_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_subject_dn())};
+ if(req == "X509.Certificate.start")
+ return {not_before().to_string()};
+ if(req == "X509.Certificate.end")
+ return {not_after().to_string()};
+
+ if(req == "X509.Certificate.version")
+ return {std::to_string(x509_version())};
+ if(req == "X509.Certificate.serial")
+ return {hex_encode(serial_number())};
+
+ return data().m_subject_ds.get(req);
+ }
+
+/*
+* Return information about the issuer
+*/
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& req) const
+ {
+ if(issuer_dn().has_field(req))
+ return issuer_dn().get_attribute(req);
+
+ if(issuer_alt_name().has_field(req))
+ return issuer_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_issuer_key_id())};
+ if(req == "X509v3.AuthorityKeyIdentifier")
+ return {hex_encode(this->authority_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_issuer_dn())};
+
+ return data().m_issuer_ds.get(req);
+ }
+
+/*
+* Return the public key in this certificate
+*/
+std::unique_ptr<Public_Key> X509_Certificate::load_subject_public_key() const
+ {
+ try
+ {
+ return std::unique_ptr<Public_Key>(X509::load_key(subject_public_key_info()));
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("X509_Certificate::load_subject_public_key", e.what());
+ }
+ }
+
+std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-256"));
+ hash->update(raw_issuer_dn());
+ return hash->final_stdvec();
+ }
+
+std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+ hash->update(raw_subject_dn());
+ return hash->final_stdvec();
+ }
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<OID>& oids)
+ {
+ std::vector<std::string> out;
+
+ for(const OID& oid : oids)
+ {
+ out.push_back(OIDS::oid2str(oid));
+ }
+ return out;
+ }
+
+}
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
+ {
+ return lookup_oids(extended_key_usage());
+ }
+
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
+ {
+ return lookup_oids(certificate_policy_oids());
+ }
+
+std::string X509_Certificate::fingerprint(const std::string& hash_name) const
+ {
+ return create_hex_fingerprint(this->BER_encode(), hash_name);
+ }
+
+bool X509_Certificate::matches_dns_name(const std::string& name) const
+ {
+ if(name.empty())
+ return false;
+
+ std::vector<std::string> issued_names = subject_info("DNS");
+
+ // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+ if(issued_names.empty())
+ issued_names = subject_info("Name");
+
+ for(size_t i = 0; i != issued_names.size(); ++i)
+ {
+ if(host_wildcard_match(issued_names[i], name))
+ return true;
+ }
+
+ return false;
+ }
+
+/*
+* Compare two certificates for equality
+*/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+ {
+ return (this->signature() == other.signature() &&
+ this->signature_algorithm() == other.signature_algorithm() &&
+ this->signed_body() == other.signed_body());
+ }
+
+bool X509_Certificate::operator<(const X509_Certificate& other) const
+ {
+ /* If signature values are not equal, sort by lexicographic ordering of that */
+ if(this->signature() != other.signature())
+ {
+ return (this->signature() < other.signature());
+ }
+
+ // Then compare the signed contents
+ return this->signed_body() < other.signed_body();
+ }
+
+/*
+* X.509 Certificate Comparison
+*/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+ {
+ return !(cert1 == cert2);
+ }
+
+std::string X509_Certificate::to_string() const
+ {
+ std::ostringstream out;
+
+ out << "Version: " << this->x509_version() << "\n";
+ out << "Subject: " << subject_dn() << "\n";
+ out << "Issuer: " << issuer_dn() << "\n";
+ out << "Issued: " << this->not_before().readable_string() << "\n";
+ out << "Expires: " << this->not_after().readable_string() << "\n";
+
+ out << "Constraints:\n";
+ Key_Constraints constraints = this->constraints();
+ if(constraints == NO_CONSTRAINTS)
+ out << " None\n";
+ else
+ {
+ if(constraints & DIGITAL_SIGNATURE)
+ out << " Digital Signature\n";
+ if(constraints & NON_REPUDIATION)
+ out << " Non-Repudiation\n";
+ if(constraints & KEY_ENCIPHERMENT)
+ out << " Key Encipherment\n";
+ if(constraints & DATA_ENCIPHERMENT)
+ out << " Data Encipherment\n";
+ if(constraints & KEY_AGREEMENT)
+ out << " Key Agreement\n";
+ if(constraints & KEY_CERT_SIGN)
+ out << " Cert Sign\n";
+ if(constraints & CRL_SIGN)
+ out << " CRL Sign\n";
+ if(constraints & ENCIPHER_ONLY)
+ out << " Encipher Only\n";
+ if(constraints & DECIPHER_ONLY)
+ out << " Decipher Only\n";
+ }
+
+ const std::vector<OID> policies = this->certificate_policy_oids();
+ if(!policies.empty())
+ {
+ out << "Policies: " << "\n";
+ for(auto oid : policies)
+ out << " " << oid.as_string() << "\n";
+ }
+
+ std::vector<OID> ex_constraints = this->extended_key_usage();
+ if(!ex_constraints.empty())
+ {
+ out << "Extended Constraints:\n";
+ for(size_t i = 0; i != ex_constraints.size(); i++)
+ out << " " << OIDS::oid2str(ex_constraints[i]) << "\n";
+ }
+
+ const NameConstraints& name_constraints = this->name_constraints();
+
+ if(!name_constraints.permitted().empty() || !name_constraints.excluded().empty())
+ {
+ out << "Name Constraints:\n";
+
+ if(!name_constraints.permitted().empty())
+ {
+ out << " Permit";
+ for(auto st: name_constraints.permitted())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+
+ if(!name_constraints.excluded().empty())
+ {
+ out << " Exclude";
+ for(auto st: name_constraints.excluded())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+ }
+
+ if(!ocsp_responder().empty())
+ out << "OCSP responder " << ocsp_responder() << "\n";
+
+ std::vector<std::string> ca_issuers = this->ca_issuers();
+ if(!ca_issuers.empty())
+ {
+ out << "CA Issuers:\n";
+ for(size_t i = 0; i != ca_issuers.size(); i++)
+ out << " URI: " << ca_issuers[i] << "\n";
+ }
+
+ if(!crl_distribution_point().empty())
+ out << "CRL " << crl_distribution_point() << "\n";
+
+ out << "Signature algorithm: " <<
+ OIDS::oid2str(this->signature_algorithm().get_oid()) << "\n";
+
+ out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+ if(this->authority_key_id().size())
+ out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+ if(this->subject_key_id().size())
+ out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+ try
+ {
+ std::unique_ptr<Public_Key> pubkey(this->subject_public_key());
+ out << "Public Key [" << pubkey->algo_name() << "-" << pubkey->key_length() << "]\n\n";
+ out << X509::PEM_encode(*pubkey);
+ }
+ catch(Decoding_Error&)
+ {
+ const AlgorithmIdentifier& alg_id = this->subject_public_key_algo();
+ out << "Failed to decode key with oid " << alg_id.get_oid().as_string() << "\n";
+ }
+
+ return out.str();
+ }
+
+}