aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-11-15 15:55:40 -0500
committerJack Lloyd <[email protected]>2017-11-15 15:55:40 -0500
commit24a5d0688748abe132b602995a993e5d0bc7d455 (patch)
treeb5ebaa023fecd6900f9fe12b57ad946063c6fd29
parent0bc3991616917745cbd78df2bf03f5c7b9c5e8ca (diff)
parent3b4a2c547a948b421d73ae7e1bc0ad9430cce465 (diff)
Merge GH #884 Refactor X.509 cert/CRL internals
-rw-r--r--doc/manual/x509.rst141
-rw-r--r--src/cli/asn1.cpp8
-rw-r--r--src/lib/asn1/asn1_alt_name.h47
-rw-r--r--src/lib/asn1/asn1_str.cpp23
-rw-r--r--src/lib/asn1/asn1_str.h6
-rw-r--r--src/lib/ffi/ffi_cert.cpp6
-rw-r--r--src/lib/x509/asn1_alt_name.cpp (renamed from src/lib/asn1/asn1_alt_name.cpp)70
-rw-r--r--src/lib/x509/asn1_alt_name.h60
-rw-r--r--src/lib/x509/crl_ent.cpp94
-rw-r--r--src/lib/x509/crl_ent.h28
-rw-r--r--src/lib/x509/datastor.cpp41
-rw-r--r--src/lib/x509/datastor.h28
-rw-r--r--src/lib/x509/info.txt1
-rw-r--r--src/lib/x509/name_constraint.cpp13
-rw-r--r--src/lib/x509/ocsp.h1
-rw-r--r--src/lib/x509/pkcs10.cpp142
-rw-r--r--src/lib/x509/pkcs10.h17
-rw-r--r--src/lib/x509/x509_ca.cpp6
-rw-r--r--src/lib/x509/x509_ca.h7
-rw-r--r--src/lib/x509/x509_crl.cpp133
-rw-r--r--src/lib/x509/x509_crl.h38
-rw-r--r--src/lib/x509/x509_dn.cpp (renamed from src/lib/asn1/x509_dn.cpp)34
-rw-r--r--src/lib/x509/x509_dn.h (renamed from src/lib/asn1/x509_dn.h)22
-rw-r--r--src/lib/x509/x509_ext.cpp383
-rw-r--r--src/lib/x509/x509_ext.h332
-rw-r--r--src/lib/x509/x509_obj.cpp6
-rw-r--r--src/lib/x509/x509cert.cpp684
-rw-r--r--src/lib/x509/x509cert.h177
-rw-r--r--src/lib/x509/x509path.cpp21
-rw-r--r--src/tests/data/misc_certs/opcuactt_ca.derbin0 -> 1072 bytes
-rw-r--r--src/tests/data/misc_certs/opcuactt_ca.pem28
-rw-r--r--src/tests/test_pkcs11_high_level.cpp1
-rw-r--r--src/tests/test_x509_dn.cpp4
-rw-r--r--src/tests/test_x509_path.cpp74
-rw-r--r--src/tests/unit_x509.cpp94
35 files changed, 1719 insertions, 1051 deletions
diff --git a/doc/manual/x509.rst b/doc/manual/x509.rst
index d82dbccf2..daf154c9a 100644
--- a/doc/manual/x509.rst
+++ b/doc/manual/x509.rst
@@ -15,43 +15,96 @@ in the :doc:`tls` protocol. A X.509 certificate is represented by
.. cpp:class:: X509_Certificate
- .. cpp:function:: Public_Key* subject_public_key() const
+ .. cpp:function:: X509_Certificate(const std::string& filename)
- Returns the public key of the subject
+ Load a certificate from a file. PEM or DER is accepted.
- .. cpp:function:: X509_DN issuer_dn() const
+ .. cpp:function:: X509_Certificate(const std::vector<uint8_t>& in)
- Returns the distinguished name (DN) of the certificate's issuer
+ Load a certificate from a byte string.
+
+ .. cpp:function:: X509_Certificate(DataSource& source)
+
+ Load a certificate from an abstract ``DataSource``.
+
+ .. cpp:function:: std::unique_ptr<Public_Key> load_subject_public_key() const
+
+ Deserialize the stored public key and return a new object. This
+ might throw, if it happens that the public key object stored in
+ the certificate is malformed in some way, or in the case that the
+ public key algorithm used is not supported by the library.
+
+ See :ref:`serializing_public_keys` for more information about what to do
+ with the returned object. It may be any type of key, in principle, though
+ RSA and ECDSA are most common.
+
+ .. cpp:function:: std::vector<uint8_t> serial_number() const
+
+ Return the certificates serial number. The tuple of issuer DN and
+ serial number should be unique.
.. cpp:function:: X509_DN subject_dn() const
- Returns the distinguished name (DN) of the certificate's subject
+ Returns the distinguished name (DN) of the certificate's subject.
- .. cpp:function:: std::string start_time() const
+ .. cpp:function:: X509_DN issuer_dn() const
+
+ Returns the distinguished name (DN) of the certificate's issuer
+
+ .. cpp:function:: X509_Time not_before() const
Returns the point in time the certificate becomes valid
- .. cpp:function:: std::string end_time() const
+ .. cpp:function:: X509_Time not_after() const
Returns the point in time the certificate expires
- .. cpp:function:: Extensions v3_extensions() const
+ .. cpp:function:: const Extensions& v3_extensions() const
+
+ Returns all extensions of this certificate. You can use this
+ to examine any extension data associated with the certificate,
+ including custom extensions the library doesn't know about.
+
+ .. cpp:function:: const AlternativeName& subject_alt_name() const
+
+ Return the subjects alternative name. This is used to store
+ values like associated URIs, DNS addresses, and email addresses.
+
+ .. cpp:function:: const AlternativeName& issuer_alt_name() const
+
+ Return alternative names for the issuer.
- Returns all extensions of this certificate
+ .. cpp:function:: std::string fingerprint(const std::string& hash_name = "SHA-1") const
-When working with certificates, the main class to remember is
-``X509_Certificate``. You can read an object of this type, but you
-can't create one on the fly; a CA object is necessary for making a new
-certificate. So for the most part, you only have to worry about
-reading them in, verifying the signatures, and getting the bits of
-data in them (most commonly the public key, and the information about
-the user of that key). An X.509v3 certificate can contain a literally
-infinite number of items related to all kinds of things. Botan doesn't
-support a lot of them, because nobody uses them and they're an
-impossible mess to work with. This section only documents the most
-commonly used ones of the ones that are supported; for the rest, read
-``x509cert.h`` and ``asn1_obj.h`` (which has the definitions of
-various common ASN.1 constructs used in X.509).
+ Return a fingerprint for the certificate.
+
+ .. cpp:function:: Key_Constraints constraints() const
+
+ Returns either an enumeration listing key constraints (what the
+ associated key can be used for) or ``NO_CONSTRAINTS`` if the
+ relevent extension was not included. Example values are
+ ``DIGITAL_SIGNATURE`` and ``KEY_CERT_SIGN``. More than one value
+ might be specified.
+
+ .. cpp:function:: bool allowed_extended_usage(const OID& usage) const
+
+ Returns true if the OID is included in the extended usage extension.
+
+ .. cpp:function:: bool matches_dns_name(const std::string& name) const
+
+ Check if the certificate's subject alternative name DNS fields
+ match ``name``. This function also handles wildcard certificates.
+
+ .. cpp:function:: std::string to_string() const
+
+ Returns a free-form human readable string describing the certificate.
+
+The ``X509_Certificate`` class has several other functions not described here.
+See the header ``x509cert.h`` for details.
+
+The data of an X.509 certificate is stored as a ``shared_ptr`` to a structure
+containing the decoded information. So copying ``X509_Certificate`` objects is
+quite cheap.
So what's in an X.509 certificate?
-----------------------------------
@@ -140,8 +193,7 @@ functions are provided to search them.
.. note::
- Validation of custom extensions during path validation
- is currently not supported.
+ Validation of custom extensions during path validation is currently not supported.
.. cpp:class:: Extensions
@@ -178,7 +230,7 @@ functions are provided to search them.
together with the corresponding criticality flag.
Contains all extensions, known as well as unknown extensions.
-Revocation Lists
+Certificate Revocation Lists
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It will occasionally happen that a certificate must be revoked before
@@ -209,22 +261,7 @@ with
.. cpp:function:: void Certificate_Store::add_crl(const X509_CRL& crl)
-and all future verifications will take into account the certificates
-listed.
-
-Reading Certificates
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-``X509_Certificate`` has two constructors, each of which takes a source of
-data; a filename to read, and a ``DataSource&``::
-
- X509_Certificate cert1("cert1.pem");
-
- /* This file contains two certificates, concatenated */
- DataSource_Stream in("certs2_and_3.pem");
-
- X509_Certificate cert2(in); // read the first cert
- X509_Certificate cert3(in); // read the second cert
+and all future verifications will take into account the provided CRL.
Certificate Stores
----------------------------------------
@@ -302,9 +339,9 @@ later.
SQL-backed Certificate Stores
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-The SQL-backed certificate stores store all objects in an SQL
-database. They also additionally provide private key storage
-and revocation of individual certificates.
+The SQL-backed certificate stores store all objects in an SQL database. They
+also additionally provide private key storage and revocation of individual
+certificates.
.. cpp:class:: Certificate_Store_In_SQL
@@ -324,7 +361,6 @@ and revocation of individual certificates.
Removes ``cert`` from the store. Returns `false` if the certificate could not
be found and `true` if removal was successful.
-
.. cpp:function:: std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const
Returns the private key for "cert" or an empty shared_ptr if none was found
@@ -357,8 +393,10 @@ and revocation of individual certificates.
Generates CRLs for all certificates marked as revoked.
A CRL is returned for each unique issuer DN.
-Botan currently only provides one SQL-backed certificate store using
-sqlite.
+The ``Certificate_Store_In_SQL`` class operates on an abstract ``SQL_Database``
+object. If support for sqlite3 was enabled at build time, Botan includes an
+implementation of this interface for sqlite3, and a subclass of
+``Certificate_Store_In_SQL`` which creates or opens a sqlite3 database.
.. cpp:class:: Certificate_Store_In_SQLite
@@ -475,7 +513,7 @@ step. The two constructors are:
and, if `minimum_key_strength` is less than or equal to 80, then
SHA-1 signatures will also be accepted.
-Certificate Authorities
+Creating New Certificates
---------------------------------
A CA is represented by the type ``X509_CA``, which can be found in
@@ -523,10 +561,9 @@ be issued. To generate a new, empty CRL, just call
.. cpp:function:: X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng, \
uint32_t next_update = 0)
- This function will return a new, empty CRL. The
- ``next_update`` parameter is the number of seconds before
- the CRL expires. If it is set to the (default) value of zero, then a
- reasonable default (currently 7 days) will be used.
+ This function will return a new, empty CRL. The ``next_update`` parameter is
+ the number of seconds before the CRL expires. If it is set to the (default)
+ value of zero, then a reasonable default (currently 7 days) will be used.
On the other hand, you may have issued a CRL before. In that case, you will
want to issue a new CRL that contains all previously revoked
diff --git a/src/cli/asn1.cpp b/src/cli/asn1.cpp
index 234cbd6e6..08c2562ba 100644
--- a/src/cli/asn1.cpp
+++ b/src/cli/asn1.cpp
@@ -336,13 +336,7 @@ void decode(std::ostream& output,
emit(output, type_name(type_tag), level, length, bit_str);
}
- else if(type_tag == Botan::PRINTABLE_STRING ||
- type_tag == Botan::NUMERIC_STRING ||
- type_tag == Botan::IA5_STRING ||
- type_tag == Botan::T61_STRING ||
- type_tag == Botan::VISIBLE_STRING ||
- type_tag == Botan::UTF8_STRING ||
- type_tag == Botan::BMP_STRING)
+ else if(Botan::ASN1_String::is_string_type(type_tag))
{
Botan::ASN1_String str;
data.decode(str);
diff --git a/src/lib/asn1/asn1_alt_name.h b/src/lib/asn1/asn1_alt_name.h
deleted file mode 100644
index 9a9b759d7..000000000
--- a/src/lib/asn1/asn1_alt_name.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-* Common ASN.1 Objects
-* (C) 1999-2007 Jack Lloyd
-* 2007 Yves Jerschow
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_ASN1_ALT_NAME_H_
-#define BOTAN_ASN1_ALT_NAME_H_
-
-#include <botan/asn1_obj.h>
-#include <botan/asn1_str.h>
-#include <botan/asn1_oid.h>
-#include <map>
-
-namespace Botan {
-
-/**
-* Alternative Name
-*/
-class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object
- {
- public:
- void encode_into(class DER_Encoder&) const override;
- void decode_from(class BER_Decoder&) override;
-
- std::multimap<std::string, std::string> contents() const;
-
- void add_attribute(const std::string&, const std::string&);
- std::multimap<std::string, std::string> get_attributes() const;
-
- void add_othername(const OID&, const std::string&, ASN1_Tag);
- std::multimap<OID, ASN1_String> get_othernames() const;
-
- bool has_items() const;
-
- AlternativeName(const std::string& = "", const std::string& = "",
- const std::string& = "", const std::string& = "");
- private:
- std::multimap<std::string, std::string> m_alt_info;
- std::multimap<OID, ASN1_String> m_othernames;
- };
-
-}
-
-#endif
diff --git a/src/lib/asn1/asn1_str.cpp b/src/lib/asn1/asn1_str.cpp
index d90aa215b..8b9524de2 100644
--- a/src/lib/asn1/asn1_str.cpp
+++ b/src/lib/asn1/asn1_str.cpp
@@ -55,14 +55,7 @@ ASN1_Tag choose_encoding(const std::string& str)
void assert_is_string_type(ASN1_Tag tag)
{
- if(tag != NUMERIC_STRING &&
- tag != PRINTABLE_STRING &&
- tag != VISIBLE_STRING &&
- tag != T61_STRING &&
- tag != IA5_STRING &&
- tag != UTF8_STRING &&
- tag != BMP_STRING &&
- tag != UNIVERSAL_STRING)
+ if(!ASN1_String::is_string_type(tag))
{
throw Invalid_Argument("ASN1_String: Unknown string type " +
std::to_string(tag));
@@ -71,6 +64,20 @@ void assert_is_string_type(ASN1_Tag tag)
}
+//static
+bool ASN1_String::is_string_type(ASN1_Tag tag)
+ {
+ return (tag == NUMERIC_STRING ||
+ tag == PRINTABLE_STRING ||
+ tag == VISIBLE_STRING ||
+ tag == T61_STRING ||
+ tag == IA5_STRING ||
+ tag == UTF8_STRING ||
+ tag == BMP_STRING ||
+ tag == UNIVERSAL_STRING);
+ }
+
+
/*
* Create an ASN1_String
*/
diff --git a/src/lib/asn1/asn1_str.h b/src/lib/asn1/asn1_str.h
index f19265494..77e2fc145 100644
--- a/src/lib/asn1/asn1_str.h
+++ b/src/lib/asn1/asn1_str.h
@@ -29,6 +29,12 @@ class BOTAN_PUBLIC_API(2,0) ASN1_String final : public ASN1_Object
std::string BOTAN_DEPRECATED("Use value() to get UTF-8 string instead")
iso_8859() const;
+ /**
+ * Return true iff this is a tag for a known string type we can handle.
+ * This ignores string types that are not supported, eg teletexString
+ */
+ static bool is_string_type(ASN1_Tag tag);
+
explicit ASN1_String(const std::string& utf8 = "");
ASN1_String(const std::string& utf8, ASN1_Tag tag);
private:
diff --git a/src/lib/ffi/ffi_cert.cpp b/src/lib/ffi/ffi_cert.cpp
index 2ac9c69af..17b78cc7c 100644
--- a/src/lib/ffi/ffi_cert.cpp
+++ b/src/lib/ffi/ffi_cert.cpp
@@ -55,7 +55,7 @@ int botan_x509_cert_get_public_key(botan_x509_cert_t cert, botan_pubkey_t* key)
*key = nullptr;
#if defined(BOTAN_HAS_RSA)
- std::unique_ptr<Botan::Public_Key> publicKey(safe_get(cert).subject_public_key());
+ std::unique_ptr<Botan::Public_Key> publicKey = safe_get(cert).load_subject_public_key();
*key = new botan_pubkey_struct(publicKey.release());
return BOTAN_FFI_SUCCESS;
#else
@@ -100,12 +100,12 @@ int botan_x509_cert_destroy(botan_x509_cert_t cert)
int botan_x509_cert_get_time_starts(botan_x509_cert_t cert, char out[], size_t* out_len)
{
- return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.start_time()); });
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_before().to_string()); });
}
int botan_x509_cert_get_time_expires(botan_x509_cert_t cert, char out[], size_t* out_len)
{
- return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.end_time()); });
+ return BOTAN_FFI_DO(Botan::X509_Certificate, cert, c, { return write_str_output(out, out_len, c.not_after().to_string()); });
}
int botan_x509_cert_get_serial_number(botan_x509_cert_t cert, uint8_t out[], size_t* out_len)
diff --git a/src/lib/asn1/asn1_alt_name.cpp b/src/lib/x509/asn1_alt_name.cpp
index 940312886..6b3eda917 100644
--- a/src/lib/asn1/asn1_alt_name.cpp
+++ b/src/lib/x509/asn1_alt_name.cpp
@@ -16,24 +16,6 @@
namespace Botan {
-namespace {
-
-/*
-* Check if type is a known ASN.1 string type
-*/
-bool is_string_type(ASN1_Tag tag)
- {
- return (tag == NUMERIC_STRING ||
- tag == PRINTABLE_STRING ||
- tag == VISIBLE_STRING ||
- tag == T61_STRING ||
- tag == IA5_STRING ||
- tag == UTF8_STRING ||
- tag == BMP_STRING);
- }
-
-}
-
/*
* Create an AlternativeName
*/
@@ -52,17 +34,17 @@ AlternativeName::AlternativeName(const std::string& email_addr,
* Add an attribute to an alternative name
*/
void AlternativeName::add_attribute(const std::string& type,
- const std::string& str)
+ const std::string& value)
{
- if(type.empty() || str.empty())
+ if(type.empty() || value.empty())
return;
auto range = m_alt_info.equal_range(type);
for(auto j = range.first; j != range.second; ++j)
- if(j->second == str)
+ if(j->second == value)
return;
- multimap_insert(m_alt_info, type, str);
+ multimap_insert(m_alt_info, type, value);
}
/*
@@ -77,22 +59,6 @@ void AlternativeName::add_othername(const OID& oid, const std::string& value,
}
/*
-* Get the attributes of this alternative name
-*/
-std::multimap<std::string, std::string> AlternativeName::get_attributes() const
- {
- return m_alt_info;
- }
-
-/*
-* Get the otherNames
-*/
-std::multimap<OID, ASN1_String> AlternativeName::get_othernames() const
- {
- return m_othernames;
- }
-
-/*
* Return all of the alternative names
*/
std::multimap<std::string, std::string> AlternativeName::contents() const
@@ -108,6 +74,30 @@ std::multimap<std::string, std::string> AlternativeName::contents() const
return names;
}
+bool AlternativeName::has_field(const std::string& attr) const
+ {
+ auto range = m_alt_info.equal_range(attr);
+ return (range.first != range.second);
+ }
+
+std::string AlternativeName::get_first_attribute(const std::string& attr) const
+ {
+ auto i = m_alt_info.lower_bound(attr);
+ if(i != m_alt_info.end() && i->first == attr)
+ return i->second;
+
+ return "";
+ }
+
+std::vector<std::string> AlternativeName::get_attribute(const std::string& attr) const
+ {
+ std::vector<std::string> results;
+ auto range = m_alt_info.equal_range(attr);
+ for(auto i = range.first; i != range.second; ++i)
+ results.push_back(i->second);
+ return results;
+ }
+
/*
* Return if this object has anything useful
*/
@@ -211,8 +201,10 @@ void AlternativeName::decode_from(BER_Decoder& source)
const ASN1_Tag value_type = value.type_tag;
- if(is_string_type(value_type) && value.class_tag == UNIVERSAL)
+ if(ASN1_String::is_string_type(value_type) && value.class_tag == UNIVERSAL)
+ {
add_othername(oid, ASN1::to_string(value), value_type);
+ }
}
}
else if(tag == 1 || tag == 2 || tag == 6)
diff --git a/src/lib/x509/asn1_alt_name.h b/src/lib/x509/asn1_alt_name.h
new file mode 100644
index 000000000..83ac215ba
--- /dev/null
+++ b/src/lib/x509/asn1_alt_name.h
@@ -0,0 +1,60 @@
+/*
+* (C) 1999-2007 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_ALT_NAME_H_
+#define BOTAN_X509_ALT_NAME_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_str.h>
+#include <botan/asn1_oid.h>
+#include <map>
+
+namespace Botan {
+
+/**
+* Alternative Name
+*/
+class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ std::multimap<std::string, std::string> contents() const;
+
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+
+ std::string get_first_attribute(const std::string& attr) const;
+
+ void add_attribute(const std::string& type, const std::string& value);
+ void add_othername(const OID& oid, const std::string& value, ASN1_Tag type);
+
+ const std::multimap<std::string, std::string>& get_attributes() const
+ {
+ return m_alt_info;
+ }
+
+ const std::multimap<OID, ASN1_String>& get_othernames() const
+ {
+ return m_othernames;
+ }
+
+ bool has_items() const;
+
+ AlternativeName(const std::string& email_addr = "",
+ const std::string& uri = "",
+ const std::string& dns = "",
+ const std::string& ip_address = "");
+ private:
+ std::multimap<std::string, std::string> m_alt_info;
+ std::multimap<OID, ASN1_String> m_othernames;
+ };
+
+}
+
+#endif
diff --git a/src/lib/x509/crl_ent.cpp b/src/lib/x509/crl_ent.cpp
index fabd88326..61fd5d31f 100644
--- a/src/lib/x509/crl_ent.cpp
+++ b/src/lib/x509/crl_ent.cpp
@@ -14,24 +14,28 @@
namespace Botan {
-/*
-* Create a CRL_Entry
-*/
-CRL_Entry::CRL_Entry(bool t_on_unknown_crit) :
- m_throw_on_unknown_critical(t_on_unknown_crit)
+struct CRL_Entry_Data
{
- m_reason = UNSPECIFIED;
- }
+ std::vector<uint8_t> m_serial;
+ X509_Time m_time;
+ CRL_Code m_reason;
+ Extensions m_extensions;
+ };
/*
* Create a CRL_Entry
*/
-CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) :
- m_throw_on_unknown_critical(false)
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why)
{
- m_serial = cert.serial_number();
- m_time = X509_Time(std::chrono::system_clock::now());
- m_reason = why;
+ m_data.reset(new CRL_Entry_Data);
+ m_data->m_serial = cert.serial_number();
+ m_data->m_time = X509_Time(std::chrono::system_clock::now());
+ m_data->m_reason = why;
+
+ if(why != UNSPECIFIED)
+ {
+ m_data->m_extensions.add(new Cert_Extension::CRL_ReasonCode(why));
+ }
}
/*
@@ -61,17 +65,13 @@ bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
*/
void CRL_Entry::encode_into(DER_Encoder& der) const
{
- Extensions extensions;
-
- extensions.add(new Cert_Extension::CRL_ReasonCode(m_reason));
-
der.start_cons(SEQUENCE)
- .encode(BigInt::decode(m_serial))
- .encode(m_time)
- .start_cons(SEQUENCE)
- .encode(extensions)
- .end_cons()
- .end_cons();
+ .encode(BigInt::decode(serial_number()))
+ .encode(expire_time())
+ .start_cons(SEQUENCE)
+ .encode(extensions())
+ .end_cons()
+ .end_cons();
}
/*
@@ -80,24 +80,58 @@ void CRL_Entry::encode_into(DER_Encoder& der) const
void CRL_Entry::decode_from(BER_Decoder& source)
{
BigInt serial_number_bn;
- m_reason = UNSPECIFIED;
+
+ std::unique_ptr<CRL_Entry_Data> data(new CRL_Entry_Data);
BER_Decoder entry = source.start_cons(SEQUENCE);
- entry.decode(serial_number_bn).decode(m_time);
+ entry.decode(serial_number_bn).decode(data->m_time);
+ data->m_serial = BigInt::encode(serial_number_bn);
if(entry.more_items())
{
- Extensions extensions(m_throw_on_unknown_critical);
- entry.decode(extensions);
- Data_Store info;
- extensions.contents_to(info, info);
- m_reason = CRL_Code(info.get1_uint32("X509v3.CRLReasonCode"));
+ entry.decode(data->m_extensions);
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_ReasonCode>())
+ {
+ data->m_reason = ext->get_reason();
+ }
+ else
+ {
+ data->m_reason = UNSPECIFIED;
+ }
}
entry.end_cons();
- m_serial = BigInt::encode(serial_number_bn);
+ m_data.reset(data.release());
+ }
+
+const CRL_Entry_Data& CRL_Entry::data() const
+ {
+ if(!m_data)
+ throw Decoding_Error("Uninitialized CRL_Entry");
+ return *m_data.get();
+ }
+
+const std::vector<uint8_t>& CRL_Entry::serial_number() const
+ {
+ return data().m_serial;
}
+const X509_Time& CRL_Entry::expire_time() const
+ {
+ return data().m_time;
+ }
+
+CRL_Code CRL_Entry::reason_code() const
+ {
+ return data().m_reason;
+ }
+
+const Extensions& CRL_Entry::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+
}
diff --git a/src/lib/x509/crl_ent.h b/src/lib/x509/crl_ent.h
index cf509d3c1..967dc92d2 100644
--- a/src/lib/x509/crl_ent.h
+++ b/src/lib/x509/crl_ent.h
@@ -12,7 +12,9 @@
namespace Botan {
+class Extensions;
class X509_Certificate;
+struct CRL_Entry_Data;
/**
* X.509v2 CRL Reason Code.
@@ -47,26 +49,29 @@ class BOTAN_PUBLIC_API(2,0) CRL_Entry final : public ASN1_Object
* Get the serial number of the certificate associated with this entry.
* @return certificate's serial number
*/
- std::vector<uint8_t> serial_number() const { return m_serial; }
+ const std::vector<uint8_t>& serial_number() const;
/**
* Get the revocation date of the certificate associated with this entry
* @return certificate's revocation date
*/
- X509_Time expire_time() const { return m_time; }
+ const X509_Time& expire_time() const;
/**
* Get the entries reason code
* @return reason code
*/
- CRL_Code reason_code() const { return m_reason; }
+ CRL_Code reason_code() const;
/**
- * Construct an empty CRL entry.
- * @param throw_on_unknown_critical_extension should we throw an exception
- * if an unknown CRL extension marked as critical is encountered
+ * Get the extensions on this CRL entry
*/
- explicit CRL_Entry(bool throw_on_unknown_critical_extension = false);
+ const Extensions& extensions() const;
+
+ /**
+ * Create uninitialized CRL_Entry object
+ */
+ CRL_Entry() {}
/**
* Construct an CRL entry.
@@ -77,10 +82,11 @@ class BOTAN_PUBLIC_API(2,0) CRL_Entry final : public ASN1_Object
CRL_Code reason = UNSPECIFIED);
private:
- bool m_throw_on_unknown_critical;
- std::vector<uint8_t> m_serial;
- X509_Time m_time;
- CRL_Code m_reason;
+ friend class X509_CRL;
+
+ const CRL_Entry_Data& data() const;
+
+ std::shared_ptr<CRL_Entry_Data> m_data;
};
/**
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/info.txt b/src/lib/x509/info.txt
index 8221dc0f1..6b136cbcf 100644
--- a/src/lib/x509/info.txt
+++ b/src/lib/x509/info.txt
@@ -7,4 +7,5 @@ OCSP -> 20161118
asn1
pubkey
sha1
+sha2_32
</requires>
diff --git a/src/lib/x509/name_constraint.cpp b/src/lib/x509/name_constraint.cpp
index e098bcd8d..21145824b 100644
--- a/src/lib/x509/name_constraint.cpp
+++ b/src/lib/x509/name_constraint.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/name_constraint.h>
+#include <botan/asn1_alt_name.h>
#include <botan/ber_dec.h>
#include <botan/loadstor.h>
#include <botan/x509_dn.h>
@@ -105,14 +106,18 @@ GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) cons
std::vector<std::string> nam;
std::function<bool(const GeneralName*, const std::string&)> match_fn;
+ const X509_DN& dn = cert.subject_dn();
+ const AlternativeName& alt_name = cert.subject_alt_name();
+
if(type() == "DNS")
{
match_fn = std::mem_fn(&GeneralName::matches_dns);
- nam = cert.subject_info("DNS");
+
+ nam = alt_name.get_attribute("DNS");
if(nam.empty())
{
- nam = cert.subject_info("CN");
+ nam = dn.get_attribute("CN");
}
}
else if(type() == "DN")
@@ -120,13 +125,13 @@ GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) cons
match_fn = std::mem_fn(&GeneralName::matches_dn);
std::stringstream ss;
- ss << cert.subject_dn();
+ ss << dn;
nam.push_back(ss.str());
}
else if(type() == "IP")
{
match_fn = std::mem_fn(&GeneralName::matches_ip);
- nam = cert.subject_info("IP");
+ nam = alt_name.get_attribute("IP");
}
else
{
diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h
index 254de5038..33177dc59 100644
--- a/src/lib/x509/ocsp.h
+++ b/src/lib/x509/ocsp.h
@@ -10,6 +10,7 @@
#include <botan/cert_status.h>
#include <botan/ocsp_types.h>
+#include <botan/x509_dn.h>
namespace Botan {
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp
index 911d6958c..82ef0945d 100644
--- a/src/lib/x509/pkcs10.cpp
+++ b/src/lib/x509/pkcs10.cpp
@@ -1,6 +1,6 @@
/*
* PKCS #10
-* (C) 1999-2007 Jack Lloyd
+* (C) 1999-2007,2017 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -14,6 +14,15 @@
namespace Botan {
+struct PKCS10_Data
+ {
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_public_key_bits;
+ AlternativeName m_alt_name;
+ std::string m_challenge;
+ Extensions m_extensions;
+ };
+
/*
* PKCS10_Request Constructor
*/
@@ -46,9 +55,13 @@ PKCS10_Request::PKCS10_Request(const std::vector<uint8_t>& in) :
/*
* Decode the CertificateRequestInfo
*/
-void PKCS10_Request::force_decode()
+namespace {
+
+std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
{
- BER_Decoder cert_req_info(signed_body());
+ std::unique_ptr<PKCS10_Data> data(new PKCS10_Data);
+
+ BER_Decoder cert_req_info(body);
size_t version;
cert_req_info.decode(version);
@@ -56,25 +69,19 @@ void PKCS10_Request::force_decode()
throw Decoding_Error("Unknown version code in PKCS #10 request: " +
std::to_string(version));
- X509_DN dn_subject;
- cert_req_info.decode(dn_subject);
-
- m_info.add(dn_subject.contents());
+ cert_req_info.decode(data->m_subject_dn);
BER_Object public_key = cert_req_info.get_next_object();
if(public_key.type_tag != SEQUENCE || public_key.class_tag != CONSTRUCTED)
throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for public key",
public_key.type_tag, public_key.class_tag);
- m_info.add("X509.Certificate.public_key",
- PEM_Code::encode(
- ASN1::put_in_sequence(unlock(public_key.value)),
- "PUBLIC KEY"
- )
- );
+ data->m_public_key_bits = ASN1::put_in_sequence(unlock(public_key.value));
BER_Object attr_bits = cert_req_info.get_next_object();
+ std::set<std::string> pkcs9_email;
+
if(attr_bits.type_tag == 0 &&
attr_bits.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
{
@@ -83,7 +90,24 @@ void PKCS10_Request::force_decode()
{
Attribute attr;
attributes.decode(attr);
- handle_attribute(attr);
+ BER_Decoder value(attr.parameters);
+
+ if(attr.oid == OIDS::lookup("PKCS9.EmailAddress"))
+ {
+ ASN1_String email;
+ value.decode(email);
+ pkcs9_email.insert(email.value());
+ }
+ else if(attr.oid == OIDS::lookup("PKCS9.ChallengePassword"))
+ {
+ ASN1_String challenge_password;
+ value.decode(challenge_password);
+ data->m_challenge = challenge_password.value();
+ }
+ else if(attr.oid == OIDS::lookup("PKCS9.ExtensionRequest"))
+ {
+ value.decode(data->m_extensions).verify_end();
+ }
}
attributes.verify_end();
}
@@ -93,33 +117,38 @@ void PKCS10_Request::force_decode()
cert_req_info.verify_end();
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_alt_name = ext->get_alt_name();
+ }
+
+ for(std::string email : pkcs9_email)
+ {
+ data->m_alt_name.add_attribute("RFC882", email);
+ }
+
+ return data;
+ }
+
+}
+
+void PKCS10_Request::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<PKCS10_Data> data = decode_pkcs10(signed_body());
+
+ m_data.reset(data.release());
+
if(!this->check_signature(subject_public_key()))
throw Decoding_Error("PKCS #10 request: Bad signature detected");
}
-/*
-* Handle attributes in a PKCS #10 request
-*/
-void PKCS10_Request::handle_attribute(const Attribute& attr)
+const PKCS10_Data& PKCS10_Request::data() const
{
- BER_Decoder value(attr.parameters);
-
- if(attr.oid == OIDS::lookup("PKCS9.EmailAddress"))
- {
- ASN1_String email;
- value.decode(email);
- m_info.add("RFC822", email.value());
- }
- else if(attr.oid == OIDS::lookup("PKCS9.ChallengePassword"))
- {
- ASN1_String challenge_password;
- value.decode(challenge_password);
- m_info.add("PKCS9.ChallengePassword", challenge_password.value());
- }
- else if(attr.oid == OIDS::lookup("PKCS9.ExtensionRequest"))
- {
- value.decode(m_extensions).verify_end();
- }
+ if(m_data == nullptr)
+ throw Decoding_Error("PKCS10_Request decoding failed");
+ return *m_data.get();
}
/*
@@ -127,24 +156,23 @@ void PKCS10_Request::handle_attribute(const Attribute& attr)
*/
std::string PKCS10_Request::challenge_password() const
{
- return m_info.get1("PKCS9.ChallengePassword");
+ return data().m_challenge;
}
/*
* Return the name of the requestor
*/
-X509_DN PKCS10_Request::subject_dn() const
+const X509_DN& PKCS10_Request::subject_dn() const
{
- return create_dn(m_info);
+ return data().m_subject_dn;
}
/*
* Return the public key of the requestor
*/
-std::vector<uint8_t> PKCS10_Request::raw_public_key() const
+const std::vector<uint8_t>& PKCS10_Request::raw_public_key() const
{
- DataSource_Memory source(m_info.get1("X509.Certificate.public_key"));
- return unlock(PEM_Code::decode_check_label(source, "PUBLIC KEY"));
+ return data().m_public_key_bits;
}
/*
@@ -152,16 +180,24 @@ std::vector<uint8_t> PKCS10_Request::raw_public_key() const
*/
Public_Key* PKCS10_Request::subject_public_key() const
{
- DataSource_Memory source(m_info.get1("X509.Certificate.public_key"));
+ DataSource_Memory source(raw_public_key());
return X509::load_key(source);
}
/*
* Return the alternative names of the requestor
*/
-AlternativeName PKCS10_Request::subject_alt_name() const
+const AlternativeName& PKCS10_Request::subject_alt_name() const
{
- return create_alt_name(m_info);
+ return data().m_alt_name;
+ }
+
+/*
+* Return the X509v3 extensions
+*/
+const Extensions& PKCS10_Request::extensions() const
+ {
+ return data().m_extensions;
}
/*
@@ -169,7 +205,7 @@ AlternativeName PKCS10_Request::subject_alt_name() const
*/
Key_Constraints PKCS10_Request::constraints() const
{
- if(auto ext = m_extensions.get(OIDS::lookup("X509v3.KeyUsage")))
+ if(auto ext = extensions().get(OIDS::lookup("X509v3.KeyUsage")))
{
return dynamic_cast<Cert_Extension::Key_Usage&>(*ext).get_constraints();
}
@@ -182,7 +218,7 @@ Key_Constraints PKCS10_Request::constraints() const
*/
std::vector<OID> PKCS10_Request::ex_constraints() const
{
- if(auto ext = m_extensions.get(OIDS::lookup("X509v3.ExtendedKeyUsage")))
+ if(auto ext = extensions().get(OIDS::lookup("X509v3.ExtendedKeyUsage")))
{
return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).get_oids();
}
@@ -195,7 +231,7 @@ std::vector<OID> PKCS10_Request::ex_constraints() const
*/
bool PKCS10_Request::is_CA() const
{
- if(auto ext = m_extensions.get(OIDS::lookup("X509v3.BasicConstraints")))
+ if(auto ext = extensions().get(OIDS::lookup("X509v3.BasicConstraints")))
{
return dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext).get_is_ca();
}
@@ -208,7 +244,7 @@ bool PKCS10_Request::is_CA() const
*/
size_t PKCS10_Request::path_limit() const
{
- if(auto ext = m_extensions.get(OIDS::lookup("X509v3.BasicConstraints")))
+ if(auto ext = extensions().get(OIDS::lookup("X509v3.BasicConstraints")))
{
Cert_Extension::Basic_Constraints& basic_constraints = dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext);
if(basic_constraints.get_is_ca())
@@ -220,12 +256,4 @@ size_t PKCS10_Request::path_limit() const
return 0;
}
-/*
-* Return the X509v3 extensions
-*/
-Extensions PKCS10_Request::extensions() const
- {
- return m_extensions;
- }
-
}
diff --git a/src/lib/x509/pkcs10.h b/src/lib/x509/pkcs10.h
index 973b91b8a..abed5fa75 100644
--- a/src/lib/x509/pkcs10.h
+++ b/src/lib/x509/pkcs10.h
@@ -12,7 +12,6 @@
#include <botan/x509_obj.h>
#include <botan/x509_dn.h>
#include <botan/x509_ext.h>
-#include <botan/datastor.h>
#include <botan/key_constraint.h>
#include <botan/asn1_attribute.h>
#include <botan/asn1_alt_name.h>
@@ -20,6 +19,8 @@
namespace Botan {
+struct PKCS10_Data;
+
/**
* PKCS #10 Certificate Request.
*/
@@ -36,19 +37,19 @@ class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
* Get the raw DER encoded public key.
* @return raw DER encoded public key
*/
- std::vector<uint8_t> raw_public_key() const;
+ const std::vector<uint8_t>& raw_public_key() const;
/**
* Get the subject DN.
* @return subject DN
*/
- X509_DN subject_dn() const;
+ const X509_DN& subject_dn() const;
/**
* Get the subject alternative name.
* @return subject alternative name.
*/
- AlternativeName subject_alt_name() const;
+ const AlternativeName& subject_alt_name() const;
/**
* Get the key constraints for the key associated with this
@@ -86,7 +87,7 @@ class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
* Get the X509v3 extensions.
* @return X509v3 extensions
*/
- Extensions extensions() const;
+ const Extensions& extensions() const;
/**
* Create a PKCS#10 Request from a data source.
@@ -110,10 +111,10 @@ class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
explicit PKCS10_Request(const std::vector<uint8_t>& vec);
private:
void force_decode() override;
- void handle_attribute(const Attribute&);
- Data_Store m_info;
- Extensions m_extensions;
+ const PKCS10_Data& data() const;
+
+ std::shared_ptr<PKCS10_Data> m_data;
};
}
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
index 81536c768..682002111 100644
--- a/src/lib/x509/x509_ca.cpp
+++ b/src/lib/x509/x509_ca.cpp
@@ -25,12 +25,14 @@ namespace Botan {
X509_CA::X509_CA(const X509_Certificate& c,
const Private_Key& key,
const std::string& hash_fn,
- RandomNumberGenerator& rng) : m_ca_cert(c)
+ RandomNumberGenerator& rng) :
+ m_ca_cert(c),
+ m_hash_fn(hash_fn)
{
if(!m_ca_cert.is_CA_cert())
throw Invalid_Argument("X509_CA: This certificate is not for a CA");
- m_signer.reset(choose_sig_format(key, rng, hash_fn, m_ca_sig_algo));
+ m_signer.reset(choose_sig_format(key, rng, m_hash_fn, m_ca_sig_algo));
}
/*
diff --git a/src/lib/x509/x509_ca.h b/src/lib/x509/x509_ca.h
index 7859a2058..cd122a6fc 100644
--- a/src/lib/x509/x509_ca.h
+++ b/src/lib/x509/x509_ca.h
@@ -116,7 +116,13 @@ class BOTAN_PUBLIC_API(2,0) X509_CA final
X509_CA(const X509_CA&) = delete;
X509_CA& operator=(const X509_CA&) = delete;
+#if !defined(BOTAN_BUILD_COMPILER_IS_MSVC_2013)
+ X509_CA(X509_CA&&) = default;
+ X509_CA& operator=(X509_CA&&) = default;
+#endif
+
~X509_CA();
+
private:
X509_CRL make_crl(const std::vector<CRL_Entry>& entries,
uint32_t crl_number, uint32_t next_update,
@@ -124,6 +130,7 @@ class BOTAN_PUBLIC_API(2,0) X509_CA final
AlgorithmIdentifier m_ca_sig_algo;
X509_Certificate m_ca_cert;
+ std::string m_hash_fn;
std::unique_ptr<PK_Signer> m_signer;
};
diff --git a/src/lib/x509/x509_crl.cpp b/src/lib/x509/x509_crl.cpp
index e27733d32..4a6c4249a 100644
--- a/src/lib/x509/x509_crl.cpp
+++ b/src/lib/x509/x509_crl.cpp
@@ -12,11 +12,24 @@
namespace Botan {
+struct CRL_Data
+ {
+ X509_DN m_issuer;
+ X509_Time m_this_update;
+ X509_Time m_next_update;
+ std::vector<CRL_Entry> m_entries;
+ Extensions m_extensions;
+
+ // cached values from extensions
+ size_t m_crl_number = 0;
+ std::vector<uint8_t> m_auth_key_id;
+ };
+
/*
* Load a X.509 CRL
*/
-X509_CRL::X509_CRL(DataSource& in, bool touc) :
- X509_Object(in, "X509 CRL/CRL"), m_throw_on_unknown_critical(touc)
+X509_CRL::X509_CRL(DataSource& in) :
+ X509_Object(in, "X509 CRL/CRL")
{
do_decode();
}
@@ -25,26 +38,30 @@ X509_CRL::X509_CRL(DataSource& in, bool touc) :
/*
* Load a X.509 CRL
*/
-X509_CRL::X509_CRL(const std::string& fsname, bool touc) :
- X509_Object(fsname, "CRL/X509 CRL"), m_throw_on_unknown_critical(touc)
+X509_CRL::X509_CRL(const std::string& fsname) :
+ X509_Object(fsname, "CRL/X509 CRL")
{
do_decode();
}
#endif
-X509_CRL::X509_CRL(const std::vector<uint8_t>& in, bool touc) :
- X509_Object(in, "CRL/X509 CRL"), m_throw_on_unknown_critical(touc)
+X509_CRL::X509_CRL(const std::vector<uint8_t>& in) :
+ X509_Object(in, "CRL/X509 CRL")
{
do_decode();
}
-X509_CRL::X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
- const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked) :
- X509_Object(), m_throw_on_unknown_critical(false), m_revoked(revoked)
+X509_CRL::X509_CRL(const X509_DN& issuer,
+ const X509_Time& this_update,
+ const X509_Time& next_update,
+ const std::vector<CRL_Entry>& revoked) :
+ X509_Object()
{
- m_info.add(issuer.contents());
- m_info.add("X509.CRL.start", thisUpdate.to_string());
- m_info.add("X509.CRL.end", nextUpdate.to_string());
+ m_data.reset(new CRL_Data);
+ m_data->m_issuer = issuer;
+ m_data->m_this_update = this_update;
+ m_data->m_next_update = next_update;
+ m_data->m_entries = revoked;
}
/**
@@ -63,18 +80,21 @@ bool X509_CRL::is_revoked(const X509_Certificate& cert) const
std::vector<uint8_t> cert_akid = cert.authority_key_id();
if(!crl_akid.empty() && !cert_akid.empty())
+ {
if(crl_akid != cert_akid)
return false;
+ }
std::vector<uint8_t> cert_serial = cert.serial_number();
bool is_revoked = false;
- for(size_t i = 0; i != m_revoked.size(); ++i)
+ // FIXME would be nice to avoid a linear scan here - maybe sort the entries?
+ for(const CRL_Entry& entry : get_revoked())
{
- if(cert_serial == m_revoked[i].serial_number())
+ if(cert_serial == entry.serial_number())
{
- if(m_revoked[i].reason_code() == REMOVE_FROM_CRL)
+ if(entry.reason_code() == REMOVE_FROM_CRL)
is_revoked = false;
else
is_revoked = true;
@@ -87,31 +107,31 @@ bool X509_CRL::is_revoked(const X509_Certificate& cert) const
/*
* Decode the TBSCertList data
*/
-void X509_CRL::force_decode()
+namespace {
+
+std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
+ const AlgorithmIdentifier& sig_algo)
{
- BER_Decoder tbs_crl(signed_body());
+ std::unique_ptr<CRL_Data> data(new CRL_Data);
+
+ BER_Decoder tbs_crl(body);
size_t version;
tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
if(version != 0 && version != 1)
- throw X509_CRL_Error("Unknown X.509 CRL version " +
+ throw X509_CRL::X509_CRL_Error("Unknown X.509 CRL version " +
std::to_string(version+1));
AlgorithmIdentifier sig_algo_inner;
tbs_crl.decode(sig_algo_inner);
- if(signature_algorithm() != sig_algo_inner)
- throw X509_CRL_Error("Algorithm identifier mismatch");
-
- X509_DN dn_issuer;
- tbs_crl.decode(dn_issuer);
- m_info.add(dn_issuer.contents());
+ if(sig_algo != sig_algo_inner)
+ throw X509_CRL::X509_CRL_Error("Algorithm identifier mismatch");
- X509_Time start, end;
- tbs_crl.decode(start).decode(end);
- m_info.add("X509.CRL.start", start.to_string());
- m_info.add("X509.CRL.end", end.to_string());
+ tbs_crl.decode(data->m_issuer)
+ .decode(data->m_this_update)
+ .decode(data->m_next_update);
BER_Object next = tbs_crl.get_next_object();
@@ -121,9 +141,9 @@ void X509_CRL::force_decode()
while(cert_list.more_items())
{
- CRL_Entry entry(m_throw_on_unknown_critical);
+ CRL_Entry entry;
cert_list.decode(entry);
- m_revoked.push_back(entry);
+ data->m_entries.push_back(entry);
}
next = tbs_crl.get_next_object();
}
@@ -132,44 +152,59 @@ void X509_CRL::force_decode()
next.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
{
BER_Decoder crl_options(next.value);
-
- Extensions extensions(m_throw_on_unknown_critical);
-
- crl_options.decode(extensions).verify_end();
-
- extensions.contents_to(m_info, m_info);
-
+ crl_options.decode(data->m_extensions).verify_end();
next = tbs_crl.get_next_object();
}
if(next.type_tag != NO_OBJECT)
- throw X509_CRL_Error("Unknown tag in CRL");
+ throw X509_CRL::X509_CRL_Error("Unknown tag in CRL");
tbs_crl.verify_end();
+
+ return data;
+ }
+
+}
+
+void X509_CRL::force_decode()
+ {
+ m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
+ }
+
+const CRL_Data& X509_CRL::data() const
+ {
+ if(!m_data)
+ throw Decoding_Error("Error decoding X509 CRL");
+ return *m_data.get();
+ }
+
+const Extensions& X509_CRL::extensions() const
+ {
+ return data().m_extensions;
}
/*
* Return the list of revoked certificates
*/
-std::vector<CRL_Entry> X509_CRL::get_revoked() const
+const std::vector<CRL_Entry>& X509_CRL::get_revoked() const
{
- return m_revoked;
+ return data().m_entries;
}
/*
* Return the distinguished name of the issuer
*/
-X509_DN X509_CRL::issuer_dn() const
+const X509_DN& X509_CRL::issuer_dn() const
{
- return create_dn(m_info);
+ return data().m_issuer;
}
/*
* Return the key identifier of the issuer
*/
-std::vector<uint8_t> X509_CRL::authority_key_id() const
+const std::vector<uint8_t>& X509_CRL::authority_key_id() const
{
- return m_info.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ return data().m_auth_key_id;
}
/*
@@ -177,23 +212,23 @@ std::vector<uint8_t> X509_CRL::authority_key_id() const
*/
uint32_t X509_CRL::crl_number() const
{
- return m_info.get1_uint32("X509v3.CRLNumber");
+ return data().m_crl_number;
}
/*
* Return the issue data of the CRL
*/
-X509_Time X509_CRL::this_update() const
+const X509_Time& X509_CRL::this_update() const
{
- return X509_Time(m_info.get1("X509.CRL.start"), ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ return data().m_this_update;
}
/*
* Return the date when a new CRL will be issued
*/
-X509_Time X509_CRL::next_update() const
+const X509_Time& X509_CRL::next_update() const
{
- return X509_Time(m_info.get1("X509.CRL.end"), ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ return data().m_next_update;
}
}
diff --git a/src/lib/x509/x509_crl.h b/src/lib/x509/x509_crl.h
index 865117300..c3c986cc1 100644
--- a/src/lib/x509/x509_crl.h
+++ b/src/lib/x509/x509_crl.h
@@ -11,11 +11,12 @@
#include <botan/x509_obj.h>
#include <botan/x509_dn.h>
#include <botan/crl_ent.h>
-#include <botan/datastor.h>
#include <vector>
namespace Botan {
+class CRL_Data;
+class Extensions;
class X509_Certificate;
/**
@@ -43,19 +44,24 @@ class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
* Get the entries of this CRL in the form of a vector.
* @return vector containing the entries of this CRL.
*/
- std::vector<CRL_Entry> get_revoked() const;
+ const std::vector<CRL_Entry>& get_revoked() const;
/**
* Get the issuer DN of this CRL.
* @return CRLs issuer DN
*/
- X509_DN issuer_dn() const;
+ const X509_DN& issuer_dn() const;
+
+ /**
+ * @return extension data for this CRL
+ */
+ const Extensions& extensions() const;
/**
* Get the AuthorityKeyIdentifier of this CRL.
* @return this CRLs AuthorityKeyIdentifier
*/
- std::vector<uint8_t> authority_key_id() const;
+ const std::vector<uint8_t>& authority_key_id() const;
/**
* Get the serial number of this CRL.
@@ -67,41 +73,33 @@ class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
* Get the CRL's thisUpdate value.
* @return CRLs thisUpdate
*/
- X509_Time this_update() const;
+ const X509_Time& this_update() const;
/**
* Get the CRL's nextUpdate value.
* @return CRLs nextdUpdate
*/
- X509_Time next_update() const;
+ const X509_Time& next_update() const;
/**
* Construct a CRL from a data source.
* @param source the data source providing the DER or PEM encoded CRL.
- * @param throw_on_unknown_critical should we throw an exception
- * if an unknown CRL extension marked as critical is encountered.
*/
- X509_CRL(DataSource& source, bool throw_on_unknown_critical = false);
+ X509_CRL(DataSource& source);
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
/**
* Construct a CRL from a file containing the DER or PEM encoded CRL.
* @param filename the name of the CRL file
- * @param throw_on_unknown_critical should we throw an exception
- * if an unknown CRL extension marked as critical is encountered.
*/
- X509_CRL(const std::string& filename,
- bool throw_on_unknown_critical = false);
+ X509_CRL(const std::string& filename);
#endif
/**
* Construct a CRL from a binary vector
* @param vec the binary (DER) representation of the CRL
- * @param throw_on_unknown_critical should we throw an exception
- * if an unknown CRL extension marked as critical is encountered.
*/
- X509_CRL(const std::vector<uint8_t>& vec,
- bool throw_on_unknown_critical = false);
+ X509_CRL(const std::vector<uint8_t>& vec);
/**
* Construct a CRL
@@ -116,9 +114,9 @@ class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
private:
void force_decode() override;
- bool m_throw_on_unknown_critical;
- std::vector<CRL_Entry> m_revoked;
- Data_Store m_info;
+ const CRL_Data& data() const;
+
+ std::shared_ptr<CRL_Data> m_data;
};
}
diff --git a/src/lib/asn1/x509_dn.cpp b/src/lib/x509/x509_dn.cpp
index dd92b25ec..d07344aae 100644
--- a/src/lib/asn1/x509_dn.cpp
+++ b/src/lib/x509/x509_dn.cpp
@@ -79,10 +79,34 @@ std::multimap<std::string, std::string> X509_DN::contents() const
{
std::multimap<std::string, std::string> retval;
for(auto i = m_dn_info.begin(); i != m_dn_info.end(); ++i)
- multimap_insert(retval, OIDS::lookup(i->first), i->second.value());
+ {
+ std::string str_value = OIDS::oid2str(i->first);
+
+ if(str_value.empty())
+ str_value = i->first.as_string();
+ multimap_insert(retval, str_value, i->second.value());
+ }
return retval;
}
+bool X509_DN::has_field(const std::string& attr) const
+ {
+ const OID oid = OIDS::lookup(deref_info_field(attr));
+ auto range = m_dn_info.equal_range(oid);
+ return (range.first != range.second);
+ }
+
+std::string X509_DN::get_first_attribute(const std::string& attr) const
+ {
+ const OID oid = OIDS::lookup(deref_info_field(attr));
+
+ auto i = m_dn_info.lower_bound(oid);
+ if(i != m_dn_info.end() && i->first == oid)
+ return i->second.value();
+
+ return "";
+ }
+
/*
* Get a single attribute type
*/
@@ -98,10 +122,7 @@ std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
return values;
}
-/*
-* Return the BER encoded data, if any
-*/
-std::vector<uint8_t> X509_DN::get_bits() const
+const std::vector<uint8_t>& X509_DN::get_bits() const
{
return m_dn_bits;
}
@@ -278,6 +299,9 @@ std::string to_short_form(const std::string& long_id)
if(long_id == "X520.CommonName")
return "CN";
+ if(long_id == "X520.Country")
+ return "C";
+
if(long_id == "X520.Organization")
return "O";
diff --git a/src/lib/asn1/x509_dn.h b/src/lib/x509/x509_dn.h
index 09f8cf16b..cbd89de7c 100644
--- a/src/lib/asn1/x509_dn.h
+++ b/src/lib/x509/x509_dn.h
@@ -25,23 +25,29 @@ class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object
void encode_into(class DER_Encoder&) const override;
void decode_from(class BER_Decoder&) override;
- std::multimap<OID, std::string> get_attributes() const;
- std::vector<std::string> get_attribute(const std::string&) const;
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+
+ std::string get_first_attribute(const std::string& attr) const;
+ std::multimap<OID, std::string> get_attributes() const;
std::multimap<std::string, std::string> contents() const;
- void add_attribute(const std::string&, const std::string&);
- void add_attribute(const OID&, const std::string&);
+ void add_attribute(const std::string& key, const std::string& val);
+ void add_attribute(const OID& oid, const std::string& val);
- static std::string deref_info_field(const std::string&);
+ static std::string deref_info_field(const std::string& key);
- std::vector<uint8_t> get_bits() const;
+ /*
+ * Return the BER encoded data, if any
+ */
+ const std::vector<uint8_t>& get_bits() const;
bool empty() const { return m_dn_info.empty(); }
X509_DN() = default;
- explicit X509_DN(const std::multimap<OID, std::string>&);
- explicit X509_DN(const std::multimap<std::string, std::string>&);
+ explicit X509_DN(const std::multimap<OID, std::string>& vals);
+ explicit X509_DN(const std::multimap<std::string, std::string>& vals);
private:
std::multimap<OID, ASN1_String> m_dn_info;
std::vector<uint8_t> m_dn_bits;
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
index 6e4c29d42..682cc1cc7 100644
--- a/src/lib/x509/x509_ext.cpp
+++ b/src/lib/x509/x509_ext.cpp
@@ -8,10 +8,11 @@
#include <botan/x509_ext.h>
#include <botan/x509cert.h>
-#include <botan/sha160.h>
+#include <botan/datastor.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
#include <botan/oids.h>
+#include <botan/hash.h>
#include <botan/internal/bit_ops.h>
#include <algorithm>
#include <sstream>
@@ -19,138 +20,177 @@
namespace Botan {
/*
-* List of X.509 Certificate Extensions
+* Create a Certificate_Extension object of some kind to handle
*/
-Certificate_Extension* Extensions::create_extension(const OID& oid, bool critical)
+Certificate_Extension*
+Extensions::create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body)
{
-#define X509_EXTENSION(NAME, TYPE) \
- if(oid == OIDS::lookup(NAME)) { return new Cert_Extension::TYPE(); }
+ const std::string oid_str = oid.as_string();
- X509_EXTENSION("X509v3.KeyUsage", Key_Usage);
- X509_EXTENSION("X509v3.BasicConstraints", Basic_Constraints);
- X509_EXTENSION("X509v3.SubjectKeyIdentifier", Subject_Key_ID);
- X509_EXTENSION("X509v3.AuthorityKeyIdentifier", Authority_Key_ID);
- X509_EXTENSION("X509v3.ExtendedKeyUsage", Extended_Key_Usage);
- X509_EXTENSION("X509v3.IssuerAlternativeName", Issuer_Alternative_Name);
- X509_EXTENSION("X509v3.SubjectAlternativeName", Subject_Alternative_Name);
- X509_EXTENSION("X509v3.NameConstraints", Name_Constraints);
- X509_EXTENSION("X509v3.CertificatePolicies", Certificate_Policies);
- X509_EXTENSION("X509v3.CRLDistributionPoints", CRL_Distribution_Points);
- X509_EXTENSION("PKIX.AuthorityInformationAccess", Authority_Information_Access);
- X509_EXTENSION("X509v3.CRLNumber", CRL_Number);
- X509_EXTENSION("X509v3.ReasonCode", CRL_ReasonCode);
+ Certificate_Extension* extn = nullptr;
- return critical ? new Cert_Extension::Unknown_Critical_Extension(oid) : nullptr;
+ if(oid == Cert_Extension::Subject_Key_ID::static_oid())
+ {
+ extn = new Cert_Extension::Subject_Key_ID;
+ }
+ else if(oid == Cert_Extension::Key_Usage::static_oid())
+ {
+ extn = new Cert_Extension::Key_Usage;
+ }
+ else if(oid == Cert_Extension::Subject_Alternative_Name::static_oid())
+ {
+ extn = new Cert_Extension::Subject_Alternative_Name;
+ }
+ else if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid())
+ {
+ extn = new Cert_Extension::Issuer_Alternative_Name;
+ }
+ else if(oid == Cert_Extension::Basic_Constraints::static_oid())
+ {
+ extn = new Cert_Extension::Basic_Constraints;
+ }
+ else if(oid == Cert_Extension::CRL_Number::static_oid())
+ {
+ extn = new Cert_Extension::CRL_Number;
+ }
+ else if(oid == Cert_Extension::CRL_ReasonCode::static_oid())
+ {
+ extn = new Cert_Extension::CRL_ReasonCode;
+ }
+ else if(oid == Cert_Extension::Authority_Key_ID::static_oid())
+ {
+ extn = new Cert_Extension::Authority_Key_ID;
+ }
+ else if(oid == Cert_Extension::Name_Constraints::static_oid())
+ {
+ extn = new Cert_Extension::Name_Constraints;
+ }
+ else if(oid == Cert_Extension::CRL_Distribution_Points::static_oid())
+ {
+ extn = new Cert_Extension::CRL_Distribution_Points;
+ }
+ else if(oid == Cert_Extension::Certificate_Policies::static_oid())
+ {
+ extn = new Cert_Extension::Certificate_Policies;
+ }
+ else if(oid == Cert_Extension::Extended_Key_Usage::static_oid())
+ {
+ extn = new Cert_Extension::Extended_Key_Usage;
+ }
+ else if(oid == Cert_Extension::Authority_Information_Access::static_oid())
+ {
+ extn = new Cert_Extension::Authority_Information_Access;
+ }
+ else
+ {
+ // some other unknown extension type
+ extn = new Cert_Extension::Unknown_Extension(oid, critical);
+ }
+
+ try
+ {
+ extn->decode_inner(body);
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error("Decoding X.509 extension " + oid.as_string() + " failed", e.what());
+ }
+ return extn;
}
/*
-* Extensions Copy Constructor
+* Validate the extension (the default implementation is a NOP)
*/
-Extensions::Extensions(const Extensions& extensions) : ASN1_Object()
+void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
+ std::vector<std::set<Certificate_Status_Code>>&,
+ size_t)
{
- *this = extensions;
}
/*
-* Extensions Assignment Operator
+* Add a new cert
*/
-Extensions& Extensions::operator=(const Extensions& other)
+void Extensions::add(Certificate_Extension* extn, bool critical)
{
- if(this == &other)
- return *this;
-
- m_extensions.clear();
-
- for(size_t i = 0; i != other.m_extensions.size(); ++i)
- m_extensions.push_back(
- std::make_pair(std::unique_ptr<Certificate_Extension>(other.m_extensions[i].first->copy()),
- other.m_extensions[i].second));
-
- m_extensions_raw = other.m_extensions_raw;
- m_throw_on_unknown_critical = other.m_throw_on_unknown_critical;
+ // sanity check: we don't want to have the same extension more than once
+ if(m_extension_info.count(extn->oid_of()) > 0)
+ throw Invalid_Argument(extn->oid_name() + " extension already present in Extensions::add");
- return (*this);
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
}
-/*
-* Return the OID of this extension
-*/
-OID Certificate_Extension::oid_of() const
+void Extensions::replace(Certificate_Extension* extn, bool critical)
{
- return OIDS::lookup(oid_name());
+ // Remove it if it existed
+ m_extension_info.erase(extn->oid_of());
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
}
-/*
-* Validate the extension (the default implementation is a NOP)
-*/
-void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
- const std::vector<std::shared_ptr<const X509_Certificate>>&,
- std::vector<std::set<Certificate_Status_Code>>&,
- size_t)
+bool Extensions::extension_set(const OID& oid) const
{
+ return (m_extension_info.find(oid) != m_extension_info.end());
}
-void Extensions::add(Certificate_Extension* extn, bool critical)
+bool Extensions::critical_extension_set(const OID& oid) const
{
- // sanity check: we don't want to have the same extension more than once
- for(const auto& ext : m_extensions)
- {
- if(ext.first->oid_of() == extn->oid_of())
- {
- throw Invalid_Argument(extn->oid_name() + " extension already present");
- }
- }
-
- if(m_extensions_raw.count(extn->oid_of()) > 0)
- {
- throw Invalid_Argument(extn->oid_name() + " extension already present");
- }
-
- m_extensions.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(extn), critical));
- m_extensions_raw.emplace(extn->oid_of(), std::make_pair(extn->encode_inner(), critical));
+ auto i = m_extension_info.find(oid);
+ if(i != m_extension_info.end())
+ return i->second.is_critical();
+ return false;
}
-void Extensions::replace(Certificate_Extension* extn, bool critical)
+const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const
{
- for(auto it = m_extensions.begin(); it != m_extensions.end(); ++it)
- {
- if(it->first->oid_of() == extn->oid_of())
- {
- m_extensions.erase(it);
- break;
- }
- }
+ auto extn = m_extension_info.find(oid);
+ if(extn == m_extension_info.end())
+ return nullptr;
- m_extensions.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(extn), critical));
- m_extensions_raw[extn->oid_of()] = std::make_pair(extn->encode_inner(), critical);
+ return &extn->second.obj();
}
std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const
{
- for(auto& ext : m_extensions)
+ if(const Certificate_Extension* ext = this->get_extension_object(oid))
{
- if(ext.first->oid_of() == oid)
- {
- return std::unique_ptr<Certificate_Extension>(ext.first->copy());
- }
+ return std::unique_ptr<Certificate_Extension>(ext->copy());
}
-
return nullptr;
}
std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const
{
std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts;
- for(auto& ext : m_extensions)
+ for(auto&& ext : m_extension_info)
{
- exts.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(ext.first->copy()), ext.second));
+ exts.push_back(
+ std::make_pair(
+ std::unique_ptr<Certificate_Extension>(ext.second.obj().copy()),
+ ext.second.is_critical())
+ );
}
return exts;
}
std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw() const
{
- return m_extensions_raw;
+ std::map<OID, std::pair<std::vector<uint8_t>, bool>> out;
+ for(auto&& ext : m_extension_info)
+ {
+ out.emplace(ext.first,
+ std::make_pair(ext.second.bits(),
+ ext.second.is_critical()));
+ }
+ return out;
}
/*
@@ -158,44 +198,20 @@ std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw(
*/
void Extensions::encode_into(DER_Encoder& to_object) const
{
- // encode any known extensions
- for(size_t i = 0; i != m_extensions.size(); ++i)
+ for(auto ext_info : m_extension_info)
{
- const Certificate_Extension* ext = m_extensions[i].first.get();
- const bool is_critical = m_extensions[i].second;
-
- const bool should_encode = ext->should_encode();
+ const OID& oid = ext_info.first;
+ const bool should_encode = ext_info.second.obj().should_encode();
if(should_encode)
{
- to_object.start_cons(SEQUENCE)
- .encode(ext->oid_of())
- .encode_optional(is_critical, false)
- .encode(ext->encode_inner(), OCTET_STRING)
- .end_cons();
- }
- }
-
- // encode any unknown extensions
- for(const auto& ext_raw : m_extensions_raw)
- {
- const bool is_critical = ext_raw.second.second;
- const OID oid = ext_raw.first;
- const std::vector<uint8_t> value = ext_raw.second.first;
-
- auto pos = std::find_if(std::begin(m_extensions), std::end(m_extensions),
- [&oid](const std::pair<std::unique_ptr<Certificate_Extension>, bool>& ext) -> bool
- {
- return ext.first->oid_of() == oid;
- });
+ const bool is_critical = ext_info.second.is_critical();
+ const std::vector<uint8_t>& ext_value = ext_info.second.bits();
- if(pos == std::end(m_extensions))
- {
- // not found in m_extensions, must be unknown
to_object.start_cons(SEQUENCE)
.encode(oid)
.encode_optional(is_critical, false)
- .encode(value, OCTET_STRING)
+ .encode(ext_value, OCTET_STRING)
.end_cons();
}
}
@@ -206,47 +222,29 @@ void Extensions::encode_into(DER_Encoder& to_object) const
*/
void Extensions::decode_from(BER_Decoder& from_source)
{
- m_extensions.clear();
- m_extensions_raw.clear();
+ m_extension_oids.clear();
+ m_extension_info.clear();
BER_Decoder sequence = from_source.start_cons(SEQUENCE);
while(sequence.more_items())
{
OID oid;
- std::vector<uint8_t> value;
bool critical;
+ std::vector<uint8_t> bits;
sequence.start_cons(SEQUENCE)
- .decode(oid)
- .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
- .decode(value, OCTET_STRING)
- .end_cons();
-
- m_extensions_raw.emplace(oid, std::make_pair(value, critical));
-
- std::unique_ptr<Certificate_Extension> ext(create_extension(oid, critical));
+ .decode(oid)
+ .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+ .decode(bits, OCTET_STRING)
+ .end_cons();
- if(!ext && critical && m_throw_on_unknown_critical)
- throw Decoding_Error("Encountered unknown X.509 extension marked "
- "as critical; OID = " + oid.as_string());
+ Extensions_Info info(critical, bits,
+ create_extn_obj(oid, critical, bits));
- if(ext)
- {
- try
- {
- ext->decode_inner(value);
- }
- catch(std::exception& e)
- {
- throw Decoding_Error("Exception while decoding extension " +
- oid.as_string() + ": " + e.what());
- }
-
- m_extensions.push_back(std::make_pair(std::move(ext), critical));
- }
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
}
-
sequence.verify_end();
}
@@ -256,14 +254,14 @@ void Extensions::decode_from(BER_Decoder& from_source)
void Extensions::contents_to(Data_Store& subject_info,
Data_Store& issuer_info) const
{
- for(size_t i = 0; i != m_extensions.size(); ++i)
+ for(auto&& m_extn_info : m_extension_info)
{
- m_extensions[i].first->contents_to(subject_info, issuer_info);
- subject_info.add(m_extensions[i].first->oid_name() + ".is_critical", (m_extensions[i].second ? 1 : 0));
+ m_extn_info.second.obj().contents_to(subject_info, issuer_info);
+ subject_info.add(m_extn_info.second.obj().oid_name() + ".is_critical",
+ m_extn_info.second.is_critical());
}
}
-
namespace Cert_Extension {
/*
@@ -402,8 +400,15 @@ void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const
/*
* Subject_Key_ID Constructor
*/
-Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key) : m_key_id(unlock(SHA_160().process(pub_key)))
- {}
+Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key, const std::string& hash_name)
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+ m_key_id.resize(hash->output_length());
+
+ hash->update(pub_key);
+ hash->final(m_key_id.data());
+ }
/*
* Encode the extension
@@ -439,61 +444,50 @@ void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
/*
* Encode the extension
*/
-std::vector<uint8_t> Alternative_Name::encode_inner() const
+std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const
{
return DER_Encoder().encode(m_alt_name).get_contents_unlocked();
}
/*
-* Decode the extension
+* Encode the extension
*/
-void Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const
{
- BER_Decoder(in).decode(m_alt_name);
+ return DER_Encoder().encode(m_alt_name).get_contents_unlocked();
}
/*
-* Return a textual representation
+* Decode the extension
*/
-void Alternative_Name::contents_to(Data_Store& subject_info,
- Data_Store& issuer_info) const
+void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
{
- std::multimap<std::string, std::string> contents =
- get_alt_name().contents();
-
- if(m_oid_name_str == "X509v3.SubjectAlternativeName")
- subject_info.add(contents);
- else if(m_oid_name_str == "X509v3.IssuerAlternativeName")
- issuer_info.add(contents);
- else
- throw Internal_Error("In Alternative_Name, unknown type " +
- m_oid_name_str);
+ BER_Decoder(in).decode(m_alt_name);
}
/*
-* Alternative_Name Constructor
+* Decode the extension
*/
-Alternative_Name::Alternative_Name(const AlternativeName& alt_name,
- const std::string& oid_name_str) :
- m_oid_name_str(oid_name_str),
- m_alt_name(alt_name)
- {}
+void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
/*
-* Subject_Alternative_Name Constructor
+* Return a textual representation
*/
-Subject_Alternative_Name::Subject_Alternative_Name(
- const AlternativeName& name) :
- Alternative_Name(name, "X509v3.SubjectAlternativeName")
+void Subject_Alternative_Name::contents_to(Data_Store& subject_info,
+ Data_Store&) const
{
+ subject_info.add(get_alt_name().contents());
}
/*
-* Issuer_Alternative_Name Constructor
+* Return a textual representation
*/
-Issuer_Alternative_Name::Issuer_Alternative_Name(const AlternativeName& name) :
- Alternative_Name(name, "X509v3.IssuerAlternativeName")
+void Issuer_Alternative_Name::contents_to(Data_Store&, Data_Store& issuer_info) const
{
+ issuer_info.add(get_alt_name().contents());
}
/*
@@ -849,22 +843,26 @@ std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const
void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf)
{
- BER_Decoder(buf).decode_list(m_distribution_points).verify_end();
- }
+ BER_Decoder(buf)
+ .decode_list(m_distribution_points)
+ .verify_end();
-void CRL_Distribution_Points::contents_to(Data_Store& info, Data_Store&) const
- {
for(size_t i = 0; i != m_distribution_points.size(); ++i)
{
auto point = m_distribution_points[i].point().contents();
auto uris = point.equal_range("URI");
-
for(auto uri = uris.first; uri != uris.second; ++uri)
- info.add("CRL.DistributionPoint", uri->second);
+ m_crl_distribution_urls.push_back(uri->second);
}
}
+void CRL_Distribution_Points::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(const std::string& crl_url : m_crl_distribution_urls)
+ subject.add("CRL.DistributionPoint", crl_url);
+ }
+
void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const
{
throw Not_Implemented("CRL_Distribution_Points encoding");
@@ -880,17 +878,20 @@ void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder&
.end_cons().end_cons();
}
-std::vector<uint8_t> Unknown_Critical_Extension::encode_inner() const
+std::vector<uint8_t> Unknown_Extension::encode_inner() const
{
- throw Not_Implemented("Unknown_Critical_Extension encoding");
+ return m_bytes;
}
-void Unknown_Critical_Extension::decode_inner(const std::vector<uint8_t>&)
+void Unknown_Extension::decode_inner(const std::vector<uint8_t>& bytes)
{
+ // Just treat as an opaque blob at this level
+ m_bytes = bytes;
}
-void Unknown_Critical_Extension::contents_to(Data_Store&, Data_Store&) const
+void Unknown_Extension::contents_to(Data_Store&, Data_Store&) const
{
+ // No information store
}
}
diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h
index 69647616f..2243d6deb 100644
--- a/src/lib/x509/x509_ext.h
+++ b/src/lib/x509/x509_ext.h
@@ -12,7 +12,6 @@
#include <botan/asn1_oid.h>
#include <botan/asn1_alt_name.h>
#include <botan/cert_status.h>
-#include <botan/datastor.h>
#include <botan/name_constraint.h>
#include <botan/key_constraint.h>
#include <botan/crl_ent.h>
@@ -20,6 +19,7 @@
namespace Botan {
+class Data_Store;
class X509_Certificate;
/**
@@ -31,7 +31,15 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension
/**
* @return OID representing this extension
*/
- virtual OID oid_of() const;
+ virtual OID oid_of() const = 0;
+
+ /*
+ * @return specific OID name
+ * If possible OIDS table should match oid_name to OIDS, ie
+ * OIDS::lookup(ext->oid_name()) == ext->oid_of()
+ * Should return empty string if OID is not known
+ */
+ virtual std::string oid_name() const = 0;
/**
* Make a copy of this extension
@@ -49,11 +57,6 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension
Data_Store& issuer) const = 0;
/*
- * @return specific OID name
- */
- virtual std::string oid_name() const = 0;
-
- /*
* Callback visited during path validation.
*
* An extension can implement this callback to inspect
@@ -87,13 +90,65 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension
class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
{
public:
+ /**
+ * Look up an object in the extensions, based on OID Returns
+ * nullptr if not set, if the extension was either absent or not
+ * handled. The pointer returned is owned by the Extensions
+ * object.
+ * This would be better with an optional<T> return value
+ */
+ const Certificate_Extension* get_extension_object(const OID& oid) const;
+
+ template<typename T>
+ const T* get_extension_object_as(const OID& oid = T::static_oid()) const
+ {
+ if(const Certificate_Extension* extn = get_extension_object(oid))
+ {
+ if(const T* extn_as_T = dynamic_cast<const T*>(extn))
+ {
+ return extn_as_T;
+ }
+ else
+ {
+ throw Exception("Exception::get_extension_object_as dynamic_cast failed");
+ }
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * Return the set of extensions in the order they appeared in the certificate
+ * (or as they were added, if constructed)
+ */
+ const std::vector<OID>& get_extension_oids() const
+ {
+ return m_extension_oids;
+ }
+
+ /**
+ * Return true if an extension was set
+ */
+ bool extension_set(const OID& oid) const;
+
+ /**
+ * Return true if an extesion was set and marked critical
+ */
+ bool critical_extension_set(const OID& oid) const;
+
+ /**
+ * Return the raw bytes of the extension
+ * Will throw if OID was not set as an extension.
+ */
+ std::vector<uint8_t> get_extension_bits(const OID& oid) const;
+
void encode_into(class DER_Encoder&) const override;
void decode_from(class BER_Decoder&) override;
void contents_to(Data_Store&, Data_Store&) const;
/**
* Adds a new extension to the list.
- * @param extn the certificate extension
+ * @param extn pointer to the certificate extension (Extensions takes ownership)
* @param critical whether this extension should be marked as critical
* @throw Invalid_Argument if the extension is already present in the list
*/
@@ -110,67 +165,103 @@ class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
* Searches for an extension by OID and returns the result.
* Only the known extensions types declared in this header
* are searched for by this function.
- * @return Pointer to extension with oid, nullptr if not found.
+ * @return Copy of extension with oid, nullptr if not found.
+ * Can avoid creating a copy by using get_extension_object function
*/
std::unique_ptr<Certificate_Extension> get(const OID& oid) const;
/**
- * Searches for an extension by OID and returns the result.
- * Only the unknown extensions, that is, extensions
- * types that are not declared in this header, are searched
- * for by this function.
- * @return Pointer to extension with oid, nullptr if not found.
+ * Searches for an extension by OID and returns the result decoding
+ * it to some arbitrary extension type chosen by the application.
+ *
+ * Only the unknown extensions, that is, extensions types that
+ * are not declared in this header, are searched for by this
+ * function.
+ *
+ * @return Pointer to new extension with oid, nullptr if not found.
*/
template<typename T>
- std::unique_ptr<T> get_raw(const OID& oid)
- {
- try
+ std::unique_ptr<T> get_raw(const OID& oid) const
{
- if(m_extensions_raw.count(oid) > 0)
+ auto extn_info = m_extension_info.find(oid);
+
+ if(extn_info != m_extension_info.end())
{
- std::unique_ptr<T> ext(new T);
- ext->decode_inner(m_extensions_raw[oid].first);
- return std::move(ext);
+ // Unknown_Extension oid_name is empty
+ if(extn_info->second.obj().oid_name() == "")
+ {
+ std::unique_ptr<T> ext(new T);
+ ext->decode_inner(extn_info->second.bits());
+ return std::move(ext);
+ }
}
+ return nullptr;
}
- catch(std::exception& e)
- {
- throw Decoding_Error("Exception while decoding extension " +
- oid.as_string() + ": " + e.what());
- }
- return nullptr;
- }
/**
- * Returns the list of extensions together with the corresponding
- * criticality flag. Only contains the known extensions
- * types declared in this header.
+ * Returns a copy of the list of extensions together with the corresponding
+ * criticality flag. All extensions are encoded as some object, falling back
+ * to Unknown_Extension class which simply allows reading the bytes as well
+ * as the criticality flag.
*/
std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
/**
* Returns the list of extensions as raw, encoded bytes
* together with the corresponding criticality flag.
- * Contains all extensions, known as well as unknown extensions.
+ * Contains all extensions, including any extensions encoded as Unknown_Extension
*/
std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const;
- Extensions& operator=(const Extensions&);
+ Extensions() {}
- Extensions(const Extensions&);
+ Extensions(const Extensions&) = default;
+ Extensions& operator=(const Extensions&) = default;
- /**
- * @param st whether to throw an exception when encountering an unknown
- * extension type during decoding
- */
- explicit Extensions(bool st = true) : m_throw_on_unknown_critical(st) {}
+#if !defined(BOTAN_BUILD_COMPILER_IS_MSVC_2013)
+ Extensions(Extensions&&) = default;
+ Extensions& operator=(Extensions&&) = default;
+#endif
private:
- static Certificate_Extension* create_extension(const OID&, bool);
+ static Certificate_Extension* create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body);
- std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> m_extensions;
- bool m_throw_on_unknown_critical;
- std::map<OID, std::pair<std::vector<uint8_t>, bool>> m_extensions_raw;
+ class Extensions_Info
+ {
+ public:
+ Extensions_Info(bool critical,
+ Certificate_Extension* ext) :
+ m_critical(critical),
+ m_bits(ext->encode_inner()),
+ m_obj(ext)
+ {}
+
+ Extensions_Info(bool critical,
+ const std::vector<uint8_t>& encoding,
+ Certificate_Extension* ext) :
+ m_critical(critical),
+ m_bits(encoding),
+ m_obj(ext)
+ {}
+
+ bool is_critical() const { return m_critical; }
+ const std::vector<uint8_t>& bits() const { return m_bits; }
+ const Certificate_Extension& obj() const
+ {
+ BOTAN_ASSERT_NONNULL(m_obj.get());
+ return *m_obj.get();
+ }
+
+ private:
+ bool m_critical = false;
+ std::vector<uint8_t> m_bits;
+ std::shared_ptr<const Certificate_Extension> m_obj;
+ };
+
+ std::vector<OID> m_extension_oids;
+ std::map<OID, Extensions_Info> m_extension_info;
};
namespace Cert_Extension {
@@ -192,6 +283,9 @@ class BOTAN_PUBLIC_API(2,0) Basic_Constraints final : public Certificate_Extensi
bool get_is_ca() const { return m_is_ca; }
size_t get_path_limit() const;
+ static OID static_oid() { return OID("2.5.29.19"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override
{ return "X509v3.BasicConstraints"; }
@@ -216,6 +310,9 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
Key_Constraints get_constraints() const { return m_constraints; }
+ static OID static_oid() { return OID("2.5.29.15"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override { return "X509v3.KeyUsage"; }
@@ -234,13 +331,21 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
class BOTAN_PUBLIC_API(2,0) Subject_Key_ID final : public Certificate_Extension
{
public:
+ Subject_Key_ID() = default;
+
+ explicit Subject_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+ Subject_Key_ID(const std::vector<uint8_t>& public_key,
+ const std::string& hash_fn);
+
Subject_Key_ID* copy() const override
{ return new Subject_Key_ID(m_key_id); }
- Subject_Key_ID() = default;
- explicit Subject_Key_ID(const std::vector<uint8_t>&);
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.14"); }
+ OID oid_of() const override { return static_oid(); }
- std::vector<uint8_t> get_key_id() const { return m_key_id; }
private:
std::string oid_name() const override
{ return "X509v3.SubjectKeyIdentifier"; }
@@ -265,7 +370,10 @@ class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extensio
Authority_Key_ID() = default;
explicit Authority_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
- std::vector<uint8_t> get_key_id() const { return m_key_id; }
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.35"); }
+ OID oid_of() const override { return static_oid(); }
private:
std::string oid_name() const override
@@ -280,52 +388,59 @@ class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extensio
};
/**
-* Alternative Name Extension Base Class
+* Subject Alternative Name Extension
*/
-class BOTAN_PUBLIC_API(2,0) Alternative_Name : public Certificate_Extension
+class BOTAN_PUBLIC_API(2,4) Subject_Alternative_Name final : public Certificate_Extension
{
public:
- AlternativeName get_alt_name() const { return m_alt_name; }
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
- protected:
- Alternative_Name(const AlternativeName&, const std::string& oid_name);
+ static OID static_oid() { return OID("2.5.29.17"); }
+ OID oid_of() const override { return static_oid(); }
+
+ Subject_Alternative_Name* copy() const override
+ { return new Subject_Alternative_Name(get_alt_name()); }
- Alternative_Name(const std::string&, const std::string&);
+ explicit Subject_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
private:
- std::string oid_name() const override { return m_oid_name_str; }
+ std::string oid_name() const override { return "X509v3.SubjectAlternativeName"; }
bool should_encode() const override { return m_alt_name.has_items(); }
std::vector<uint8_t> encode_inner() const override;
void decode_inner(const std::vector<uint8_t>&) override;
void contents_to(Data_Store&, Data_Store&) const override;
- std::string m_oid_name_str;
AlternativeName m_alt_name;
};
/**
-* Subject Alternative Name Extension
+* Issuer Alternative Name Extension
*/
-class BOTAN_PUBLIC_API(2,0) Subject_Alternative_Name final : public Alternative_Name
+class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Certificate_Extension
{
public:
- Subject_Alternative_Name* copy() const override
- { return new Subject_Alternative_Name(get_alt_name()); }
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
- explicit Subject_Alternative_Name(const AlternativeName& = AlternativeName());
- };
+ static OID static_oid() { return OID("2.5.29.18"); }
+ OID oid_of() const override { return static_oid(); }
-/**
-* Issuer Alternative Name Extension
-*/
-class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Alternative_Name
- {
- public:
Issuer_Alternative_Name* copy() const override
{ return new Issuer_Alternative_Name(get_alt_name()); }
- explicit Issuer_Alternative_Name(const AlternativeName& = AlternativeName());
+ explicit Issuer_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
+
+ private:
+ std::string oid_name() const override { return "X509v3.IssuerAlternativeName"; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ AlternativeName m_alt_name;
};
/**
@@ -340,11 +455,13 @@ class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extens
Extended_Key_Usage() = default;
explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
- std::vector<OID> get_oids() const { return m_oids; }
+ const std::vector<OID>& get_oids() const { return m_oids; }
+
+ static OID static_oid() { return OID("2.5.29.37"); }
+ OID oid_of() const override { return static_oid(); }
private:
- std::string oid_name() const override
- { return "X509v3.ExtendedKeyUsage"; }
+ std::string oid_name() const override { return "X509v3.ExtendedKeyUsage"; }
bool should_encode() const override { return (m_oids.size() > 0); }
std::vector<uint8_t> encode_inner() const override;
@@ -371,6 +488,11 @@ class BOTAN_PUBLIC_API(2,0) Name_Constraints final : public Certificate_Extensio
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos) override;
+ const NameConstraints& get_name_constraints() const { return m_name_constraints; }
+
+ static OID static_oid() { return OID("2.5.29.30"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override
{ return "X509v3.NameConstraints"; }
@@ -395,8 +517,14 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Policies final : public Certificate_Exte
Certificate_Policies() = default;
explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
+ BOTAN_DEPRECATED("Use get_policy_oids")
std::vector<OID> get_oids() const { return m_oids; }
+ const std::vector<OID>& get_policy_oids() const { return m_oids; }
+
+ static OID static_oid() { return OID("2.5.29.32"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override
{ return "X509v3.CertificatePolicies"; }
@@ -420,6 +548,11 @@ class BOTAN_PUBLIC_API(2,0) Authority_Information_Access final : public Certific
explicit Authority_Information_Access(const std::string& ocsp) :
m_ocsp_responder(ocsp) {}
+ std::string ocsp_responder() const { return m_ocsp_responder; }
+
+ static OID static_oid() { return OID("1.3.6.1.5.5.7.1.1"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override
{ return "PKIX.AuthorityInformationAccess"; }
@@ -447,6 +580,9 @@ class BOTAN_PUBLIC_API(2,0) CRL_Number final : public Certificate_Extension
size_t get_crl_number() const;
+ static OID static_oid() { return OID("2.5.29.20"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override { return "X509v3.CRLNumber"; }
@@ -472,6 +608,9 @@ class BOTAN_PUBLIC_API(2,0) CRL_ReasonCode final : public Certificate_Extension
CRL_Code get_reason() const { return m_reason; }
+ static OID static_oid() { return OID("2.5.29.21"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override { return "X509v3.ReasonCode"; }
@@ -508,9 +647,15 @@ class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_E
explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) :
m_distribution_points(points) {}
- std::vector<Distribution_Point> distribution_points() const
+ const std::vector<Distribution_Point>& distribution_points() const
{ return m_distribution_points; }
+ const std::vector<std::string>& crl_distribution_urls() const
+ { return m_crl_distribution_urls; }
+
+ static OID static_oid() { return OID("2.5.29.31"); }
+ OID oid_of() const override { return static_oid(); }
+
private:
std::string oid_name() const override
{ return "X509v3.CRLDistributionPoints"; }
@@ -523,44 +668,65 @@ class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_E
void contents_to(Data_Store&, Data_Store&) const override;
std::vector<Distribution_Point> m_distribution_points;
+ std::vector<std::string> m_crl_distribution_urls;
};
/**
-* An unknown X.509 extension marked as critical
-* Will always add a failure to the path validation result.
+* An unknown X.509 extension
+* Will add a failure to the path validation result, if critical
*/
-class BOTAN_PUBLIC_API(2,0) Unknown_Critical_Extension final : public Certificate_Extension
+class BOTAN_PUBLIC_API(2,4) Unknown_Extension final : public Certificate_Extension
{
public:
- explicit Unknown_Critical_Extension(OID oid) : m_oid(oid) {}
+ Unknown_Extension(const OID& oid, bool critical) :
+ m_oid(oid), m_critical(critical) {}
- Unknown_Critical_Extension* copy() const override
- { return new Unknown_Critical_Extension(m_oid); }
+ Unknown_Extension* copy() const override
+ { return new Unknown_Extension(m_oid, m_critical); }
+ /**
+ * Return the OID of this unknown extension
+ */
OID oid_of() const override
{ return m_oid; }
+ //static_oid not defined for Unknown_Extension
+
+ /**
+ * Return the extension contents
+ */
+ const std::vector<uint8_t>& extension_contents() const { return m_bytes; }
+
+ /**
+ * Return if this extension was marked critical
+ */
+ bool is_critical_extension() const { return m_critical; }
+
void validate(const X509_Certificate&, const X509_Certificate&,
const std::vector<std::shared_ptr<const X509_Certificate>>&,
std::vector<std::set<Certificate_Status_Code>>& cert_status,
size_t pos) override
{
- cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+ if(m_critical)
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+ }
}
private:
- std::string oid_name() const override
- { return "Unknown OID name"; }
+ std::string oid_name() const override { return ""; }
- bool should_encode() const override { return false; }
+ bool should_encode() const override { return true; }
std::vector<uint8_t> encode_inner() const override;
void decode_inner(const std::vector<uint8_t>&) override;
void contents_to(Data_Store&, Data_Store&) const override;
OID m_oid;
+ bool m_critical;
+ std::vector<uint8_t> m_bytes;
};
-}
+ }
}
diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp
index f566be00e..dad27d6ff 100644
--- a/src/lib/x509/x509_obj.cpp
+++ b/src/lib/x509/x509_obj.cpp
@@ -299,13 +299,11 @@ void X509_Object::do_decode()
}
catch(Decoding_Error& e)
{
- throw Decoding_Error(m_PEM_label_pref + " decoding failed (" +
- e.what() + ")");
+ throw Decoding_Error(m_PEM_label_pref + " decoding failed", e.what());
}
catch(Invalid_Argument& e)
{
- throw Decoding_Error(m_PEM_label_pref + " decoding failed (" +
- e.what() + ")");
+ throw Decoding_Error(m_PEM_label_pref + " decoding failed", e.what());
}
}
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 5a6588ecc..6814f54c1 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,259 @@ 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)
+ {
+ 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 +438,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 +490,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 +504,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 +720,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 +754,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 +803,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 +814,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
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
index fcc5bf0ba..11bcdbb12 100644
--- a/src/lib/x509/x509path.cpp
+++ b/src/lib/x509/x509path.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/x509path.h>
+#include <botan/x509_ext.h>
#include <botan/pk_keys.h>
#include <botan/ocsp.h>
#include <algorithm>
@@ -46,6 +47,20 @@ PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& ce
if(!cert_path[0]->allowed_usage(usage))
cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ if(cert_path[0]->is_CA_cert() == false &&
+ cert_path[0]->has_constraints(KEY_CERT_SIGN))
+ {
+ /*
+ "If the keyCertSign bit is asserted, then the cA bit in the
+ basic constraints extension (Section 4.2.1.9) MUST also be
+ asserted." - RFC 5280
+
+ We don't bother doing this check on the rest of the path since they
+ must have the cA bit asserted or the validation will fail anyway.
+ */
+ cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ }
+
for(size_t i = 0; i != cert_path.size(); ++i)
{
std::set<Certificate_Status_Code>& status = cert_status.at(i);
@@ -67,10 +82,10 @@ PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& ce
}
// Check all certs for valid time range
- if(validation_time < X509_Time(subject->start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ if(validation_time < subject->not_before())
status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
- if(validation_time > X509_Time(subject->end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ if(validation_time > subject->not_after())
status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
// Check issuer constraints
@@ -495,7 +510,9 @@ PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>
const std::string fprint = issuer->fingerprint("SHA-256");
if(certs_seen.count(fprint) > 0) // already seen?
+ {
return Certificate_Status_Code::CERT_CHAIN_LOOP;
+ }
certs_seen.insert(fprint);
cert_path.push_back(issuer);
diff --git a/src/tests/data/misc_certs/opcuactt_ca.der b/src/tests/data/misc_certs/opcuactt_ca.der
new file mode 100644
index 000000000..35e8e5cb0
--- /dev/null
+++ b/src/tests/data/misc_certs/opcuactt_ca.der
Binary files differ
diff --git a/src/tests/data/misc_certs/opcuactt_ca.pem b/src/tests/data/misc_certs/opcuactt_ca.pem
new file mode 100644
index 000000000..1987a0a07
--- /dev/null
+++ b/src/tests/data/misc_certs/opcuactt_ca.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMAXkiTXQ8mQnY
+n+99br/MU9dRvUzliOZRu0owQnAgTtH2Rl+Q9icXQyhcowaiCv6DFrvPlR1MKMux
+kJngiOYLI10gLgXjiO4pd53IPNa60+6j4Xgobl7lfCGnSwbYVE8OYl0MyjfrC8Ca
+OmbYYVLvgBSW2fPyBHrucCBTB+f4FT+Wuu473hmNHrRx8WT9H9GjRcUQ+RkDqI8S
+1ydcQdGu6BWTHCvvRtdxvDk+4WzBdtgoSkGeFBZa3BAnsHTMiKHzMogeqNUGPHVJ
+hBobqxLha1ztkRvaIByuidNN1lZAhnonkFNU5QAykPqSAaem1RwPi6xA5VQUxCoz
+LmZBvubHAgMBAAECggEBAJtOM82tcWQAjcJgX436FgGTgkQz/KpxggWOs3fx3DJ8
+TtNR18cf3bqT4dJEOfR6si9Ry0DIoDkuhYN0NfD9x0OLdIXfA5So1cazzWZ3CnHI
+jdAtAbCl9ZB245fcQiXGaTjmIThvagqCM+o1s73euaiitQnyewgv6PZEXhdX2Xy+
+Mqm1gXi0edyegSdjVRYd1vfx0S52R1TfoLWUy471VlOsKTo+ukUARryB36VA8iNh
+0Wn2419zMyjt1ocOIU0RGFFag7wjZ7rGhv+p9Bb92gBrKkbC/2UWgJRpO4OZ17zX
+R3AajPW30e4iUYgFqvDpEhi48N9G7zAxia1FWF0VR8ECgYEA/KVK6trcbxy8Ohqd
+hBQUkElCAnBvkIY+OStAieLMppVEvQfMgTWiOCzub4hiGKkPZUK9ibmQgDzcXbZy
+bB1cXVK6nwAEfABTYzifu2ADJCDOBd3B1PXW2waLHuMVI0CEnySmADDUJ5+LmZm/
+um6j4iGcJIGZjGNVMQTdTampcs8CgYEAzrbcOMBaQZMW5fDD21x/tGwiNmyIBpNC
+M865xHjRHFvGdVkWoIvm2wLV5O7AZMzqc5h6sOxCTj2E4dlWnshn0pTBR48epyXl
+cv287G2czafQsttyEJq83InV3JlwycyOy2/EJOCuL/T2O8nGJlNTTUEQbC6Udu8v
+/r/HGkO9qokCgYB0LaqCzzwY2FTyPzT5/KXsJ9Pz/TJAeor4jRwzjBjh7bhbWM/B
+ByHexUKsBUJe5rdOsF8qiyuY3OPVMEXz05iazaVF4qMtRpUSBoLljmRDY9Z5uh0d
+SiOQOrUU8gXRXSTfbeHsKogU5Hg0nRAesiwom54K55HtjewqC3uc8A0c3wKBgQDC
+XNNiFRKIN7o/CAvQFQAKb+YXUCLyM8H6nnSzFHph9LT8n4CUAhdVdCwTrp196eLE
+P+mUswCBOnzYMpesgniEWtQE6cADn7FHVuctUr8t641irs1oaWYM4xkP68JOLCVT
+iUpe9lcxxl1DyCuk25ImwHelkIKN9cYl/MJDotASKQKBgFMX48y7qyqYTZUnXHza
+g8yVwpU3DaynPNd7IHwC2/Nj1jZW79Tg8YHzKxujvHSByyqt7UOY8oMTdywzqAh5
+JH3Z9JfPKFTp18cNk8Z0vhPnNV5lQPPXKaWsW7YW62AuDYTX/CwL9+Z+sCIQ/WBz
+MMcuPWIKk0t5acScslBtLdk3
+-----END PRIVATE KEY-----
diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp
index 7b4f749fb..1ae7f5527 100644
--- a/src/tests/test_pkcs11_high_level.cpp
+++ b/src/tests/test_pkcs11_high_level.cpp
@@ -47,6 +47,7 @@
#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_PKCS11)
#include <botan/p11_x509.h>
+ #include <botan/x509_dn.h>
#endif
#if defined(BOTAN_HAS_HMAC_DRBG)
diff --git a/src/tests/test_x509_dn.cpp b/src/tests/test_x509_dn.cpp
index 55cf05c1c..74803909b 100644
--- a/src/tests/test_x509_dn.cpp
+++ b/src/tests/test_x509_dn.cpp
@@ -6,14 +6,14 @@
#include "tests.h"
-#if defined(BOTAN_HAS_ASN1)
+#if defined(BOTAN_HAS_CERTIFICATES)
#include <botan/x509_dn.h>
#include <botan/ber_dec.h>
#endif
namespace Botan_Tests {
-#if defined(BOTAN_HAS_ASN1)
+#if defined(BOTAN_HAS_CERTIFICATES)
class X509_DN_Comparisons_Tests final : public Text_Based_Test
{
public:
diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp
index cb0895369..c1d8a5b17 100644
--- a/src/tests/test_x509_path.cpp
+++ b/src/tests/test_x509_path.cpp
@@ -182,55 +182,63 @@ std::vector<Test::Result> NIST_Path_Validation_Tests::run()
for(auto i = expected.begin(); i != expected.end(); ++i)
{
- const std::string test_name = i->first;
- const std::string expected_result = i->second;
-
- const std::string test_dir = nist_test_dir + "/" + test_name;
-
Test::Result result("NIST path validation");
result.start_timer();
- const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
+ const std::string test_name = i->first;
- if(all_files.empty())
+ try
{
- result.test_failure("No test files found in " + test_dir);
- results.push_back(result);
- continue;
- }
+ const std::string expected_result = i->second;
- Botan::Certificate_Store_In_Memory store;
+ const std::string test_dir = nist_test_dir + "/" + test_name;
- store.add_certificate(root_cert);
- store.add_crl(root_crl);
+ const std::vector<std::string> all_files = Botan::get_files_recursive(test_dir);
- for(auto const& file : all_files)
- {
- if(file.find(".crt") != std::string::npos && file != "end.crt")
+ if(all_files.empty())
{
- store.add_certificate(Botan::X509_Certificate(file));
+ result.test_failure("No test files found in " + test_dir);
+ results.push_back(result);
+ continue;
}
- else if(file.find(".crl") != std::string::npos)
+
+ Botan::Certificate_Store_In_Memory store;
+
+ store.add_certificate(root_cert);
+ store.add_crl(root_crl);
+
+ for(auto const& file : all_files)
{
- Botan::DataSource_Stream in(file, true);
- Botan::X509_CRL crl(in);
- store.add_crl(crl);
+ if(file.find(".crt") != std::string::npos && file != "end.crt")
+ {
+ store.add_certificate(Botan::X509_Certificate(file));
+ }
+ else if(file.find(".crl") != std::string::npos)
+ {
+ Botan::DataSource_Stream in(file, true);
+ Botan::X509_CRL crl(in);
+ store.add_crl(crl);
+ }
}
- }
- Botan::X509_Certificate end_user(test_dir + "/end.crt");
+ Botan::X509_Certificate end_user(test_dir + "/end.crt");
- // 1024 bit root cert
- Botan::Path_Validation_Restrictions restrictions(true, 80);
+ // 1024 bit root cert
+ Botan::Path_Validation_Restrictions restrictions(true, 80);
- Botan::Path_Validation_Result validation_result =
- Botan::x509_path_validate(end_user,
- restrictions,
- store);
+ Botan::Path_Validation_Result validation_result =
+ Botan::x509_path_validate(end_user,
+ restrictions,
+ store);
- result.test_eq(test_name + " path validation result",
- validation_result.result_string(),
- expected_result);
+ result.test_eq(test_name + " path validation result",
+ validation_result.result_string(),
+ expected_result);
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure(test_name, e.what());
+ }
result.end_timer();
results.push_back(result);
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index d635f7fe1..d2156cf60 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -11,6 +11,7 @@
#include <botan/calendar.h>
#include <botan/pkcs10.h>
+ #include <botan/pkcs8.h>
#include <botan/x509self.h>
#include <botan/x509path.h>
#include <botan/x509_ca.h>
@@ -358,6 +359,30 @@ Test::Result test_x509_dates()
return result;
}
+Test::Result test_crl_dn_name()
+ {
+ Test::Result result("CRL DN name");
+
+ // See GH #1252
+
+ const Botan::OID dc_oid("0.9.2342.19200300.100.1.25");
+
+ Botan::X509_Certificate cert(Test::data_file("misc_certs/opcuactt_ca.der"));
+
+ Botan::DataSource_Stream key_input(Test::data_file("misc_certs/opcuactt_ca.pem"));
+ std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(key_input);
+ Botan::X509_CA ca(cert, *key, "SHA-256", Test::rng());
+
+ Botan::X509_CRL crl = ca.new_crl(Test::rng());
+
+ result.confirm("matches issuer cert", crl.issuer_dn() == cert.subject_dn());
+
+ result.confirm("contains DC component",
+ crl.issuer_dn().get_attributes().count(dc_oid) == 1);
+
+ return result;
+ }
+
Test::Result test_x509_utf8()
{
Test::Result result("X509 with UTF-8 encoded fields");
@@ -379,10 +404,12 @@ Test::Result test_x509_utf8()
const std::string location =
"\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
- result.test_eq("O", utf8_cert.issuer_info("O").at(0), organization);
- result.test_eq("OU", utf8_cert.issuer_info("OU").at(0), organization_unit);
- result.test_eq("CN", utf8_cert.issuer_info("CN").at(0), common_name);
- result.test_eq("L", utf8_cert.issuer_info("L").at(0), location);
+ const Botan::X509_DN& issuer_dn = utf8_cert.issuer_dn();
+
+ result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
+ result.test_eq("OU", issuer_dn.get_first_attribute("OU"), organization_unit);
+ result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
+ result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
}
catch (const Botan::Decoding_Error &ex)
{
@@ -409,9 +436,11 @@ Test::Result test_x509_bmpstring()
// UTF-8 encoded fields of test certificate (contains only ASCII characters)
const std::string location = "Berlin";
- result.test_eq("O", ucs2_cert.issuer_info("O").at(0), organization);
- result.test_eq("CN", ucs2_cert.issuer_info("CN").at(0), common_name);
- result.test_eq("L", ucs2_cert.issuer_info("L").at(0), location);
+ const Botan::X509_DN& issuer_dn = ucs2_cert.issuer_dn();
+
+ result.test_eq("O", issuer_dn.get_first_attribute("O"), organization);
+ result.test_eq("CN", issuer_dn.get_first_attribute("CN"), common_name);
+ result.test_eq("L", issuer_dn.get_first_attribute("L"), location);
}
catch (const Botan::Decoding_Error &ex)
{
@@ -503,10 +532,16 @@ Test::Result test_x509_cert(const std::string& sig_algo, const std::string& hash
/* Get cert data */
result.test_eq("x509 version", user1_cert.x509_version(), size_t(3));
- result.test_eq("issuer info CN", user1_cert.issuer_info("CN").at(0), ca_opts().common_name);
- result.test_eq("issuer info Country", user1_cert.issuer_info("C").at(0), ca_opts().country);
- result.test_eq("issuer info Orga", user1_cert.issuer_info("O").at(0), ca_opts().organization);
- result.test_eq("issuer info OrgaUnit", user1_cert.issuer_info("OU").at(0), ca_opts().org_unit);
+ const Botan::X509_DN& user1_issuer_dn = user1_cert.issuer_dn();
+ result.test_eq("issuer info CN", user1_issuer_dn.get_first_attribute("CN"), ca_opts().common_name);
+ result.test_eq("issuer info Country", user1_issuer_dn.get_first_attribute("C"), ca_opts().country);
+ result.test_eq("issuer info Orga", user1_issuer_dn.get_first_attribute("O"), ca_opts().organization);
+ result.test_eq("issuer info OrgaUnit", user1_issuer_dn.get_first_attribute("OU"), ca_opts().org_unit);
+
+ const Botan::AlternativeName& user1_altname = user1_cert.subject_alt_name();
+ result.test_eq("subject alt email", user1_altname.get_first_attribute("RFC822"), "[email protected]");
+ result.test_eq("subject alt email", user1_altname.get_first_attribute("DNS"), "botan.randombit.net");
+ result.test_eq("subject alt email", user1_altname.get_first_attribute("URI"), "https://botan.randombit.net");
const Botan::X509_CRL crl1 = ca.new_crl(Test::rng());
@@ -952,6 +987,12 @@ class String_Extension final : public Botan::Certificate_Extension
{
return m_oid;
}
+
+ bool should_encode() const override
+ {
+ return true;
+ }
+
std::string oid_name() const override
{
return "String Extension";
@@ -1005,16 +1046,19 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
// include a custom extension in the request
Botan::Extensions req_extensions;
- Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
- req_extensions.add(new String_Extension("1Test"), false);
+ const Botan::OID oid("1.2.3.4.5.6.7.8.9.1");
+ const Botan::OID ku_oid = Botan::OIDS::lookup("X509v3.KeyUsage");
+ req_extensions.add(new String_Extension("AAAAAAAAAAAAAABCDEF"), false);
opts.extensions = req_extensions;
/* Create a self-signed certificate */
const Botan::X509_Certificate self_signed_cert = Botan::X509::create_self_signed_cert(
opts, *user_key, hash_fn, Test::rng());
+ result.confirm("Extensions::extension_set true for Key_Usage", self_signed_cert.v3_extensions().extension_set(ku_oid));
+
// check if known Key_Usage extension is present in self-signed cert
- auto key_usage_ext = self_signed_cert.v3_extensions().get(Botan::OIDS::lookup("X509v3.KeyUsage"));
+ auto key_usage_ext = self_signed_cert.v3_extensions().get(ku_oid);
if(result.confirm("Key_Usage extension present in self-signed certificate", key_usage_ext != nullptr))
{
result.confirm("Key_Usage extension value matches in self-signed certificate",
@@ -1025,29 +1069,34 @@ Test::Result test_x509_extensions(const std::string& sig_algo, const std::string
auto string_ext = self_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
if(result.confirm("Custom extension present in self-signed certificate", string_ext != nullptr))
{
- result.test_eq("Custom extension value matches in self-signed certificate", string_ext->value(), "1Test");
+ result.test_eq("Custom extension value matches in self-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
}
const Botan::PKCS10_Request user_req = Botan::X509::create_cert_req(opts, *user_key, hash_fn, Test::rng());
/* Create a CA-signed certificate */
- const Botan::X509_Certificate user_cert = ca.sign_request(
- user_req, Test::rng(), from_date(2008, 01, 01), from_date(2033, 01, 01));
+ const Botan::X509_Certificate ca_signed_cert =
+ ca.sign_request(user_req, Test::rng(),
+ from_date(2008, 01, 01),
+ from_date(2033, 01, 01));
// check if known Key_Usage extension is present in CA-signed cert
- key_usage_ext = self_signed_cert.v3_extensions().get(Botan::OIDS::lookup("X509v3.KeyUsage"));
- if(result.confirm("Key_Usage extension present in user certificate", key_usage_ext != nullptr))
+ result.confirm("Extensions::extension_set true for Key_Usage", ca_signed_cert.v3_extensions().extension_set(ku_oid));
+
+ key_usage_ext = ca_signed_cert.v3_extensions().get(ku_oid);
+ if(result.confirm("Key_Usage extension present in CA-signed certificate", key_usage_ext != nullptr))
{
result.confirm("Key_Usage extension value matches in user certificate",
dynamic_cast<Botan::Cert_Extension::Key_Usage&>(*key_usage_ext).get_constraints() == Botan::DIGITAL_SIGNATURE);
}
// check if custom extension is present in CA-signed cert
- string_ext = user_cert.v3_extensions().get_raw<String_Extension>(oid);
- if(result.confirm("Custom extension present in user certificate", string_ext != nullptr))
+ result.confirm("Extensions::extension_set true for String_Extension", ca_signed_cert.v3_extensions().extension_set(oid));
+ string_ext = ca_signed_cert.v3_extensions().get_raw<String_Extension>(oid);
+ if(result.confirm("Custom extension present in CA-signed certificate", string_ext != nullptr))
{
- result.test_eq("Custom extension value matches in user certificate", string_ext->value(), "1Test");
+ result.test_eq("Custom extension value matches in CA-signed certificate", string_ext->value(), "AAAAAAAAAAAAAABCDEF");
}
return result;
@@ -1200,6 +1249,7 @@ class X509_Cert_Unit_Tests final : public Test
results.push_back(test_hashes("ECDSA"));
results.push_back(test_x509_utf8());
results.push_back(test_x509_bmpstring());
+ results.push_back(test_crl_dn_name());
return results;
}