diff options
Diffstat (limited to '')
-rw-r--r-- | comm/third_party/botan/src/lib/x509/x509cert.cpp | 956 |
1 files changed, 956 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/x509/x509cert.cpp b/comm/third_party/botan/src/lib/x509/x509cert.cpp new file mode 100644 index 0000000000..55f279c58c --- /dev/null +++ b/comm/third_party/botan/src/lib/x509/x509cert.cpp @@ -0,0 +1,956 @@ +/* +* 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/x509_key.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; + + std::vector<uint8_t> m_issuer_dn_bits_sha256; + std::vector<uint8_t> m_subject_dn_bits_sha256; + + std::string m_fingerprint_sha1; + std::string m_fingerprint_sha256; + + 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(); + + const std::vector<std::string> public_key_info = + split_on(OIDS::oid2str_or_empty(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"); + } + } + } + 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("RSA algorithm 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))) + { + // Path validation will reject a v1/v2 cert with v3 extensions + 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(); + /* + RFC 5280: When the keyUsage extension appears in a certificate, + at least one of the bits MUST be set to 1. + */ + if(data->m_key_constraints == NO_CONSTRAINTS) + { + throw Decoding_Error("Certificate has invalid encoding for KeyUsage"); + } + } + 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) + { + /* + * RFC 5280 section 4.2.1.3 requires that CAs include KeyUsage in all + * intermediate CA certificates they issue. Currently we accept it being + * missing, as do most other implementations. But it may be worth + * removing this entirely, or alternately adding a warning level + * validation failure for it. + */ + 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 + { + /* + If a parse error or unknown algorithm is encountered, default + to assuming it is self signed. We have no way of being certain but + that is usually the default case (self-issued is rare in practice). + */ + data->m_self_signed = true; + + 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; + } + else + { + data->m_self_signed = false; + } + } + catch(...) + { + // ignore errors here to allow parsing to continue + } + } + } + + const std::vector<uint8_t> full_encoding = obj.BER_encode(); + + 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_fingerprint_sha1 = create_hex_fingerprint(full_encoding, "SHA-1"); + } + + std::unique_ptr<HashFunction> sha256(HashFunction::create("SHA-256")); + if(sha256) + { + sha256->update(data->m_issuer_dn_bits); + data->m_issuer_dn_bits_sha256 = sha256->final_stdvec(); + + sha256->update(data->m_subject_dn_bits); + data->m_subject_dn_bits_sha256 = sha256->final_stdvec(); + + data->m_fingerprint_sha256 = create_hex_fingerprint(full_encoding, "SHA-256"); + } + + 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 static_cast<uint32_t>(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 + { + if(data().m_version < 3 && data().m_self_signed) + return true; + + return data().m_is_ca_certificate; + } + +uint32_t X509_Certificate::path_limit() const + { + if(data().m_version < 3 && data().m_self_signed) + return 32; // in theory infinite, but this is more than enough + + return static_cast<uint32_t>(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(OID::from_string(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(); + + case Usage_Type::ENCRYPTION: + return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT)); + } + + 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(OID::from_string(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(OID::from_string(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); + } + } + +Public_Key* X509_Certificate::subject_public_key() const + { + return load_subject_public_key().release(); + } + +std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const + { + if(data().m_issuer_dn_bits_sha256.empty()) + throw Encoding_Error("X509_Certificate::raw_issuer_dn_sha256 called but SHA-256 disabled in build"); + return data().m_issuer_dn_bits_sha256; + } + +std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const + { + if(data().m_subject_dn_bits_sha256.empty()) + throw Encoding_Error("X509_Certificate::raw_subject_dn_sha256 called but SHA-256 disabled in build"); + return data().m_subject_dn_bits_sha256; + } + +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(oid.to_formatted_string()); + } + 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 + { + /* + * The SHA-1 and SHA-256 fingerprints are precomputed since these + * are the most commonly used. Especially, SHA-256 fingerprints are + * used for cycle detection during path construction. + * + * If SHA-1 or SHA-256 was missing at parsing time the vectors are + * left empty in which case we fall back to create_hex_fingerprint + * which will throw if the hash is unavailable. + */ + if(hash_name == "SHA-256" && data().m_fingerprint_sha256.size() > 0) + return data().m_fingerprint_sha256; + else if(hash_name == "SHA-1" && data().m_fingerprint_sha1.size() > 0) + return data().m_fingerprint_sha1; + else + 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.to_string() << "\n"; + } + + const std::vector<OID>& ex_constraints = this->extended_key_usage(); + if(!ex_constraints.empty()) + { + out << "Extended Constraints:\n"; + for(auto&& oid : ex_constraints) + { + out << " " << oid.to_formatted_string() << "\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"; + + const 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: " << this->signature_algorithm().get_oid().to_formatted_string() << "\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().to_string() << "\n"; + } + + return out.str(); + } + +} |