aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/x509/datastor.cpp41
-rw-r--r--src/lib/x509/datastor.h28
-rw-r--r--src/lib/x509/x509cert.cpp685
-rw-r--r--src/lib/x509/x509cert.h177
4 files changed, 567 insertions, 364 deletions
diff --git a/src/lib/x509/datastor.cpp b/src/lib/x509/datastor.cpp
index ae6b1e45c..2cdd3458c 100644
--- a/src/lib/x509/datastor.cpp
+++ b/src/lib/x509/datastor.cpp
@@ -161,4 +161,45 @@ void Data_Store::add(const std::multimap<std::string, std::string>& in)
}
}
+/*
+* Create and populate a X509_DN
+*/
+X509_DN create_dn(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key.find("X520.") != std::string::npos);
+ });
+
+ X509_DN dn;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ dn.add_attribute(i->first, i->second);
+
+ return dn;
+ }
+
+/*
+* Create and populate an AlternativeName
+*/
+AlternativeName create_alt_name(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key == "RFC822" ||
+ key == "DNS" ||
+ key == "URI" ||
+ key == "IP");
+ });
+
+ AlternativeName alt_name;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ alt_name.add_attribute(i->first, i->second);
+
+ return alt_name;
+ }
+
}
diff --git a/src/lib/x509/datastor.h b/src/lib/x509/datastor.h
index c730c7140..556a78984 100644
--- a/src/lib/x509/datastor.h
+++ b/src/lib/x509/datastor.h
@@ -8,7 +8,8 @@
#ifndef BOTAN_DATA_STORE_H_
#define BOTAN_DATA_STORE_H_
-#include <botan/secmem.h>
+#include <botan/x509_dn.h>
+#include <botan/asn1_alt_name.h>
#include <functional>
#include <string>
#include <vector>
@@ -23,7 +24,7 @@ namespace Botan {
* reasons. There is no reason for applications to use this type directly.
* It will be removed in a future major release.
*/
-class BOTAN_PUBLIC_API(2,0) Data_Store
+class BOTAN_UNSTABLE_API Data_Store
{
public:
/**
@@ -55,6 +56,29 @@ class BOTAN_PUBLIC_API(2,0) Data_Store
std::multimap<std::string, std::string> m_contents;
};
+/*
+* Data Store Extraction Operations
+*/
+
+/*
+* Create and populate a X509_DN
+* @param info data store containing DN information
+* @return DN containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) X509_DN
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_dn(const Data_Store& info);
+
+/*
+* Create and populate an AlternativeName
+* @param info data store containing AlternativeName information
+* @return AlternativeName containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) AlternativeName
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_alt_name(const Data_Store& info);
+
+
}
#endif
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 5a6588ecc..808d4b2b2 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -1,12 +1,13 @@
/*
* X.509 Certificates
-* (C) 1999-2010,2015 Jack Lloyd
+* (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>
@@ -20,29 +21,59 @@
namespace Botan {
-namespace {
+struct X509_Certificate_Data
+ {
+ size_t m_version = 0;
+ 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;
+ AlgorithmIdentifier m_subject_public_key_algid;
+ std::vector<uint8_t> m_subject_public_key_bitstring;
+ std::vector<uint8_t> m_subject_public_key_bitstring_sha1;
+
+ std::vector<uint8_t> m_v2_issuer_key_id;
+ std::vector<uint8_t> m_v2_subject_key_id;
+ Extensions m_v3_extensions;
+
+ Key_Constraints m_key_constraints;
+ 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<std::string> m_crl_distribution_points;
+ std::string m_ocsp_responder;
+
+ size_t m_path_len_constraint = 0;
+ bool m_self_signed = false;
+ bool m_is_ca_certificate = false;
+
+ AlternativeName m_subject_alt_name;
+ AlternativeName m_issuer_alt_name;
+
+ Data_Store m_subject_ds;
+ Data_Store m_issuer_ds;
+ };
/*
-* Lookup each OID in the vector
+* X509_Certificate Constructor
*/
-std::vector<std::string> lookup_oids(const std::vector<std::string>& in)
+X509_Certificate::X509_Certificate(DataSource& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
{
- std::vector<std::string> out;
-
- for(auto i = in.begin(); i != in.end(); ++i)
- out.push_back(OIDS::lookup(OID(*i)));
- return out;
+ do_decode();
}
-}
-
/*
* X509_Certificate Constructor
*/
-X509_Certificate::X509_Certificate(DataSource& in) :
- X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
- m_self_signed(false),
- m_v3_extensions(false)
+X509_Certificate::X509_Certificate(const std::vector<uint8_t>& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
{
do_decode();
}
@@ -52,71 +83,55 @@ X509_Certificate::X509_Certificate(DataSource& in) :
* X509_Certificate Constructor
*/
X509_Certificate::X509_Certificate(const std::string& fsname) :
- X509_Object(fsname, "CERTIFICATE/X509 CERTIFICATE"),
- m_self_signed(false),
- m_v3_extensions(false)
+ X509_Object(fsname, "CERTIFICATE/X509 CERTIFICATE")
{
do_decode();
}
#endif
-/*
-* X509_Certificate Constructor
-*/
-X509_Certificate::X509_Certificate(const std::vector<uint8_t>& in) :
- X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
- m_self_signed(false),
- m_v3_extensions(false)
- {
- do_decode();
- }
+namespace {
-/*
-* Decode the TBSCertificate data
-*/
-void X509_Certificate::force_decode()
+std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& obj)
{
- size_t version;
- BigInt serial_bn;
- AlgorithmIdentifier sig_algo_inner;
- X509_DN dn_issuer, dn_subject;
- X509_Time start, end;
+ std::unique_ptr<X509_Certificate_Data> data(new X509_Certificate_Data);
- BER_Decoder tbs_cert(signed_body());
+ BER_Decoder tbs_cert(obj.signed_body());
+ BigInt serial_bn;
- tbs_cert.decode_optional(version, ASN1_Tag(0),
+ tbs_cert.decode_optional(data->m_version, ASN1_Tag(0),
ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
.decode(serial_bn)
- .decode(sig_algo_inner)
- .decode(dn_issuer)
+ .decode(data->m_sig_algo_inner)
+ .decode(data->m_issuer_dn)
.start_cons(SEQUENCE)
- .decode(start)
- .decode(end)
+ .decode(data->m_not_before)
+ .decode(data->m_not_after)
.end_cons()
- .decode(dn_subject);
-
- if(version > 2)
- throw Decoding_Error("Unknown X.509 cert version " + std::to_string(version));
- if(signature_algorithm() != sig_algo_inner)
- throw Decoding_Error("Algorithm identifier mismatch");
+ .decode(data->m_subject_dn);
+ 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");
- m_subject.add(dn_subject.contents());
- m_issuer.add(dn_issuer.contents());
+ // for general sanity convert wire version (0 based) to standards version (v1 .. v3)
+ data->m_version += 1;
- m_subject.add("X509.Certificate.dn_bits", ASN1::put_in_sequence(dn_subject.get_bits()));
- m_issuer.add("X509.Certificate.dn_bits", ASN1::put_in_sequence(dn_issuer.get_bits()));
+ 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());
BER_Object public_key = tbs_cert.get_next_object();
if(public_key.type_tag != SEQUENCE || public_key.class_tag != CONSTRUCTED)
throw BER_Bad_Tag("X509_Certificate: Unexpected tag for public key",
public_key.type_tag, public_key.class_tag);
+ // validate_public_key_params(public_key.value);
AlgorithmIdentifier public_key_alg_id;
BER_Decoder(public_key.value).decode(public_key_alg_id).discard_remaining();
std::vector<std::string> public_key_info =
- split_on(OIDS::lookup(public_key_alg_id.oid), '/');
+ split_on(OIDS::oid2str(public_key_alg_id.oid), '/');
if(!public_key_info.empty() && public_key_info[0] == "RSA")
{
@@ -139,7 +154,7 @@ void X509_Certificate::force_decode()
ToDo: Allow salt length to be greater
*/
- if(public_key_alg_id != signature_algorithm())
+ if(public_key_alg_id != obj.signature_algorithm())
{
throw Decoding_Error("Algorithm identifier mismatch");
}
@@ -159,146 +174,260 @@ void X509_Certificate::force_decode()
}
}
- std::vector<uint8_t> v2_issuer_key_id, v2_subject_key_id;
+ data->m_subject_public_key_bits = unlock(public_key.value);
- tbs_cert.decode_optional_string(v2_issuer_key_id, BIT_STRING, 1);
- tbs_cert.decode_optional_string(v2_subject_key_id, BIT_STRING, 2);
+ BER_Decoder(data->m_subject_public_key_bits)
+ .decode(data->m_subject_public_key_algid)
+ .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+
+ tbs_cert.decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1);
+ tbs_cert.decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2);
BER_Object v3_exts_data = tbs_cert.get_next_object();
if(v3_exts_data.type_tag == 3 &&
v3_exts_data.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
{
- BER_Decoder(v3_exts_data.value).decode(m_v3_extensions).verify_end();
- m_v3_extensions.contents_to(m_subject, m_issuer);
+ BER_Decoder(v3_exts_data.value).decode(data->m_v3_extensions).verify_end();
}
else if(v3_exts_data.type_tag != NO_OBJECT)
throw BER_Bad_Tag("Unknown tag in X.509 cert",
v3_exts_data.type_tag, v3_exts_data.class_tag);
if(tbs_cert.more_items())
- throw Decoding_Error("TBSCertificate has more items that expected");
+ throw Decoding_Error("TBSCertificate has extra data after extensions block");
+
+ // 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;
+ }
- m_subject.add("X509.Certificate.version", static_cast<uint32_t>(version));
- m_subject.add("X509.Certificate.serial", BigInt::encode(serial_bn));
- m_subject.add("X509.Certificate.start", start.to_string());
- m_subject.add("X509.Certificate.end", end.to_string());
+ 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();
+ }
- m_issuer.add("X509.Certificate.v2.key_id", v2_issuer_key_id);
- m_subject.add("X509.Certificate.v2.key_id", v2_subject_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();
+ }
- m_subject.add("X509.Certificate.public_key", hex_encode(public_key.value));
+ 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();
+ }
- m_self_signed = false;
- if(dn_subject == dn_issuer)
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
{
- std::unique_ptr<Public_Key> pub_key(subject_public_key());
- m_self_signed = check_signature(*pub_key);
+ data->m_subject_alt_name = ext->get_alt_name();
}
- if(m_self_signed && version == 0)
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
{
- m_subject.add("X509v3.BasicConstraints.is_ca", 1);
- m_subject.add("X509v3.BasicConstraints.path_constraint", Cert_Extension::NO_CERT_PATH_LIMIT);
+ data->m_extended_key_usage = ext->get_oids();
}
- if(is_CA_cert() &&
- !m_subject.has_value("X509v3.BasicConstraints.path_constraint"))
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>())
{
- const size_t limit = (x509_version() < 3) ?
- Cert_Extension::NO_CERT_PATH_LIMIT : 0;
+ data->m_ocsp_responder = ext->ocsp_responder();
+ }
- m_subject.add("X509v3.BasicConstraints.path_constraint", static_cast<uint32_t>(limit));
+ 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)
+ {
+ std::unique_ptr<Public_Key> pub_key(
+ X509::load_key(ASN1::put_in_sequence(data->m_subject_public_key_bits)));
+ data->m_self_signed = obj.check_signature(*pub_key);
+ }
+
+ 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;
}
+}
+
/*
-* Return the X.509 version in use
+* 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)
+ {
+ abort();
+ throw Decoding_Error("Failed to parse X509 certificate");
+ }
+ return *m_data.get();
+ }
+
uint32_t X509_Certificate::x509_version() const
{
- return (m_subject.get1_uint32("X509.Certificate.version") + 1);
+ return data().m_version;
}
-/*
-* Return the time this cert becomes valid
-*/
-std::string X509_Certificate::start_time() const
+bool X509_Certificate::is_self_signed() const
{
- return m_subject.get1("X509.Certificate.start");
+ return data().m_self_signed;
}
-/*
-* Return the time this cert becomes invalid
-*/
-std::string X509_Certificate::end_time() const
+const X509_Time& X509_Certificate::not_before() const
{
- return m_subject.get1("X509.Certificate.end");
+ return data().m_not_before;
}
-/*
-* Return information about the subject
-*/
-std::vector<std::string>
-X509_Certificate::subject_info(const std::string& what) const
+const X509_Time& X509_Certificate::not_after() const
{
- return m_subject.get(X509_DN::deref_info_field(what));
+ return data().m_not_after;
}
-/*
-* Return information about the issuer
-*/
-std::vector<std::string>
-X509_Certificate::issuer_info(const std::string& what) const
+const std::vector<uint8_t>& X509_Certificate::v2_issuer_key_id() const
{
- return m_issuer.get(X509_DN::deref_info_field(what));
+ return data().m_v2_issuer_key_id;
}
-/*
-* Return the public key in this certificate
-*/
-Public_Key* X509_Certificate::subject_public_key() const
+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 X509::load_key(
- ASN1::put_in_sequence(this->subject_public_key_bits()));
+ return data().m_subject_public_key_bits;
}
-std::vector<uint8_t> X509_Certificate::subject_public_key_bits() const
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring() const
{
- return hex_decode(m_subject.get1("X509.Certificate.public_key"));
+ return data().m_subject_public_key_bitstring;
}
-std::vector<uint8_t> X509_Certificate::subject_public_key_bitstring() const
+std::vector<uint8_t> X509_Certificate::subject_public_key_bitstring_sha1() const
{
- // TODO: cache this
- const std::vector<uint8_t> key_bits = subject_public_key_bits();
+ 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;
+ }
- AlgorithmIdentifier public_key_algid;
- std::vector<uint8_t> public_key_bitstr;
+const std::vector<uint8_t>& X509_Certificate::authority_key_id() const
+ {
+ return data().m_authority_key_id;
+ }
- BER_Decoder(key_bits)
- .decode(public_key_algid)
- .decode(public_key_bitstr, BIT_STRING);
+const std::vector<uint8_t>& X509_Certificate::subject_key_id() const
+ {
+ return data().m_subject_key_id;
+ }
- return public_key_bitstr;
+const std::vector<uint8_t>& X509_Certificate::serial_number() const
+ {
+ return data().m_serial;
}
-std::vector<uint8_t> X509_Certificate::subject_public_key_bitstring_sha1() const
+const X509_DN& X509_Certificate::issuer_dn() const
{
- // TODO: cache this value
- std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-1"));
- hash->update(this->subject_public_key_bitstring());
- return hash->final_stdvec();
+ 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;
+ }
+
+std::vector<OID> X509_Certificate::certificate_policy_oids() const
+ {
+ if(auto ext = v3_extensions().get_extension_object_as<Cert_Extension::Certificate_Policies>())
+ {
+ return ext->get_policy_oids();
+ }
+ return std::vector<OID>();
}
/*
-* Check if the certificate is for a CA
+* Return the name constraints
*/
-bool X509_Certificate::is_CA_cert() const
+NameConstraints X509_Certificate::name_constraints() const
{
- if(!m_subject.get1_uint32("X509v3.BasicConstraints.is_ca"))
- return false;
+ if(auto ext = v3_extensions().get_extension_object_as<Cert_Extension::Name_Constraints>())
+ {
+ return ext->get_name_constraints();
+ }
+ return NameConstraints(); // no constraints
+ }
- return allowed_usage(Key_Constraints(KEY_CERT_SIGN));
+const Extensions& X509_Certificate::v3_extensions() const
+ {
+ return data().m_v3_extensions;
}
bool X509_Certificate::allowed_usage(Key_Constraints usage) const
@@ -310,8 +439,12 @@ bool X509_Certificate::allowed_usage(Key_Constraints usage) const
bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
{
- const std::vector<std::string> ex = ex_constraints();
+ 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;
@@ -358,27 +491,13 @@ bool X509_Certificate::has_constraints(Key_Constraints constraints) const
bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
{
- const std::vector<std::string> ex = ex_constraints();
-
- if(ex.empty())
- {
- return false;
- }
-
- if(std::find(ex.begin(), ex.end(), ex_constraint) != ex.end())
- {
- return true;
- }
-
- return false;
+ return has_ex_constraint(OIDS::str2oid(ex_constraint));
}
-/*
-* Return the path length constraint
-*/
-uint32_t X509_Certificate::path_limit() const
+bool X509_Certificate::has_ex_constraint(const OID& usage) const
{
- return m_subject.get1_uint32("X509v3.BasicConstraints.path_constraint", Cert_Extension::NO_CERT_PATH_LIMIT);
+ const std::vector<OID>& ex = extended_key_usage();
+ return (std::find(ex.begin(), ex.end(), usage) != ex.end());
}
/*
@@ -386,132 +505,155 @@ uint32_t X509_Certificate::path_limit() const
*/
bool X509_Certificate::is_critical(const std::string& ex_name) const
{
- return !!m_subject.get1_uint32(ex_name + ".is_critical",0);
+ return v3_extensions().critical_extension_set(OIDS::str2oid(ex_name));
}
-/*
-* Return the key usage constraints
-*/
-Key_Constraints X509_Certificate::constraints() const
+std::string X509_Certificate::ocsp_responder() const
{
- return Key_Constraints(m_subject.get1_uint32("X509v3.KeyUsage",
- NO_CONSTRAINTS));
+ return data().m_ocsp_responder;
}
-/*
-* Return the list of extended key usage OIDs
-*/
-std::vector<std::string> X509_Certificate::ex_constraints() const
+std::string X509_Certificate::crl_distribution_point() const
{
- return lookup_oids(m_subject.get("X509v3.ExtendedKeyUsage"));
+ // just returns the first (arbitrarily)
+ if(data().m_crl_distribution_points.size() > 0)
+ return data().m_crl_distribution_points[0];
+ return "";
}
-/*
-* Return the name constraints
-*/
-NameConstraints X509_Certificate::name_constraints() const
+const AlternativeName& X509_Certificate::subject_alt_name() const
{
- std::vector<GeneralSubtree> permit, exclude;
-
- for(const std::string& v: m_subject.get("X509v3.NameConstraints.permitted"))
- {
- permit.push_back(GeneralSubtree(v));
- }
-
- for(const std::string& v: m_subject.get("X509v3.NameConstraints.excluded"))
- {
- exclude.push_back(GeneralSubtree(v));
- }
+ return data().m_subject_alt_name;
+ }
- return NameConstraints(std::move(permit),std::move(exclude));
+const AlternativeName& X509_Certificate::issuer_alt_name() const
+ {
+ return data().m_issuer_alt_name;
}
/*
-* Return the list of certificate policies
+* Return information about the subject
*/
-std::vector<std::string> X509_Certificate::policies() const
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& req) const
{
- return lookup_oids(m_subject.get("X509v3.CertificatePolicies"));
- }
+ if(subject_dn().has_field(req))
+ return subject_dn().get_attribute(req);
-Extensions X509_Certificate::v3_extensions() const
- {
- return m_v3_extensions;
- }
+ if(subject_alt_name().has_field(req))
+ return subject_alt_name().get_attribute(req);
-std::string X509_Certificate::ocsp_responder() const
- {
- return m_subject.get1("OCSP.responder", "");
- }
+ // 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()};
-std::string X509_Certificate::crl_distribution_point() const
- {
- return m_subject.get1("CRL.DistributionPoint", "");
- }
+ if(req == "X509.Certificate.version")
+ return {std::to_string(x509_version())};
+ if(req == "X509.Certificate.serial")
+ return {hex_encode(serial_number())};
-/*
-* Return the authority key id
-*/
-std::vector<uint8_t> X509_Certificate::authority_key_id() const
- {
- return m_issuer.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ return data().m_subject_ds.get(req);
}
/*
-* Return the subject key id
+* Return information about the issuer
*/
-std::vector<uint8_t> X509_Certificate::subject_key_id() const
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& req) const
{
- return m_subject.get1_memvec("X509v3.SubjectKeyIdentifier");
+ 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 certificate serial number
+* Return the public key in this certificate
*/
-std::vector<uint8_t> X509_Certificate::serial_number() const
- {
- return m_subject.get1_memvec("X509.Certificate.serial");
- }
-
-X509_DN X509_Certificate::issuer_dn() const
+std::unique_ptr<Public_Key> X509_Certificate::load_subject_public_key() const
{
- return create_dn(m_issuer);
+ try
+ {
+ return std::unique_ptr<Public_Key>(X509::load_key(ASN1::put_in_sequence(this->subject_public_key_bits())));
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("X509_Certificate::load_subject_public_key", e.what());
+ }
}
-std::vector<uint8_t> X509_Certificate::raw_issuer_dn() const
+std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const
{
- return m_issuer.get1_memvec("X509.Certificate.dn_bits");
+ 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_issuer_dn_sha256() const
+std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
{
std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
- hash->update(raw_issuer_dn());
+ hash->update(raw_subject_dn());
return hash->final_stdvec();
}
-X509_DN X509_Certificate::subject_dn() const
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<OID>& oids)
{
- return create_dn(m_subject);
+ std::vector<std::string> out;
+
+ for(const OID& oid : oids)
+ {
+ out.push_back(OIDS::oid2str(oid));
+ }
+ return out;
}
-std::vector<uint8_t> X509_Certificate::raw_subject_dn() const
+}
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
{
- return m_subject.get1_memvec("X509.Certificate.dn_bits");
+ return lookup_oids(extended_key_usage());
}
-std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
{
- std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
- hash->update(raw_subject_dn());
- return hash->final_stdvec();
+ return lookup_oids(certificate_policy_oids());
}
std::string X509_Certificate::fingerprint(const std::string& hash_name) const
{
- std::unique_ptr<HashFunction> hash(HashFunction::create(hash_name));
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
hash->update(this->BER_encode());
- const auto hex_print = hex_encode(hash->final());
+ const std::string hex_print = hex_encode(hash->final());
std::string formatted_print;
@@ -579,42 +721,13 @@ bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
std::string X509_Certificate::to_string() const
{
- const std::vector<std::string> dn_fields{
- "Name",
- "Email",
- "Organization",
- "Organizational Unit",
- "Locality",
- "State",
- "Country",
- "IP",
- "DNS",
- "URI",
- "PKIX.XMPPAddr"
- };
-
std::ostringstream out;
- for(auto&& field : dn_fields)
- {
- for(auto&& val : subject_info(field))
- {
- out << "Subject " << field << ": " << val << "\n";
- }
- }
-
- for(auto&& field : dn_fields)
- {
- for(auto&& val : issuer_info(field))
- {
- out << "Issuer " << field << ": " << val << "\n";
- }
- }
-
out << "Version: " << this->x509_version() << "\n";
-
- out << "Not valid before: " << this->start_time() << "\n";
- out << "Not valid after: " << this->end_time() << "\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();
@@ -642,20 +755,20 @@ std::string X509_Certificate::to_string() const
out << " Decipher Only\n";
}
- std::vector<std::string> policies = this->policies();
+ const std::vector<OID> policies = this->certificate_policy_oids();
if(!policies.empty())
{
out << "Policies: " << "\n";
- for(size_t i = 0; i != policies.size(); i++)
- out << " " << policies[i] << "\n";
+ for(auto oid : policies)
+ out << " " << oid.as_string() << "\n";
}
- std::vector<std::string> ex_constraints = this->ex_constraints();
+ 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 << " " << ex_constraints[i] << "\n";
+ out << " " << OIDS::oid2str(ex_constraints[i]) << "\n";
}
NameConstraints name_constraints = this->name_constraints();
@@ -691,7 +804,7 @@ std::string X509_Certificate::to_string() const
out << "CRL " << crl_distribution_point() << "\n";
out << "Signature algorithm: " <<
- OIDS::lookup(this->signature_algorithm().oid) << "\n";
+ OIDS::oid2str(this->signature_algorithm().oid) << "\n";
out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
@@ -702,50 +815,10 @@ std::string X509_Certificate::to_string() const
out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
std::unique_ptr<Public_Key> pubkey(this->subject_public_key());
- out << "Public Key:\n" << X509::PEM_encode(*pubkey);
+ out << "Public Key [" << pubkey->algo_name() << "-" << pubkey->key_length() << "]\n\n";
+ out << X509::PEM_encode(*pubkey);
return out.str();
}
-/*
-* Create and populate a X509_DN
-*/
-X509_DN create_dn(const Data_Store& info)
- {
- auto names = info.search_for(
- [](const std::string& key, const std::string&)
- {
- return (key.find("X520.") != std::string::npos);
- });
-
- X509_DN dn;
-
- for(auto i = names.begin(); i != names.end(); ++i)
- dn.add_attribute(i->first, i->second);
-
- return dn;
- }
-
-/*
-* Create and populate an AlternativeName
-*/
-AlternativeName create_alt_name(const Data_Store& info)
- {
- auto names = info.search_for(
- [](const std::string& key, const std::string&)
- {
- return (key == "RFC822" ||
- key == "DNS" ||
- key == "URI" ||
- key == "IP");
- });
-
- AlternativeName alt_name;
-
- for(auto i = names.begin(); i != names.end(); ++i)
- alt_name.add_attribute(i->first, i->second);
-
- return alt_name;
- }
-
}
diff --git a/src/lib/x509/x509cert.h b/src/lib/x509/x509cert.h
index 14db2c133..c9cf8bb7b 100644
--- a/src/lib/x509/x509cert.h
+++ b/src/lib/x509/x509cert.h
@@ -1,6 +1,6 @@
/*
* X.509 Certificates
-* (C) 1999-2007,2015 Jack Lloyd
+* (C) 1999-2007,2015,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -9,17 +9,19 @@
#define BOTAN_X509_CERTS_H_
#include <botan/x509_obj.h>
-#include <botan/x509_dn.h>
#include <botan/x509_key.h>
-#include <botan/x509_ext.h>
-#include <botan/asn1_alt_name.h>
-#include <botan/datastor.h>
+#include <botan/asn1_time.h>
#include <botan/key_constraint.h>
#include <botan/name_constraint.h>
#include <memory>
namespace Botan {
+class Public_Key;
+class X509_DN;
+class AlternativeName;
+class Extensions;
+
enum class Usage_Type
{
UNSPECIFIED, // no restrictions
@@ -29,33 +31,53 @@ enum class Usage_Type
OCSP_RESPONDER
};
+struct X509_Certificate_Data;
+
/**
-* This class represents X.509 Certificate
+* This class represents an X.509 Certificate
*/
class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
{
public:
/**
- * Get the public key associated with this certificate.
+ * Return a newly allocated copy of the public key associated
+ * with the subject of this certificate. This object is owned
+ * by the caller.
+ *
+ * @return public key
+ */
+ Public_Key* subject_public_key() const
+ {
+ return load_subject_public_key().release();
+ }
+
+ /**
+ * Create a public key object associated with the public key bits in this
+ * certificate. If the public key bits was valid for X.509 encoding
+ * purposes but invalid algorithmically (for example, RSA with an even
+ * modulus) that will be detected at this point, and an exception will be
+ * thrown.
+ *
* @return subject public key of this certificate
*/
- Public_Key* subject_public_key() const;
+ std::unique_ptr<Public_Key> load_subject_public_key() const;
/**
- * Get the public key associated with this certificate.
+ * Get the public key associated with this certificate. This includes the
+ * outer AlgorithmIdentifier
* @return subject public key of this certificate
*/
- std::vector<uint8_t> subject_public_key_bits() const;
+ const std::vector<uint8_t>& subject_public_key_bits() const;
/**
* Get the bit string of the public key associated with this certificate
- * @return subject public key of this certificate
+ * @return public key bits
*/
- std::vector<uint8_t> subject_public_key_bitstring() const;
+ const std::vector<uint8_t>& subject_public_key_bitstring() const;
/**
* Get the SHA-1 bit string of the public key associated with this certificate.
- * This is used for OCSP among other protocols
+ * This is used for OCSP among other protocols.
* @return hash of subject public key of this certificate
*/
std::vector<uint8_t> subject_public_key_bitstring_sha1() const;
@@ -64,13 +86,13 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
* Get the certificate's issuer distinguished name (DN).
* @return issuer DN of this certificate
*/
- X509_DN issuer_dn() const;
+ const X509_DN& issuer_dn() const;
/**
* Get the certificate's subject distinguished name (DN).
* @return subject DN of this certificate
*/
- X509_DN subject_dn() const;
+ const X509_DN& subject_dn() const;
/**
* Get a value for a specific subject_info parameter name.
@@ -97,9 +119,9 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
std::vector<std::string> issuer_info(const std::string& name) const;
/**
- * Raw issuer DN
+ * Raw issuer DN bits
*/
- std::vector<uint8_t> raw_issuer_dn() const;
+ const std::vector<uint8_t>& raw_issuer_dn() const;
/**
* SHA-256 of Raw issuer DN
@@ -109,7 +131,7 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
/**
* Raw subject DN
*/
- std::vector<uint8_t> raw_subject_dn() const;
+ const std::vector<uint8_t>& raw_subject_dn() const;
/**
* SHA-256 of Raw subject DN
@@ -117,16 +139,34 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
std::vector<uint8_t> raw_subject_dn_sha256() const;
/**
- * Get the notBefore of the certificate.
+ * Get the notBefore of the certificate as a string
* @return notBefore of the certificate
*/
- std::string start_time() const;
+ std::string BOTAN_DEPRECATED("Use not_before().to_string()") start_time() const
+ {
+ return not_before().to_string();
+ }
/**
- * Get the notAfter of the certificate.
+ * Get the notAfter of the certificate as a string
* @return notAfter of the certificate
*/
- std::string end_time() const;
+ std::string BOTAN_DEPRECATED("Use not_after().to_string()") end_time() const
+ {
+ return not_after().to_string();
+ }
+
+ /**
+ * Get the notBefore of the certificate as X509_Time
+ * @return notBefore of the certificate
+ */
+ const X509_Time& not_before() const;
+
+ /**
+ * Get the notAfter of the certificate as X509_Time
+ * @return notAfter of the certificate
+ */
+ const X509_Time& not_after() const;
/**
* Get the X509 version of this certificate object.
@@ -138,25 +178,26 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
* Get the serial number of this certificate.
* @return certificates serial number
*/
- std::vector<uint8_t> serial_number() const;
+ const std::vector<uint8_t>& serial_number() const;
/**
* Get the DER encoded AuthorityKeyIdentifier of this certificate.
* @return DER encoded AuthorityKeyIdentifier
*/
- std::vector<uint8_t> authority_key_id() const;
+ const std::vector<uint8_t>& authority_key_id() const;
/**
* Get the DER encoded SubjectKeyIdentifier of this certificate.
* @return DER encoded SubjectKeyIdentifier
*/
- std::vector<uint8_t> subject_key_id() const;
+ const std::vector<uint8_t>& subject_key_id() const;
/**
* Check whether this certificate is self signed.
+ * If the DN issuer and subject agree,
* @return true if this certificate is self signed
*/
- bool is_self_signed() const { return m_self_signed; }
+ bool is_self_signed() const;
/**
* Check whether this certificate is a CA certificate.
@@ -181,21 +222,39 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
bool allowed_extended_usage(const std::string& usage) const;
/**
+ * Returns true if the specified usage is set in the extended key usage extension,
+ * or if no extended key usage constraints are set at all.
+ * To check if a certain extended key constraint is set in the certificate
+ * use @see X509_Certificate#has_ex_constraint.
+ */
+ bool allowed_extended_usage(const OID& usage) const;
+
+ /**
* Returns true if the required key and extended key constraints are set in the certificate
* for the specified @param usage or if no key constraints are set in both the key usage
* and extended key usage extension.
*/
bool allowed_usage(Usage_Type usage) const;
- /// Returns true if the specified @param constraints are included in the key usage extension.
+ /**
+ * Returns true if the specified @param constraints are included in the key
+ * usage extension.
+ */
bool has_constraints(Key_Constraints constraints) const;
/**
- * Returns true if and only if @param ex_constraint (referring to an extended key
- * constraint, eg "PKIX.ServerAuth") is included in the extended
- * key extension.
+ * Returns true if and only if @param ex_constraint (referring to an
+ * extended key constraint, eg "PKIX.ServerAuth") is included in the
+ * extended key extension.
+ */
+ bool BOTAN_DEPRECATED("Use version taking an OID")
+ has_ex_constraint(const std::string& ex_constraint) const;
+
+ /**
+ * Returns true if and only if OID @param ex_constraint is
+ * included in the extended key extension.
*/
- bool has_ex_constraint(const std::string& ex_constraint) const;
+ bool has_ex_constraint(const OID& ex_constraint) const;
/**
* Get the path limit as defined in the BasicConstraints extension of
@@ -222,7 +281,15 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
* extension of this certificate.
* @return key constraints
*/
- std::vector<std::string> ex_constraints() const;
+ std::vector<std::string>
+ BOTAN_DEPRECATED("Use extended_key_usage") ex_constraints() const;
+
+ /**
+ * Get the key usage as defined in the ExtendedKeyUsage extension
+ * of this certificate, or else an empty vector.
+ * @return key usage
+ */
+ const std::vector<OID>& extended_key_usage() const;
/**
* Get the name constraints as defined in the NameConstraints
@@ -238,11 +305,28 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
*/
std::vector<std::string> policies() const;
+ std::vector<OID> certificate_policy_oids() const;
+
/**
* Get all extensions of this certificate.
* @return certificate extensions
*/
- Extensions v3_extensions() const;
+ const Extensions& v3_extensions() const;
+
+ /**
+ * Return the v2 issuer key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_issuer_key_id() const;
+
+ /**
+ * Return the v2 subject key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_subject_key_id() const;
+
+ const AlternativeName& subject_alt_name() const;
+ const AlternativeName& issuer_alt_name() const;
/**
* Return the listed address of an OCSP responder, or empty if not set
@@ -255,7 +339,7 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
std::string crl_distribution_point() const;
/**
- * @return a string describing the certificate
+ * @return a free-form string describing the certificate
*/
std::string to_string() const;
@@ -313,13 +397,12 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
private:
void force_decode() override;
friend class X509_CA;
- friend class BER_Decoder;
X509_Certificate() = default;
- Data_Store m_subject, m_issuer;
- bool m_self_signed;
- Extensions m_v3_extensions;
+ const X509_Certificate_Data& data() const;
+
+ std::shared_ptr<X509_Certificate_Data> m_data;
};
/**
@@ -331,24 +414,6 @@ class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
*/
BOTAN_PUBLIC_API(2,0) bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2);
-/*
-* Data Store Extraction Operations
-*/
-
-/*
-* Create and populate a X509_DN
-* @param info data store containing DN information
-* @return DN containing attributes from data store
-*/
-BOTAN_PUBLIC_API(2,0) X509_DN create_dn(const Data_Store& info);
-
-/*
-* Create and populate an AlternativeName
-* @param info data store containing AlternativeName information
-* @return AlternativeName containing attributes from data store
-*/
-BOTAN_PUBLIC_API(2,0) AlternativeName create_alt_name(const Data_Store& info);
-
}
#endif