/* * 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 #include #include #include #include #include #include #include #include #include #include #include namespace Botan { struct X509_Certificate_Data { std::vector m_serial; AlgorithmIdentifier m_sig_algo_inner; X509_DN m_issuer_dn; X509_DN m_subject_dn; std::vector m_issuer_dn_bits; std::vector m_subject_dn_bits; X509_Time m_not_before; X509_Time m_not_after; std::vector m_subject_public_key_bits; std::vector m_subject_public_key_bits_seq; std::vector m_subject_public_key_bitstring; std::vector m_subject_public_key_bitstring_sha1; AlgorithmIdentifier m_subject_public_key_algid; std::vector m_v2_issuer_key_id; std::vector m_v2_subject_key_id; Extensions m_v3_extensions; std::vector m_extended_key_usage; std::vector m_authority_key_id; std::vector m_subject_key_id; std::vector m_cert_policies; std::vector m_crl_distribution_points; std::string m_ocsp_responder; std::vector 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 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& 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 parse_x509_cert_body(const X509_Object& obj) { std::unique_ptr 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 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()) { 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()) { data->m_subject_key_id = ext->get_key_id(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { data->m_authority_key_id = ext->get_key_id(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { data->m_name_constraints = ext->get_name_constraints(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { 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()) { data->m_issuer_alt_name = ext->get_alt_name(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { data->m_subject_alt_name = ext->get_alt_name(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { data->m_extended_key_usage = ext->get_oids(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { data->m_cert_policies = ext->get_policy_oids(); } if(auto ext = data->m_v3_extensions.get_extension_object_as()) { 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()) { 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 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 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 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& X509_Certificate::v2_issuer_key_id() const { return data().m_v2_issuer_key_id; } const std::vector& X509_Certificate::v2_subject_key_id() const { return data().m_v2_subject_key_id; } const std::vector& X509_Certificate::subject_public_key_bits() const { return data().m_subject_public_key_bits; } const std::vector& X509_Certificate::subject_public_key_info() const { return data().m_subject_public_key_bits_seq; } const std::vector& X509_Certificate::subject_public_key_bitstring() const { return data().m_subject_public_key_bitstring; } const std::vector& 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& X509_Certificate::authority_key_id() const { return data().m_authority_key_id; } const std::vector& X509_Certificate::subject_key_id() const { return data().m_subject_key_id; } const std::vector& 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& X509_Certificate::raw_issuer_dn() const { return data().m_issuer_dn_bits; } const std::vector& 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& X509_Certificate::extended_key_usage() const { return data().m_extended_key_usage; } const std::vector& 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& 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& 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 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 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 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 X509_Certificate::load_subject_public_key() const { try { return std::unique_ptr(X509::load_key(subject_public_key_info())); } catch(std::exception& e) { throw Decoding_Error("X509_Certificate::load_subject_public_key", e.what()); } } std::vector X509_Certificate::raw_issuer_dn_sha256() const { std::unique_ptr hash(HashFunction::create_or_throw("SHA-256")); hash->update(raw_issuer_dn()); return hash->final_stdvec(); } std::vector X509_Certificate::raw_subject_dn_sha256() const { std::unique_ptr hash(HashFunction::create("SHA-256")); hash->update(raw_subject_dn()); return hash->final_stdvec(); } namespace { /* * Lookup each OID in the vector */ std::vector lookup_oids(const std::vector& oids) { std::vector out; for(const OID& oid : oids) { out.push_back(OIDS::oid2str(oid)); } return out; } } /* * Return the list of extended key usage OIDs */ std::vector X509_Certificate::ex_constraints() const { return lookup_oids(extended_key_usage()); } /* * Return the list of certificate policies */ std::vector 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 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 policies = this->certificate_policy_oids(); if(!policies.empty()) { out << "Policies: " << "\n"; for(auto oid : policies) out << " " << oid.as_string() << "\n"; } std::vector 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 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 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(); } }