aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/x509
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/x509')
-rw-r--r--src/lib/x509/cert_status.h63
-rw-r--r--src/lib/x509/certstor.cpp152
-rw-r--r--src/lib/x509/certstor.h134
-rw-r--r--src/lib/x509/certstor_sql/certstor_sql.cpp301
-rw-r--r--src/lib/x509/certstor_sql/certstor_sql.h104
-rw-r--r--src/lib/x509/certstor_sql/info.txt5
-rw-r--r--src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp19
-rw-r--r--src/lib/x509/certstor_sqlite3/certstor_sqlite.h34
-rw-r--r--src/lib/x509/certstor_sqlite3/info.txt6
-rw-r--r--src/lib/x509/crl_ent.cpp104
-rw-r--r--src/lib/x509/crl_ent.h98
-rw-r--r--src/lib/x509/info.txt10
-rw-r--r--src/lib/x509/key_constraint.cpp45
-rw-r--r--src/lib/x509/key_constraint.h46
-rw-r--r--src/lib/x509/name_constraint.cpp273
-rw-r--r--src/lib/x509/name_constraint.h179
-rw-r--r--src/lib/x509/ocsp.cpp251
-rw-r--r--src/lib/x509/ocsp.h113
-rw-r--r--src/lib/x509/ocsp_types.cpp119
-rw-r--r--src/lib/x509/ocsp_types.h67
-rw-r--r--src/lib/x509/pkcs10.cpp210
-rw-r--r--src/lib/x509/pkcs10.h112
-rw-r--r--src/lib/x509/x509_ca.cpp266
-rw-r--r--src/lib/x509/x509_ca.h145
-rw-r--r--src/lib/x509/x509_crl.cpp202
-rw-r--r--src/lib/x509/x509_crl.h125
-rw-r--r--src/lib/x509/x509_ext.cpp834
-rw-r--r--src/lib/x509/x509_ext.h505
-rw-r--r--src/lib/x509/x509_obj.cpp249
-rw-r--r--src/lib/x509/x509_obj.h119
-rw-r--r--src/lib/x509/x509cert.cpp672
-rw-r--r--src/lib/x509/x509cert.h330
-rw-r--r--src/lib/x509/x509opt.cpp94
-rw-r--r--src/lib/x509/x509path.cpp454
-rw-r--r--src/lib/x509/x509path.h239
-rw-r--r--src/lib/x509/x509self.cpp176
-rw-r--r--src/lib/x509/x509self.h197
37 files changed, 7052 insertions, 0 deletions
diff --git a/src/lib/x509/cert_status.h b/src/lib/x509/cert_status.h
new file mode 100644
index 000000000..b69bd1832
--- /dev/null
+++ b/src/lib/x509/cert_status.h
@@ -0,0 +1,63 @@
+/*
+* Result enums
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PATH_RESULT_H__
+#define BOTAN_X509_PATH_RESULT_H__
+
+namespace Botan {
+
+/**
+* Certificate validation status code
+*/
+enum class Certificate_Status_Code {
+ VERIFIED = 0x00000000,
+ OCSP_RESPONSE_GOOD,
+ NO_REVOCATION_DATA,
+
+ // Local policy failures
+ SIGNATURE_METHOD_TOO_WEAK = 1000,
+ UNTRUSTED_HASH,
+
+ // Time problems
+ CERT_NOT_YET_VALID = 2000,
+ CERT_HAS_EXPIRED,
+ OCSP_NOT_YET_VALID,
+ OCSP_HAS_EXPIRED,
+ CRL_NOT_YET_VALID,
+ CRL_HAS_EXPIRED,
+
+ // Chain generation problems
+ CERT_ISSUER_NOT_FOUND = 3000,
+ CANNOT_ESTABLISH_TRUST,
+
+ CERT_CHAIN_LOOP,
+
+ // Validation errors
+ POLICY_ERROR = 4000,
+ INVALID_USAGE,
+ CERT_CHAIN_TOO_LONG,
+ CA_CERT_NOT_FOR_CERT_ISSUER,
+ NAME_CONSTRAINT_ERROR,
+
+ // Revocation errors
+ CA_CERT_NOT_FOR_CRL_ISSUER,
+ OCSP_CERT_NOT_LISTED,
+ OCSP_BAD_STATUS,
+
+ CERT_NAME_NOMATCH,
+
+ UNKNOWN_CRITICAL_EXTENSION,
+
+ // Hard failures
+ CERT_IS_REVOKED = 5000,
+ CRL_BAD_SIGNATURE,
+ SIGNATURE_ERROR,
+};
+
+}
+
+#endif
diff --git a/src/lib/x509/certstor.cpp b/src/lib/x509/certstor.cpp
new file mode 100644
index 000000000..24cd84de7
--- /dev/null
+++ b/src/lib/x509/certstor.cpp
@@ -0,0 +1,152 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor.h>
+#include <botan/internal/filesystem.h>
+
+namespace Botan {
+
+std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
+ {
+ return std::shared_ptr<const X509_CRL>();
+ }
+
+void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert)
+ {
+ for(size_t i = 0; i != m_certs.size(); ++i)
+ {
+ if(*m_certs[i] == cert)
+ return;
+ }
+
+ m_certs.push_back(std::make_shared<X509_Certificate>(cert));
+ }
+
+std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const
+ {
+ std::vector<X509_DN> subjects;
+ for(size_t i = 0; i != m_certs.size(); ++i)
+ subjects.push_back(m_certs[i]->subject_dn());
+ return subjects;
+ }
+
+namespace {
+
+template<typename T>
+std::shared_ptr<const X509_Certificate>
+cert_search(const X509_DN& subject_dn, const std::vector<byte>& key_id,
+ const std::vector<std::shared_ptr<T>>& certs)
+ {
+ for(size_t i = 0; i != certs.size(); ++i)
+ {
+ // Only compare key ids if set in both call and in the cert
+ if(key_id.size())
+ {
+ std::vector<byte> skid = certs[i]->subject_key_id();
+
+ if(skid.size() && skid != key_id) // no match
+ continue;
+ }
+
+ if(certs[i]->subject_dn() == subject_dn)
+ return certs[i];
+ }
+
+ return std::shared_ptr<const X509_Certificate>();
+ }
+
+}
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
+ const std::vector<byte>& key_id) const
+ {
+ return cert_search(subject_dn, key_id, m_certs);
+ }
+
+void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl)
+ {
+ X509_DN crl_issuer = crl.issuer_dn();
+
+ for(size_t i = 0; i != m_crls.size(); ++i)
+ {
+ // Found an update of a previously existing one; replace it
+ if(m_crls[i]->issuer_dn() == crl_issuer)
+ {
+ if(m_crls[i]->this_update() <= crl.this_update())
+ m_crls[i] = std::make_shared<X509_CRL>(crl);
+ return;
+ }
+ }
+
+ // Totally new CRL, add to the list
+ m_crls.push_back(std::make_shared<X509_CRL>(crl));
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
+ {
+ const std::vector<byte>& key_id = subject.authority_key_id();
+
+ for(size_t i = 0; i != m_crls.size(); ++i)
+ {
+ // Only compare key ids if set in both call and in the CRL
+ if(key_id.size())
+ {
+ std::vector<byte> akid = m_crls[i]->authority_key_id();
+
+ if(akid.size() && akid != key_id) // no match
+ continue;
+ }
+
+ if(m_crls[i]->issuer_dn() == subject.issuer_dn())
+ return m_crls[i];
+ }
+
+ return std::shared_ptr<const X509_CRL>();
+ }
+
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
+ {
+ add_certificate(cert);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
+ {
+ if(dir.empty())
+ return;
+
+ std::vector<std::string> maybe_certs = get_files_recursive(dir);
+ for(auto&& cert_file : maybe_certs)
+ {
+ try
+ {
+ m_certs.push_back(std::make_shared<X509_Certificate>(cert_file));
+ }
+ catch(std::exception&)
+ {
+ }
+ }
+ }
+#endif
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_Overlay::find_cert(const X509_DN& subject_dn,
+ const std::vector<byte>& key_id) const
+ {
+ return cert_search(subject_dn, key_id, m_certs);
+ }
+
+std::vector<X509_DN> Certificate_Store_Overlay::all_subjects() const
+ {
+ std::vector<X509_DN> subjects;
+ for(size_t i = 0; i != m_certs.size(); ++i)
+ subjects.push_back(m_certs[i]->subject_dn());
+ return subjects;
+ }
+
+}
diff --git a/src/lib/x509/certstor.h b/src/lib/x509/certstor.h
new file mode 100644
index 000000000..56176739b
--- /dev/null
+++ b/src/lib/x509/certstor.h
@@ -0,0 +1,134 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_H__
+#define BOTAN_CERT_STORE_H__
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+
+namespace Botan {
+
+/**
+* Certificate Store Interface
+*/
+class BOTAN_DLL Certificate_Store
+ {
+ public:
+ virtual ~Certificate_Store() {}
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @param subject_dn the subject's distinguished name
+ * @param key_id an optional key id
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const = 0;
+
+ /**
+ * Finds a CRL for the given certificate
+ * @param subject the subject certificate
+ * @return the CRL for subject or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const;
+
+ /**
+ * @return whether the certificate is known
+ * @param cert certififcate to be searched
+ */
+ bool certificate_known(const X509_Certificate& cert) const
+ {
+ return find_cert(cert.subject_dn(), cert.subject_key_id()) != nullptr;
+ }
+
+ // remove this (used by TLS::Server)
+ virtual std::vector<X509_DN> all_subjects() const = 0;
+ };
+
+/**
+* In Memory Certificate Store
+*/
+class BOTAN_DLL Certificate_Store_In_Memory : public Certificate_Store
+ {
+ public:
+ /**
+ * Attempt to parse all files in dir (including subdirectories)
+ * as certificates. Ignores errors.
+ */
+ explicit Certificate_Store_In_Memory(const std::string& dir);
+
+ /**
+ * Adds given certificate to the store.
+ */
+ explicit Certificate_Store_In_Memory(const X509_Certificate& cert);
+
+ /**
+ * Create an empty store.
+ */
+ Certificate_Store_In_Memory() {}
+
+ /**
+ * Add a certificate to the store.
+ * @param cert certificate to be added
+ */
+ void add_certificate(const X509_Certificate& cert);
+
+ /**
+ * Add a certificate revocation list (CRL) to the store.
+ * @param crl CRL to be added
+ */
+ void add_crl(const X509_CRL& crl);
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /*
+ * Find a certificate by Subject DN and (optionally) key identifier
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<byte>& key_id) const override;
+
+ /**
+ * Finds a CRL for the given certificate
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+ private:
+ // TODO: Add indexing on the DN and key id to avoid linear search
+ std::vector<std::shared_ptr<X509_Certificate>> m_certs;
+ std::vector<std::shared_ptr<X509_CRL>> m_crls;
+ };
+
+/**
+* FIXME add doc
+*/
+class BOTAN_DLL Certificate_Store_Overlay : public Certificate_Store
+ {
+ public:
+ explicit Certificate_Store_Overlay(const std::vector<std::shared_ptr<const X509_Certificate>>& certs) :
+ m_certs(certs) {}
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<byte>& key_id) const override;
+ private:
+ const std::vector<std::shared_ptr<const X509_Certificate>>& m_certs;
+ };
+
+}
+#endif
diff --git a/src/lib/x509/certstor_sql/certstor_sql.cpp b/src/lib/x509/certstor_sql/certstor_sql.cpp
new file mode 100644
index 000000000..dfb8c5d78
--- /dev/null
+++ b/src/lib/x509/certstor_sql/certstor_sql.cpp
@@ -0,0 +1,301 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sql.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/internal/filesystem.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ m_rng(rng),
+ m_database(db),
+ m_prefix(table_prefix),
+ m_password(passwd)
+ {
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " +
+ m_prefix + "certificates ( \
+ fingerprint BLOB PRIMARY KEY, \
+ subject_dn BLOB, \
+ key_id BLOB, \
+ priv_fingerprint BLOB, \
+ certificate BLOB UNIQUE NOT NULL\
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "keys (\
+ fingerprint BLOB PRIMARY KEY, \
+ key BLOB UNIQUE NOT NULL \
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "revoked (\
+ fingerprint BLOB PRIMARY KEY, \
+ reason BLOB NOT NULL, \
+ time BLOB NOT NULL \
+ )");
+ }
+
+// Certificate handling
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const
+ {
+ DER_Encoder enc;
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ subject_dn.encode_into(enc);
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1");
+ stmt->bind(1,enc.get_contents_unlocked());
+ }
+ else
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+ subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
+ stmt->bind(1,enc.get_contents_unlocked());
+ stmt->bind(2,key_id);
+ }
+
+ std::shared_ptr<const X509_Certificate> cert;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ cert = std::make_shared<X509_Certificate>(
+ std::vector<byte>(blob.first,blob.first + blob.second));
+
+ }
+
+ return cert;
+ }
+
+std::shared_ptr<const X509_CRL>
+Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const
+ {
+ auto all_crls = generate_crls();
+
+ for(auto crl: all_crls)
+ {
+ if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn())
+ return std::shared_ptr<X509_CRL>(new X509_CRL(crl));
+ }
+
+ return std::shared_ptr<X509_CRL>();
+ }
+
+std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const
+ {
+ std::vector<X509_DN> ret;
+ auto stmt = m_database->new_statement("SELECT subject_dn FROM " + m_prefix + "certificates");
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ BER_Decoder dec(blob.first,blob.second);
+ X509_DN dn;
+
+ dn.decode_from(dec);
+
+ ret.push_back(dn);
+ }
+
+ return ret;
+ }
+
+bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert)
+ {
+ if(find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ DER_Encoder enc;
+ auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " +
+ m_prefix + "certificates (\
+ fingerprint, \
+ subject_dn, \
+ key_id, \
+ priv_fingerprint, \
+ certificate \
+ ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ cert.subject_dn().encode_into(enc);
+ stmt->bind(2,enc.get_contents_unlocked());
+ stmt->bind(3,cert.subject_key_id());
+ stmt->bind(4,std::vector<byte>());
+ enc = DER_Encoder();
+ cert.encode_into(enc);
+ stmt->bind(5,enc.get_contents_unlocked());
+ stmt->spin();
+
+ return true;
+ }
+
+
+bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert)
+ {
+ if(!find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+
+ return true;
+ }
+
+// Private key handling
+std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const
+ {
+ auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix + "keys "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "keys.fingerprint == " + m_prefix + "certificates.priv_fingerprint "
+ "WHERE " + m_prefix + "certificates.fingerprint == ?1");
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+
+ std::shared_ptr<const Private_Key> key;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ DataSource_Memory src(blob.first,blob.second);
+ key.reset(PKCS8::load_key(src, m_rng, m_password));
+ }
+
+ return key;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const
+ {
+ auto fpr = key.fingerprint("SHA-256");
+ auto stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE priv_fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+
+ std::vector<std::shared_ptr<const X509_Certificate>> certs;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ certs.push_back(std::make_shared<X509_Certificate>(
+ std::vector<byte>(blob.first,blob.first + blob.second)));
+ }
+
+ return certs;
+ }
+
+bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
+ insert_cert(cert);
+
+ if(find_key(cert))
+ return false;
+
+ auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
+ auto fpr = key.fingerprint("SHA-256");
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )");
+
+ stmt1->bind(1,fpr);
+ stmt1->bind(2,pkcs8.data(),pkcs8.size());
+ stmt1->spin();
+
+ auto stmt2 = m_database->new_statement(
+ "UPDATE " + m_prefix + "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2");
+
+ stmt2->bind(1,fpr);
+ stmt2->bind(2,cert.fingerprint("SHA-256"));
+ stmt2->spin();
+
+ return true;
+ }
+
+void Certificate_Store_In_SQL::remove_key(const Private_Key& key)
+ {
+ auto fpr = key.fingerprint("SHA-256");
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+ stmt->spin();
+ }
+
+// Revocation
+void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time)
+ {
+ insert_cert(cert);
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO " + m_prefix + "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
+
+ stmt1->bind(1,cert.fingerprint("SHA-256"));
+ stmt1->bind(2,code);
+
+ if(time.time_is_set())
+ {
+ DER_Encoder der;
+ time.encode_into(der);
+ stmt1->bind(3,der.get_contents_unlocked());
+ }
+ else
+ {
+ stmt1->bind(3,-1);
+ }
+
+ stmt1->spin();
+ }
+
+void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert)
+ {
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+ }
+
+std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const
+ {
+ auto stmt = m_database->new_statement(
+ "SELECT certificate,reason,time FROM " + m_prefix + "revoked "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
+
+ std::map<X509_DN,std::vector<CRL_Entry>> crls;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ auto cert = X509_Certificate(
+ std::vector<byte>(blob.first,blob.first + blob.second));
+ auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
+ auto ent = CRL_Entry(cert,code);
+
+ auto i = crls.find(cert.issuer_dn());
+ if(i == crls.end())
+ {
+ crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent})));
+ }
+ else
+ {
+ i->second.push_back(ent);
+ }
+ }
+
+ std::vector<X509_CRL> ret;
+ X509_Time t(std::chrono::system_clock::now());
+
+ for(auto p: crls)
+ {
+ ret.push_back(X509_CRL(p.first,t,t,p.second));
+ }
+
+ return ret;
+ }
+
+}
diff --git a/src/lib/x509/certstor_sql/certstor_sql.h b/src/lib/x509/certstor_sql/certstor_sql.h
new file mode 100644
index 000000000..0025884f9
--- /dev/null
+++ b/src/lib/x509/certstor_sql/certstor_sql.h
@@ -0,0 +1,104 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQL_H__
+#define BOTAN_CERT_STORE_SQL_H__
+
+#include <botan/certstor.h>
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/database.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+
+/**
+ * Certificate and private key store backed by an SQL database.
+ */
+class BOTAN_DLL Certificate_Store_In_SQL : public Certificate_Store
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db underlying database storage
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+
+ /**
+ * Returns the first certificate with matching subject DN and optional key ID.
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<byte>& key_id) const override;
+
+ /**
+ * Returns all subject DNs known to the store instance.
+ */
+ virtual std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Inserts "cert" into the store, returns false if the certificate is
+ * already known and true if insertion was successful.
+ */
+ bool insert_cert(const X509_Certificate& cert);
+
+ /**
+ * Removes "cert" from the store. Returns false if the certificate could not
+ * be found and true if removal was successful.
+ */
+ bool remove_cert(const X509_Certificate& cert);
+
+ /// Returns the private key for "cert" or an empty shared_ptr if none was found.
+ std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const;
+
+ /// Returns all certificates for private key "key".
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_certs_for_key(const Private_Key& key) const;
+
+ /**
+ * Inserts "key" for "cert" into the store, returns false if the key is
+ * already known and true if insertion was successful.
+ */
+ bool insert_key(const X509_Certificate& cert, const Private_Key& key);
+
+ /// Removes "key" from the store.
+ void remove_key(const Private_Key& key);
+
+ /// Marks "cert" as revoked starting from "time".
+ void revoke_cert(const X509_Certificate&, CRL_Code, const X509_Time& time = X509_Time());
+
+ /// Reverses the revokation for "cert".
+ void affirm_cert(const X509_Certificate&);
+
+ /**
+ * Generates Certificate Revocation Lists for all certificates marked as revoked.
+ * A CRL is returned for each unique issuer DN.
+ */
+ std::vector<X509_CRL> generate_crls() const;
+
+ /**
+ * Generates a CRL for all certificates issued by the given issuer.
+ */
+ virtual std::shared_ptr<const X509_CRL>
+ find_crl_for(const X509_Certificate& issuer) const override;
+
+ private:
+ RandomNumberGenerator& m_rng;
+ std::shared_ptr<SQL_Database> m_database;
+ std::string m_prefix;
+ std::string m_password;
+ mutex_type m_mutex;
+ };
+
+}
+#endif
diff --git a/src/lib/x509/certstor_sql/info.txt b/src/lib/x509/certstor_sql/info.txt
new file mode 100644
index 000000000..cfdd521a2
--- /dev/null
+++ b/src/lib/x509/certstor_sql/info.txt
@@ -0,0 +1,5 @@
+define CERTSTOR_SQL 20160818
+
+<requires>
+datastor
+</requires>
diff --git a/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp b/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
new file mode 100644
index 000000000..b7c066483
--- /dev/null
+++ b/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
@@ -0,0 +1,19 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, rng, table_prefix)
+ {}
+}
diff --git a/src/lib/x509/certstor_sqlite3/certstor_sqlite.h b/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
new file mode 100644
index 000000000..d1a1e3d21
--- /dev/null
+++ b/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
@@ -0,0 +1,34 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQLITE_H__
+#define BOTAN_CERT_STORE_SQLITE_H__
+
+#include <botan/certstor_sql.h>
+
+namespace Botan {
+
+/**
+* Certificate and private key store backed by an sqlite (http://sqlite.org) database.
+*/
+class BOTAN_DLL Certificate_Store_In_SQLite : public Certificate_Store_In_SQL
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db_path path to the database file
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+ };
+}
+#endif
diff --git a/src/lib/x509/certstor_sqlite3/info.txt b/src/lib/x509/certstor_sqlite3/info.txt
new file mode 100644
index 000000000..d5e50fc95
--- /dev/null
+++ b/src/lib/x509/certstor_sqlite3/info.txt
@@ -0,0 +1,6 @@
+define CERTSTOR_SQLITE3 20160818
+
+<requires>
+certstor_sql
+sqlite3
+</requires>
diff --git a/src/lib/x509/crl_ent.cpp b/src/lib/x509/crl_ent.cpp
new file mode 100644
index 000000000..7074f0609
--- /dev/null
+++ b/src/lib/x509/crl_ent.cpp
@@ -0,0 +1,104 @@
+/*
+* CRL Entry
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/crl_ent.h>
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create a CRL_Entry
+*/
+CRL_Entry::CRL_Entry(bool t_on_unknown_crit) :
+ m_throw_on_unknown_critical(t_on_unknown_crit)
+ {
+ m_reason = UNSPECIFIED;
+ }
+
+/*
+* Create a CRL_Entry
+*/
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) :
+ m_throw_on_unknown_critical(false)
+ {
+ m_serial = cert.serial_number();
+ m_time = X509_Time(std::chrono::system_clock::now());
+ m_reason = why;
+ }
+
+/*
+* Compare two CRL_Entrys for equality
+*/
+bool operator==(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ if(a1.serial_number() != a2.serial_number())
+ return false;
+ if(a1.expire_time() != a2.expire_time())
+ return false;
+ if(a1.reason_code() != a2.reason_code())
+ return false;
+ return true;
+ }
+
+/*
+* Compare two CRL_Entrys for inequality
+*/
+bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ return !(a1 == a2);
+ }
+
+/*
+* DER encode a CRL_Entry
+*/
+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();
+ }
+
+/*
+* Decode a BER encoded CRL_Entry
+*/
+void CRL_Entry::decode_from(BER_Decoder& source)
+ {
+ BigInt serial_number_bn;
+ m_reason = UNSPECIFIED;
+
+ BER_Decoder entry = source.start_cons(SEQUENCE);
+
+ entry.decode(serial_number_bn).decode(m_time);
+
+ 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_u32bit("X509v3.CRLReasonCode"));
+ }
+
+ entry.end_cons();
+
+ m_serial = BigInt::encode(serial_number_bn);
+ }
+
+}
diff --git a/src/lib/x509/crl_ent.h b/src/lib/x509/crl_ent.h
new file mode 100644
index 000000000..6600621e5
--- /dev/null
+++ b/src/lib/x509/crl_ent.h
@@ -0,0 +1,98 @@
+/*
+* CRL Entry
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRL_ENTRY_H__
+#define BOTAN_CRL_ENTRY_H__
+
+#include <botan/asn1_time.h>
+
+namespace Botan {
+
+class X509_Certificate;
+
+/**
+* X.509v2 CRL Reason Code.
+*/
+enum CRL_Code {
+ UNSPECIFIED = 0,
+ KEY_COMPROMISE = 1,
+ CA_COMPROMISE = 2,
+ AFFILIATION_CHANGED = 3,
+ SUPERSEDED = 4,
+ CESSATION_OF_OPERATION = 5,
+ CERTIFICATE_HOLD = 6,
+ REMOVE_FROM_CRL = 8,
+ PRIVLEDGE_WITHDRAWN = 9,
+ AA_COMPROMISE = 10,
+
+ DELETE_CRL_ENTRY = 0xFF00,
+ OCSP_GOOD = 0xFF01,
+ OCSP_UNKNOWN = 0xFF02
+};
+
+/**
+* This class represents CRL entries
+*/
+class BOTAN_DLL CRL_Entry final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * Get the serial number of the certificate associated with this entry.
+ * @return certificate's serial number
+ */
+ std::vector<byte> serial_number() const { return m_serial; }
+
+ /**
+ * Get the revocation date of the certificate associated with this entry
+ * @return certificate's revocation date
+ */
+ X509_Time expire_time() const { return m_time; }
+
+ /**
+ * Get the entries reason code
+ * @return reason code
+ */
+ CRL_Code reason_code() const { return m_reason; }
+
+ /**
+ * 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
+ */
+ explicit CRL_Entry(bool throw_on_unknown_critical_extension = false);
+
+ /**
+ * Construct an CRL entry.
+ * @param cert the certificate to revoke
+ * @param reason the reason code to set in the entry
+ */
+ CRL_Entry(const X509_Certificate& cert,
+ CRL_Code reason = UNSPECIFIED);
+
+ private:
+ bool m_throw_on_unknown_critical;
+ std::vector<byte> m_serial;
+ X509_Time m_time;
+ CRL_Code m_reason;
+ };
+
+/**
+* Test two CRL entries for equality in all fields.
+*/
+BOTAN_DLL bool operator==(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* Test two CRL entries for inequality in at least one field.
+*/
+BOTAN_DLL bool operator!=(const CRL_Entry&, const CRL_Entry&);
+
+}
+
+#endif
diff --git a/src/lib/x509/info.txt b/src/lib/x509/info.txt
new file mode 100644
index 000000000..be1e879c3
--- /dev/null
+++ b/src/lib/x509/info.txt
@@ -0,0 +1,10 @@
+define X509_CERTIFICATES 20151023
+define OCSP 20131128
+
+<requires>
+asn1
+datastor
+http_util
+pubkey
+sha1
+</requires>
diff --git a/src/lib/x509/key_constraint.cpp b/src/lib/x509/key_constraint.cpp
new file mode 100644
index 000000000..30d1cb3b8
--- /dev/null
+++ b/src/lib/x509/key_constraint.cpp
@@ -0,0 +1,45 @@
+/*
+* KeyUsage
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/key_constraint.h>
+#include <botan/x509_key.h>
+
+namespace Botan {
+
+/*
+* Make sure the given key constraints are permitted for the given key type
+*/
+void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints)
+ {
+ const std::string name = pub_key.algo_name();
+
+ size_t permitted = 0;
+
+ if(name == "DH" || name == "ECDH")
+ {
+ permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY;
+ }
+
+ if(name == "RSA" || name == "ElGamal")
+ {
+ permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT;
+ }
+
+ if(name == "RSA" || name == "DSA" || name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA")
+ {
+ permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN;
+ }
+
+ if ( ( constraints & permitted ) != constraints )
+ {
+ throw Exception("Constraint not permitted for key type " + name);
+ }
+ }
+
+}
diff --git a/src/lib/x509/key_constraint.h b/src/lib/x509/key_constraint.h
new file mode 100644
index 000000000..02c65acec
--- /dev/null
+++ b/src/lib/x509/key_constraint.h
@@ -0,0 +1,46 @@
+/*
+* Enumerations
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ENUMS_H__
+#define BOTAN_ENUMS_H__
+
+#include <botan/build.h>
+
+namespace Botan {
+
+/**
+* X.509v3 Key Constraints.
+* If updating update copy in ffi.h
+*/
+enum Key_Constraints {
+ NO_CONSTRAINTS = 0,
+ DIGITAL_SIGNATURE = 1 << 15,
+ NON_REPUDIATION = 1 << 14,
+ KEY_ENCIPHERMENT = 1 << 13,
+ DATA_ENCIPHERMENT = 1 << 12,
+ KEY_AGREEMENT = 1 << 11,
+ KEY_CERT_SIGN = 1 << 10,
+ CRL_SIGN = 1 << 9,
+ ENCIPHER_ONLY = 1 << 8,
+ DECIPHER_ONLY = 1 << 7
+};
+
+class Public_Key;
+
+/**
+* Check that key constraints are permitted for a specific public key.
+* @param pub_key the public key on which the constraints shall be enforced on
+* @param constraints the constraints that shall be enforced on the key
+* @throw Exception if the given constraints are not permitted for this key
+*/
+BOTAN_DLL void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints);
+
+}
+
+#endif
diff --git a/src/lib/x509/name_constraint.cpp b/src/lib/x509/name_constraint.cpp
new file mode 100644
index 000000000..e4d69c6ac
--- /dev/null
+++ b/src/lib/x509/name_constraint.cpp
@@ -0,0 +1,273 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/name_constraint.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/charset.h>
+#include <botan/loadstor.h>
+#include <botan/x509_dn.h>
+#include <botan/x509cert.h>
+#include <sstream>
+
+namespace Botan {
+
+GeneralName::GeneralName(const std::string& str) : GeneralName()
+ {
+ size_t p = str.find(':');
+
+ if(p != std::string::npos)
+ {
+ m_type = str.substr(0, p);
+ m_name = str.substr(p + 1, std::string::npos);
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralName::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("GeneralName encoding");
+ }
+
+void GeneralName::decode_from(class BER_Decoder& ber)
+ {
+ BER_Object obj = ber.get_next_object();
+ if((obj.class_tag != CONTEXT_SPECIFIC) &&
+ (obj.class_tag != (CONTEXT_SPECIFIC | CONSTRUCTED)))
+ throw Decoding_Error("Invalid class tag while decoding GeneralName");
+
+ const ASN1_Tag tag = obj.type_tag;
+
+ if(tag == 1 || tag == 2 || tag == 6)
+ {
+ m_name = Charset::transcode(ASN1::to_string(obj), LATIN1_CHARSET, LOCAL_CHARSET);
+
+ if(tag == 1)
+ {
+ m_type = "RFC822";
+ }
+ else if(tag == 2)
+ {
+ m_type = "DNS";
+ }
+ else if(tag == 6)
+ {
+ m_type = "URI";
+ }
+ }
+ else if(tag == 4)
+ {
+ X509_DN dn;
+ std::multimap<std::string, std::string> nam;
+ BER_Decoder dec(obj.value);
+ std::stringstream ss;
+
+ dn.decode_from(dec);
+ ss << dn;
+
+ m_name = ss.str();
+ m_type = "DN";
+ }
+ else if(tag == 7)
+ {
+ if(obj.value.size() == 8)
+ {
+ const std::vector<byte> ip(obj.value.begin(), obj.value.begin() + 4);
+ const std::vector<byte> net(obj.value.begin() + 4, obj.value.end());
+ m_type = "IP";
+ m_name = ipv4_to_string(load_be<u32bit>(ip.data(), 0)) + "/" + ipv4_to_string(load_be<u32bit>(net.data(), 0));
+ }
+ else if(obj.value.size() == 32)
+ {
+ throw Decoding_Error("Unsupported IPv6 name constraint");
+ }
+ else
+ {
+ throw Decoding_Error("Invalid IP name constraint size " +
+ std::to_string(obj.value.size()));
+ }
+ }
+ else
+ {
+ throw Decoding_Error("Found unknown GeneralName type");
+ }
+ }
+
+GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const
+ {
+ std::vector<std::string> nam;
+ std::function<bool(const GeneralName*, const std::string&)> match_fn;
+
+ if(type() == "DNS")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dns);
+ nam = cert.subject_info("DNS");
+
+ if(nam.empty())
+ {
+ nam = cert.subject_info("CN");
+ }
+ }
+ else if(type() == "DN")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dn);
+
+ std::stringstream ss;
+ ss << cert.subject_dn();
+ nam.push_back(ss.str());
+ }
+ else if(type() == "IP")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_ip);
+ nam = cert.subject_info("IP");
+ }
+ else
+ {
+ return MatchResult::UnknownType;
+ }
+
+ if(nam.empty())
+ {
+ return MatchResult::NotFound;
+ }
+
+ bool some = false;
+ bool all = true;
+
+ for(const std::string& n: nam)
+ {
+ bool m = match_fn(this, n);
+
+ some |= m;
+ all &= m;
+ }
+
+ if(all)
+ {
+ return MatchResult::All;
+ }
+ else if(some)
+ {
+ return MatchResult::Some;
+ }
+ else
+ {
+ return MatchResult::None;
+ }
+ }
+
+bool GeneralName::matches_dns(const std::string& nam) const
+ {
+ if(nam.size() == name().size())
+ {
+ return nam == name();
+ }
+ else if(name().size() > nam.size())
+ {
+ return false;
+ }
+ else // name.size() < nam.size()
+ {
+ std::string constr = name().front() == '.' ? name() : "." + name();
+ // constr is suffix of nam
+ return constr == nam.substr(nam.size() - constr.size(), constr.size());
+ }
+ }
+
+bool GeneralName::matches_dn(const std::string& nam) const
+ {
+ std::stringstream ss(nam);
+ std::stringstream tt(name());
+ X509_DN nam_dn, my_dn;
+
+ ss >> nam_dn;
+ tt >> my_dn;
+
+ auto attr = nam_dn.get_attributes();
+ bool ret = true;
+ int trys = 0;
+
+ for(const std::pair<OID,std::string>& c: my_dn.get_attributes())
+ {
+ auto i = attr.equal_range(c.first);
+
+ if(i.first != i.second)
+ {
+ trys += 1;
+ ret &= i.first->second == c.second;
+ }
+ }
+
+ return trys > 0 && ret;
+ }
+
+bool GeneralName::matches_ip(const std::string& nam) const
+ {
+ u32bit ip = string_to_ipv4(nam);
+ std::vector<std::string> p = split_on(name(), '/');
+
+ if(p.size() != 2)
+ throw Decoding_Error("failed to parse IPv4 address");
+
+ u32bit net = string_to_ipv4(p.at(0));
+ u32bit mask = string_to_ipv4(p.at(1));
+
+ return (ip & mask) == net;
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
+ {
+ os << gn.type() << ":" << gn.name();
+ return os;
+ }
+
+GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
+ {
+ size_t p0, p1;
+ size_t min = std::stoull(str, &p0, 10);
+ size_t max = std::stoull(str.substr(p0 + 1), &p1, 10);
+ GeneralName gn(str.substr(p0 + p1 + 2));
+
+ if(p0 > 0 && p1 > 0)
+ {
+ m_minimum = min;
+ m_maximum = max;
+ m_base = gn;
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralSubtree::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("General Subtree encoding");
+ }
+
+void GeneralSubtree::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .decode(m_base)
+ .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
+ .end_cons();
+
+ if(m_minimum != 0)
+ throw Decoding_Error("GeneralSubtree minimum must be 0");
+
+ m_maximum = std::numeric_limits<std::size_t>::max();
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
+ {
+ os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+ return os;
+ }
+}
diff --git a/src/lib/x509/name_constraint.h b/src/lib/x509/name_constraint.h
new file mode 100644
index 000000000..43d7fcbcb
--- /dev/null
+++ b/src/lib/x509/name_constraint.h
@@ -0,0 +1,179 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NAME_CONSTRAINT_H__
+#define BOTAN_NAME_CONSTRAINT_H__
+
+#include <botan/asn1_obj.h>
+#include <ostream>
+
+namespace Botan {
+
+class X509_Certificate;
+
+/**
+* @brief X.509 GeneralName Type
+*
+* Handles parsing GeneralName types in their BER and canonical string
+* encoding. Allows matching GeneralNames against each other using
+* the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
+*/
+class BOTAN_DLL GeneralName : public ASN1_Object
+ {
+ public:
+ enum MatchResult : int
+ {
+ All,
+ Some,
+ None,
+ NotFound,
+ UnknownType,
+ };
+
+ /**
+ * Creates an empty GeneralName.
+ */
+ GeneralName() : m_type(), m_name() {}
+
+ /**
+ * Creates a new GeneralName for its string format.
+ * @param str type and name, colon-separated, e.g., "DNS:google.com"
+ */
+ GeneralName(const std::string& str);
+
+ void encode_into(class DER_Encoder&) const override;
+
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * @return Type of the name. Can be DN, DNS, IP, RFC822 or URI.
+ */
+ const std::string& type() const { return m_type; }
+
+ /**
+ * @return The name as string. Format depends on type.
+ */
+ const std::string& name() const { return m_name; }
+
+ /**
+ * Checks whether a given certificate (partially) matches this name.
+ * @param cert certificate to be matched
+ * @return the match result
+ */
+ MatchResult matches(const X509_Certificate& cert) const;
+
+ private:
+ std::string m_type;
+ std::string m_name;
+
+ bool matches_dns(const std::string&) const;
+ bool matches_dn(const std::string&) const;
+ bool matches_ip(const std::string&) const;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+
+/**
+* @brief A single Name Constraint
+*
+* The Name Constraint extension adds a minimum and maximum path
+* length to a GeneralName to form a constraint. The length limits
+* are currently unused.
+*/
+class BOTAN_DLL GeneralSubtree : public ASN1_Object
+ {
+ public:
+ /**
+ * Creates an empty name constraint.
+ */
+ GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+ {}
+
+ /***
+ * Creates a new name constraint.
+ * @param base name
+ * @param min minimum path length
+ * @param max maximum path length
+ */
+ GeneralSubtree(GeneralName base, size_t min, size_t max)
+ : m_base(base), m_minimum(min), m_maximum(max)
+ {}
+
+ /**
+ * Creates a new name constraint for its string format.
+ * @param str name constraint
+ */
+ GeneralSubtree(const std::string& str);
+
+ void encode_into(class DER_Encoder&) const override;
+
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * @return name
+ */
+ GeneralName base() const { return m_base; }
+
+ /**
+ * @return minimum path length
+ */
+ size_t minimum() const { return m_minimum; }
+
+ /**
+ * @return maximum path length
+ */
+ size_t maximum() const { return m_maximum; }
+
+ private:
+ GeneralName m_base;
+ size_t m_minimum;
+ size_t m_maximum;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs);
+
+/**
+* @brief Name Constraints
+*
+* Wraps the Name Constraints associated with a certificate.
+*/
+class BOTAN_DLL NameConstraints
+ {
+ public:
+ /**
+ * Creates an empty name NameConstraints.
+ */
+ NameConstraints() : m_permitted_subtrees(), m_excluded_subtrees() {}
+
+ /**
+ * Creates NameConstraints from a list of permitted and excluded subtrees.
+ * @param permitted_subtrees names for which the certificate is permitted
+ * @param excluded_subtrees names for which the certificate is not permitted
+ */
+ NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+ std::vector<GeneralSubtree>&& excluded_subtrees)
+ : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+ {}
+
+ /**
+ * @return permitted names
+ */
+ const std::vector<GeneralSubtree>& permitted() const { return m_permitted_subtrees; }
+
+ /**
+ * @return excluded names
+ */
+ const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+
+ private:
+ std::vector<GeneralSubtree> m_permitted_subtrees;
+ std::vector<GeneralSubtree> m_excluded_subtrees;
+};
+
+}
+
+#endif
diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp
new file mode 100644
index 000000000..761c5b436
--- /dev/null
+++ b/src/lib/x509/ocsp.cpp
@@ -0,0 +1,251 @@
+/*
+* OCSP
+* (C) 2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/certstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/oids.h>
+#include <botan/base64.h>
+#include <botan/pubkey.h>
+#include <botan/x509path.h>
+#include <botan/http_util.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+namespace {
+
+void decode_optional_list(BER_Decoder& ber,
+ ASN1_Tag tag,
+ std::vector<X509_Certificate>& output)
+ {
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.type_tag != tag || obj.class_tag != (CONTEXT_SPECIFIC | CONSTRUCTED))
+ {
+ ber.push_back(obj);
+ return;
+ }
+
+ BER_Decoder list(obj.value);
+
+ while(list.more_items())
+ {
+ BER_Object certbits = list.get_next_object();
+ X509_Certificate cert(unlock(certbits.value));
+ output.push_back(std::move(cert));
+ }
+ }
+
+void check_signature(const std::vector<byte>& tbs_response,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<byte>& signature,
+ const X509_Certificate& cert)
+ {
+ std::unique_ptr<Public_Key> pub_key(cert.subject_public_key());
+
+ const std::vector<std::string> sig_info =
+ split_on(OIDS::lookup(sig_algo.oid), '/');
+
+ if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+ throw Exception("Information in OCSP response does not match cert");
+
+ std::string padding = sig_info[1];
+ Signature_Format format =
+ (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+
+ PK_Verifier verifier(*pub_key, padding, format);
+
+ if(!verifier.verify_message(ASN1::put_in_sequence(tbs_response), signature))
+ throw Exception("Signature on OCSP response does not verify");
+ }
+
+void check_signature(const std::vector<byte>& tbs_response,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<byte>& signature,
+ const Certificate_Store& trusted_roots,
+ const std::vector<X509_Certificate>& certs)
+ {
+ if(certs.size() < 1)
+ throw Invalid_Argument("Short cert chain for check_signature");
+
+ if(trusted_roots.certificate_known(certs[0]))
+ return check_signature(tbs_response, sig_algo, signature, certs[0]);
+
+ // Otherwise attempt to chain the signing cert to a trust root
+
+ if(!certs[0].allowed_extended_usage("PKIX.OCSPSigning"))
+ throw Exception("OCSP response cert does not allow OCSP signing");
+
+ auto result = x509_path_validate(certs, Path_Validation_Restrictions(), trusted_roots);
+
+ if(!result.successful_validation())
+ throw Exception("Certificate validation failure: " + result.result_string());
+
+ if(!trusted_roots.certificate_known(result.trust_root())) // not needed anymore?
+ throw Exception("Certificate chain roots in unknown/untrusted CA");
+
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = result.cert_path();
+
+ check_signature(tbs_response, sig_algo, signature, *cert_path[0]);
+ }
+
+}
+
+std::vector<byte> Request::BER_encode() const
+ {
+ CertID certid(m_issuer, m_subject);
+
+ return DER_Encoder().start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(static_cast<size_t>(0)) // version #
+ .end_explicit()
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(certid)
+ .end_cons()
+ .end_cons()
+ .end_cons()
+ .end_cons().get_contents_unlocked();
+ }
+
+std::string Request::base64_encode() const
+ {
+ return Botan::base64_encode(BER_encode());
+ }
+
+Response::Response(const Certificate_Store& trusted_roots,
+ const std::vector<byte>& response_bits)
+ {
+ BER_Decoder response_outer = BER_Decoder(response_bits).start_cons(SEQUENCE);
+
+ size_t resp_status = 0;
+
+ response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
+
+ if(resp_status != 0)
+ throw Exception("OCSP response status " + std::to_string(resp_status));
+
+ if(response_outer.more_items())
+ {
+ BER_Decoder response_bytes =
+ response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
+
+ response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"),
+ "Unknown response type in OCSP response");
+
+ BER_Decoder basicresponse =
+ BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
+
+ std::vector<byte> tbs_bits;
+ AlgorithmIdentifier sig_algo;
+ std::vector<byte> signature;
+ std::vector<X509_Certificate> certs;
+
+ basicresponse.start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .end_cons()
+ .decode(sig_algo)
+ .decode(signature, BIT_STRING);
+ decode_optional_list(basicresponse, ASN1_Tag(0), certs);
+
+ size_t responsedata_version = 0;
+ X509_DN name;
+ std::vector<byte> key_hash;
+ X509_Time produced_at;
+ Extensions extensions;
+
+ BER_Decoder(tbs_bits)
+ .decode_optional(responsedata_version, ASN1_Tag(0),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional(name, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional_string(key_hash, OCTET_STRING, 2,
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode(produced_at)
+
+ .decode_list(m_responses)
+
+ .decode_optional(extensions, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+
+ if(certs.empty())
+ {
+ if(auto cert = trusted_roots.find_cert(name, std::vector<byte>()))
+ certs.push_back(*cert);
+ else
+ throw Exception("Could not find certificate that signed OCSP response");
+ }
+
+ check_signature(tbs_bits, sig_algo, signature, trusted_roots, certs);
+ }
+
+ response_outer.end_cons();
+ }
+
+Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const
+ {
+ for(const auto& response : m_responses)
+ {
+ if(response.certid().is_id_for(issuer, subject))
+ {
+ X509_Time current_time(std::chrono::system_clock::now());
+
+ if(response.cert_status() == 1)
+ return Certificate_Status_Code::CERT_IS_REVOKED;
+
+ if(response.this_update() > current_time)
+ return Certificate_Status_Code::OCSP_NOT_YET_VALID;
+
+ if(response.next_update().time_is_set() && current_time > response.next_update())
+ return Certificate_Status_Code::OCSP_HAS_EXPIRED;
+
+ if(response.cert_status() == 0)
+ return Certificate_Status_Code::OCSP_RESPONSE_GOOD;
+ else
+ return Certificate_Status_Code::OCSP_BAD_STATUS;
+ }
+ }
+
+ return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
+ }
+
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ const Certificate_Store* trusted_roots)
+ {
+ const std::string responder_url = subject.ocsp_responder();
+
+ if(responder_url.empty())
+ throw Exception("No OCSP responder specified");
+
+ OCSP::Request req(issuer, subject);
+
+ auto http = HTTP::POST_sync(responder_url,
+ "application/ocsp-request",
+ req.BER_encode());
+
+ http.throw_unless_ok();
+
+ // Check the MIME type?
+
+ OCSP::Response response(*trusted_roots, http.body());
+
+ return response;
+ }
+
+}
+
+}
diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h
new file mode 100644
index 000000000..fe1796984
--- /dev/null
+++ b/src/lib/x509/ocsp.h
@@ -0,0 +1,113 @@
+/*
+* OCSP
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_H__
+#define BOTAN_OCSP_H__
+
+#include <botan/cert_status.h>
+#include <botan/ocsp_types.h>
+
+namespace Botan {
+
+class Certificate_Store;
+
+namespace OCSP {
+
+/**
+* An OCSP request.
+*/
+class BOTAN_DLL Request
+ {
+ public:
+ /**
+ * Create an OCSP request.
+ * @param issuer_cert issuer certificate
+ * @param subject_cert subject certificate
+ */
+ Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert) :
+ m_issuer(issuer_cert),
+ m_subject(subject_cert)
+ {}
+
+ /**
+ * @return BER-encoded OCSP request
+ */
+ std::vector<byte> BER_encode() const;
+
+ /**
+ * @return Base64-encoded OCSP request
+ */
+ std::string base64_encode() const;
+
+ /**
+ * @return issuer certificate
+ */
+ const X509_Certificate& issuer() const { return m_issuer; }
+
+ /**
+ * @return subject certificate
+ */
+ const X509_Certificate& subject() const { return m_subject; }
+ private:
+ X509_Certificate m_issuer, m_subject;
+ };
+
+/**
+* An OCSP response.
+*/
+class BOTAN_DLL Response
+ {
+ public:
+ /**
+ * Creates an empty OCSP response.
+ */
+ Response() {}
+
+ /**
+ * Creates an OCSP response.
+ * @param trusted_roots trusted roots for the OCSP response
+ * @param response_bits response bits received
+ */
+ Response(const Certificate_Store& trusted_roots,
+ const std::vector<byte>& response_bits);
+
+ /**
+ * Searches the OCSP response for issuer and subject certificate.
+ * @param issuer issuer certificate
+ * @param subject subject certificate
+ * @return OCSP status code, possible values:
+ * CERT_IS_REVOKED,
+ * OCSP_NOT_YET_VALID,
+ * OCSP_HAS_EXPIRED,
+ * OCSP_RESPONSE_GOOD,
+ * OCSP_BAD_STATUS,
+ * OCSP_CERT_NOT_LISTED
+ */
+ Certificate_Status_Code status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const;
+
+ private:
+ std::vector<SingleResponse> m_responses;
+ };
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject subject certificate
+* @param trusted_roots trusted roots for the OCSP response
+* @return OCSP response
+*/
+BOTAN_DLL Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ const Certificate_Store* trusted_roots);
+
+}
+
+}
+
+#endif
diff --git a/src/lib/x509/ocsp_types.cpp b/src/lib/x509/ocsp_types.cpp
new file mode 100644
index 000000000..d470c2fa1
--- /dev/null
+++ b/src/lib/x509/ocsp_types.cpp
@@ -0,0 +1,119 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/hash.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+CertID::CertID(const X509_Certificate& issuer,
+ const X509_Certificate& subject)
+ {
+ /*
+ In practice it seems some responders, including, notably,
+ ocsp.verisign.com, will reject anything but SHA-1 here
+ */
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-160"));
+
+ m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM);
+ m_issuer_key_hash = unlock(hash->process(extract_key_bitstr(issuer)));
+ m_issuer_dn_hash = unlock(hash->process(subject.raw_issuer_dn()));
+ m_subject_serial = BigInt::decode(subject.serial_number());
+ }
+
+std::vector<byte> CertID::extract_key_bitstr(const X509_Certificate& cert) const
+ {
+ const auto key_bits = cert.subject_public_key_bits();
+
+ AlgorithmIdentifier public_key_algid;
+ std::vector<byte> public_key_bitstr;
+
+ BER_Decoder(key_bits)
+ .decode(public_key_algid)
+ .decode(public_key_bitstr, BIT_STRING);
+
+ return public_key_bitstr;
+ }
+
+bool CertID::is_id_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const
+ {
+ try
+ {
+ if(BigInt::decode(subject.serial_number()) != m_subject_serial)
+ return false;
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create(OIDS::lookup(m_hash_id.oid)));
+
+ if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn())))
+ return false;
+
+ if(m_issuer_key_hash != unlock(hash->process(extract_key_bitstr(issuer))))
+ return false;
+ }
+ catch(...)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+void CertID::encode_into(class DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .encode(m_hash_id)
+ .encode(m_issuer_dn_hash, OCTET_STRING)
+ .encode(m_issuer_key_hash, OCTET_STRING)
+ .encode(m_subject_serial)
+ .end_cons();
+ }
+
+void CertID::decode_from(class BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .decode(m_hash_id)
+ .decode(m_issuer_dn_hash, OCTET_STRING)
+ .decode(m_issuer_key_hash, OCTET_STRING)
+ .decode(m_subject_serial)
+ .end_cons();
+
+ }
+
+void SingleResponse::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("SingleResponse::encode_into");
+ }
+
+void SingleResponse::decode_from(class BER_Decoder& from)
+ {
+ BER_Object cert_status;
+ Extensions extensions;
+
+ from.start_cons(SEQUENCE)
+ .decode(m_certid)
+ .get_next(cert_status)
+ .decode(m_thisupdate)
+ .decode_optional(m_nextupdate, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .decode_optional(extensions,
+ ASN1_Tag(1),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .end_cons();
+
+ m_cert_status = cert_status.type_tag;
+ }
+
+}
+
+}
diff --git a/src/lib/x509/ocsp_types.h b/src/lib/x509/ocsp_types.h
new file mode 100644
index 000000000..6df8ac17f
--- /dev/null
+++ b/src/lib/x509/ocsp_types.h
@@ -0,0 +1,67 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_TYPES_H__
+#define BOTAN_OCSP_TYPES_H__
+
+#include <botan/x509cert.h>
+#include <botan/asn1_time.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+class BOTAN_DLL CertID final : public ASN1_Object
+ {
+ public:
+ CertID() {}
+
+ CertID(const X509_Certificate& issuer,
+ const X509_Certificate& subject);
+
+ bool is_id_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const;
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+ private:
+ std::vector<byte> extract_key_bitstr(const X509_Certificate& cert) const;
+
+ AlgorithmIdentifier m_hash_id;
+ std::vector<byte> m_issuer_dn_hash;
+ std::vector<byte> m_issuer_key_hash;
+ BigInt m_subject_serial;
+ };
+
+class BOTAN_DLL SingleResponse final : public ASN1_Object
+ {
+ public:
+ const CertID& certid() const { return m_certid; }
+
+ size_t cert_status() const { return m_cert_status; }
+
+ X509_Time this_update() const { return m_thisupdate; }
+
+ X509_Time next_update() const { return m_nextupdate; }
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+ private:
+ CertID m_certid;
+ size_t m_cert_status = 2; // unknown
+ X509_Time m_thisupdate;
+ X509_Time m_nextupdate;
+ };
+
+}
+
+}
+
+#endif
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp
new file mode 100644
index 000000000..ccd22454b
--- /dev/null
+++ b/src/lib/x509/pkcs10.cpp
@@ -0,0 +1,210 @@
+/*
+* PKCS #10
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs10.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+/*
+* PKCS10_Request Constructor
+*/
+PKCS10_Request::PKCS10_Request(DataSource& in) :
+ X509_Object(in, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST")
+ {
+ do_decode();
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* PKCS10_Request Constructor
+*/
+PKCS10_Request::PKCS10_Request(const std::string& fsname) :
+ X509_Object(fsname, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST")
+ {
+ do_decode();
+ }
+#endif
+
+/*
+* PKCS10_Request Constructor
+*/
+PKCS10_Request::PKCS10_Request(const std::vector<byte>& in) :
+ X509_Object(in, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST")
+ {
+ do_decode();
+ }
+
+/*
+* Deocde the CertificateRequestInfo
+*/
+void PKCS10_Request::force_decode()
+ {
+ BER_Decoder cert_req_info(m_tbs_bits);
+
+ size_t version;
+ cert_req_info.decode(version);
+ if(version != 0)
+ 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());
+
+ 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"
+ )
+ );
+
+ BER_Object attr_bits = cert_req_info.get_next_object();
+
+ if(attr_bits.type_tag == 0 &&
+ attr_bits.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ {
+ BER_Decoder attributes(attr_bits.value);
+ while(attributes.more_items())
+ {
+ Attribute attr;
+ attributes.decode(attr);
+ handle_attribute(attr);
+ }
+ attributes.verify_end();
+ }
+ else if(attr_bits.type_tag != NO_OBJECT)
+ throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for attributes",
+ attr_bits.type_tag, attr_bits.class_tag);
+
+ cert_req_info.verify_end();
+
+ 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)
+ {
+ 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"))
+ {
+ Extensions extensions;
+ value.decode(extensions).verify_end();
+
+ Data_Store issuer_info;
+ extensions.contents_to(m_info, issuer_info);
+ }
+ }
+
+/*
+* Return the challenge password (if any)
+*/
+std::string PKCS10_Request::challenge_password() const
+ {
+ return m_info.get1("PKCS9.ChallengePassword");
+ }
+
+/*
+* Return the name of the requestor
+*/
+X509_DN PKCS10_Request::subject_dn() const
+ {
+ return create_dn(m_info);
+ }
+
+/*
+* Return the public key of the requestor
+*/
+std::vector<byte> 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 the public key of the requestor
+*/
+Public_Key* PKCS10_Request::subject_public_key() const
+ {
+ DataSource_Memory source(m_info.get1("X509.Certificate.public_key"));
+ return X509::load_key(source);
+ }
+
+/*
+* Return the alternative names of the requestor
+*/
+AlternativeName PKCS10_Request::subject_alt_name() const
+ {
+ return create_alt_name(m_info);
+ }
+
+/*
+* Return the key constraints (if any)
+*/
+Key_Constraints PKCS10_Request::constraints() const
+ {
+ return Key_Constraints(m_info.get1_u32bit("X509v3.KeyUsage", NO_CONSTRAINTS));
+ }
+
+/*
+* Return the extendend key constraints (if any)
+*/
+std::vector<OID> PKCS10_Request::ex_constraints() const
+ {
+ std::vector<std::string> oids = m_info.get("X509v3.ExtendedKeyUsage");
+
+ std::vector<OID> result;
+ for(size_t i = 0; i != oids.size(); ++i)
+ result.push_back(OID(oids[i]));
+ return result;
+ }
+
+/*
+* Return is a CA certificate is requested
+*/
+bool PKCS10_Request::is_CA() const
+ {
+ return (m_info.get1_u32bit("X509v3.BasicConstraints.is_ca") > 0);
+ }
+
+/*
+* Return the desired path limit (if any)
+*/
+u32bit PKCS10_Request::path_limit() const
+ {
+ return m_info.get1_u32bit("X509v3.BasicConstraints.path_constraint", 0);
+ }
+
+}
diff --git a/src/lib/x509/pkcs10.h b/src/lib/x509/pkcs10.h
new file mode 100644
index 000000000..c7a9ec300
--- /dev/null
+++ b/src/lib/x509/pkcs10.h
@@ -0,0 +1,112 @@
+/*
+* PKCS #10
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS10_H__
+#define BOTAN_PKCS10_H__
+
+#include <botan/x509_obj.h>
+#include <botan/x509_dn.h>
+#include <botan/datastor.h>
+#include <botan/key_constraint.h>
+#include <botan/asn1_attribute.h>
+#include <botan/asn1_alt_name.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* PKCS #10 Certificate Request.
+*/
+class BOTAN_DLL PKCS10_Request final : public X509_Object
+ {
+ public:
+ /**
+ * Get the subject public key.
+ * @return subject public key
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Get the raw DER encoded public key.
+ * @return raw DER encoded public key
+ */
+ std::vector<byte> raw_public_key() const;
+
+ /**
+ * Get the subject DN.
+ * @return subject DN
+ */
+ X509_DN subject_dn() const;
+
+ /**
+ * Get the subject alternative name.
+ * @return subject alternative name.
+ */
+ AlternativeName subject_alt_name() const;
+
+ /**
+ * Get the key constraints for the key associated with this
+ * PKCS#10 object.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the extendend key constraints (if any).
+ * @return extended key constraints
+ */
+ std::vector<OID> ex_constraints() const;
+
+ /**
+ * Find out whether this is a CA request.
+ * @result true if it is a CA request, false otherwise.
+ */
+ bool is_CA() const;
+
+ /**
+ * Return the constraint on the path length defined
+ * in the BasicConstraints extension.
+ * @return path limit
+ */
+ u32bit path_limit() const;
+
+ /**
+ * Get the challenge password for this request
+ * @return challenge password for this request
+ */
+ std::string challenge_password() const;
+
+ /**
+ * Create a PKCS#10 Request from a data source.
+ * @param source the data source providing the DER encoded request
+ */
+ explicit PKCS10_Request(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a PKCS#10 Request from a file.
+ * @param filename the name of the file containing the DER or PEM
+ * encoded request file
+ */
+ explicit PKCS10_Request(const std::string& filename);
+#endif
+
+ /**
+ * Create a PKCS#10 Request from binary data.
+ * @param vec a std::vector containing the DER value
+ */
+ explicit PKCS10_Request(const std::vector<byte>& vec);
+ private:
+ void force_decode() override;
+ void handle_attribute(const Attribute&);
+
+ Data_Store m_info;
+ };
+
+}
+
+#endif
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
new file mode 100644
index 000000000..179d903c4
--- /dev/null
+++ b/src/lib/x509/x509_ca.cpp
@@ -0,0 +1,266 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ca.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/key_constraint.h>
+#include <algorithm>
+#include <typeinfo>
+#include <iterator>
+#include <set>
+
+namespace Botan {
+
+/*
+* Load the certificate and private key
+*/
+X509_CA::X509_CA(const X509_Certificate& c,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) : m_cert(c)
+ {
+ if(!m_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ m_signer = choose_sig_format(key, rng, hash_fn, m_ca_sig_algo);
+ }
+
+/*
+* X509_CA Destructor
+*/
+X509_CA::~X509_CA()
+ {
+ delete m_signer;
+ }
+
+/*
+* Sign a PKCS #10 certificate request
+*/
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after)
+ {
+ Key_Constraints constraints;
+ if(req.is_CA())
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ std::unique_ptr<Public_Key> key(req.subject_public_key());
+ verify_cert_constraints_valid_for_key_type(*key, req.constraints());
+ constraints = req.constraints();
+ }
+
+ Extensions extensions;
+
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ extensions.add(new Cert_Extension::Authority_Key_ID(m_cert.subject_key_id()));
+ extensions.add(new Cert_Extension::Subject_Key_ID(req.raw_public_key()));
+
+ extensions.add(
+ new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name()));
+
+ extensions.add(
+ new Cert_Extension::Extended_Key_Usage(req.ex_constraints()));
+
+ return make_cert(m_signer, rng, m_ca_sig_algo,
+ req.raw_public_key(),
+ not_before, not_after,
+ m_cert.subject_dn(), req.subject_dn(),
+ extensions);
+ }
+
+/*
+* Create a new certificate
+*/
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<byte>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions)
+ {
+ const size_t X509_CERT_VERSION = 3;
+ const size_t SERIAL_BITS = 128;
+
+ BigInt serial_no(rng, SERIAL_BITS);
+
+ // clang-format off
+ return X509_Certificate(X509_Object::make_signed(
+ signer, rng, sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(X509_CERT_VERSION-1)
+ .end_explicit()
+
+ .encode(serial_no)
+
+ .encode(sig_algo)
+ .encode(issuer_dn)
+
+ .start_cons(SEQUENCE)
+ .encode(not_before)
+ .encode(not_after)
+ .end_cons()
+
+ .encode(subject_dn)
+ .raw_bytes(pub_key)
+
+ .start_explicit(3)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents()
+ ));;
+ // clang-format on
+ }
+
+/*
+* Create a new, empty CRL
+*/
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+ u32bit next_update) const
+ {
+ std::vector<CRL_Entry> empty;
+ return make_crl(empty, 1, next_update, rng);
+ }
+
+/*
+* Update a CRL with new entries
+*/
+X509_CRL X509_CA::update_crl(const X509_CRL& crl,
+ const std::vector<CRL_Entry>& new_revoked,
+ RandomNumberGenerator& rng,
+ u32bit next_update) const
+ {
+ std::vector<CRL_Entry> revoked = crl.get_revoked();
+
+ std::copy(new_revoked.begin(), new_revoked.end(),
+ std::back_inserter(revoked));
+
+ return make_crl(revoked, crl.crl_number() + 1, next_update, rng);
+ }
+
+/*
+* Create a CRL
+*/
+X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
+ u32bit crl_number, u32bit next_update,
+ RandomNumberGenerator& rng) const
+ {
+ const size_t X509_CRL_VERSION = 2;
+
+ if(next_update == 0)
+ next_update = timespec_to_u32bit("7d");
+
+ // Totally stupid: ties encoding logic to the return of std::time!!
+ auto current_time = std::chrono::system_clock::now();
+ auto expire_time = current_time + std::chrono::seconds(next_update);
+
+ Extensions extensions;
+ extensions.add(
+ new Cert_Extension::Authority_Key_ID(m_cert.subject_key_id()));
+ extensions.add(new Cert_Extension::CRL_Number(crl_number));
+
+ // clang-format off
+ const std::vector<byte> crl = X509_Object::make_signed(
+ m_signer, rng, m_ca_sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .encode(X509_CRL_VERSION-1)
+ .encode(m_ca_sig_algo)
+ .encode(m_cert.issuer_dn())
+ .encode(X509_Time(current_time))
+ .encode(X509_Time(expire_time))
+ .encode_if(revoked.size() > 0,
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(revoked)
+ .end_cons()
+ )
+ .start_explicit(0)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents());
+ // clang-format on
+
+ return X509_CRL(crl);
+ }
+
+/*
+* Return the CA's certificate
+*/
+X509_Certificate X509_CA::ca_certificate() const
+ {
+ return m_cert;
+ }
+
+/*
+* Choose a signing format for the key
+*/
+PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ const std::string algo_name = key.algo_name();
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create(hash_fn));
+ if(!hash)
+ throw Algorithm_Not_Found(hash_fn);
+
+ if(key.max_input_bits() < hash->output_length() * 8)
+ throw Invalid_Argument("Key is too small for chosen hash function");
+
+ std::string padding;
+ if(algo_name == "RSA")
+ {
+ padding = "EMSA3";
+ }
+ else if(algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" || algo_name == "ECKCDSA")
+ {
+ padding = "EMSA1";
+ }
+ else
+ {
+ throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
+ }
+
+ const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363;
+
+ padding = padding + "(" + hash->name() + ")";
+
+ sig_algo.oid = OIDS::lookup(algo_name + "/" + padding);
+ sig_algo.parameters = key.algorithm_identifier().parameters;
+
+ return new PK_Signer(key, rng, padding, format);
+ }
+
+}
diff --git a/src/lib/x509/x509_ca.h b/src/lib/x509/x509_ca.h
new file mode 100644
index 000000000..c96a709d5
--- /dev/null
+++ b/src/lib/x509/x509_ca.h
@@ -0,0 +1,145 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CA_H__
+#define BOTAN_X509_CA_H__
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.h>
+#include <botan/pkcs10.h>
+#include <botan/pubkey.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+/**
+* This class represents X.509 Certificate Authorities (CAs).
+*/
+class BOTAN_DLL X509_CA
+ {
+ public:
+ /**
+ * Sign a PKCS#10 Request.
+ * @param req the request to sign
+ * @param rng the rng to use
+ * @param not_before the starting time for the certificate
+ * @param not_after the expiration time for the certificate
+ * @return resulting certificate
+ */
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after);
+
+ /**
+ * Get the certificate of this CA.
+ * @return CA certificate
+ */
+ X509_Certificate ca_certificate() const;
+
+ /**
+ * Create a new and empty CRL for this CA.
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ * @return new CRL
+ */
+ X509_CRL new_crl(RandomNumberGenerator& rng,
+ u32bit next_update = 0) const;
+
+ /**
+ * Create a new CRL by with additional entries.
+ * @param last_crl the last CRL of this CA to add the new entries to
+ * @param new_entries contains the new CRL entries to be added to the CRL
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ */
+ X509_CRL update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_entries,
+ RandomNumberGenerator& rng,
+ u32bit next_update = 0) const;
+
+ /**
+ * Interface for creating new certificates
+ * @param signer a signing object
+ * @param rng a random number generator
+ * @param sig_algo the signature algorithm identifier
+ * @param pub_key the serialized public key
+ * @param not_before the start time of the certificate
+ * @param not_after the end time of the certificate
+ * @param issuer_dn the DN of the issuer
+ * @param subject_dn the DN of the subject
+ * @param extensions an optional list of certificate extensions
+ * @returns newly minted certificate
+ */
+ static X509_Certificate make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<byte>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions);
+
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ BOTAN_DEPRECATED("Use version taking RNG object")
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn) :
+ X509_CA(ca_certificate, key, hash_fn, system_rng())
+ {}
+#endif
+
+ X509_CA(const X509_CA&) = delete;
+ X509_CA& operator=(const X509_CA&) = delete;
+
+ ~X509_CA();
+ private:
+ X509_CRL make_crl(const std::vector<CRL_Entry>& entries,
+ u32bit crl_number, u32bit next_update,
+ RandomNumberGenerator& rng) const;
+
+ AlgorithmIdentifier m_ca_sig_algo;
+ X509_Certificate m_cert;
+ PK_Signer* m_signer;
+ };
+
+/**
+* Choose the default signature format for a certain public key signature
+* scheme.
+* @param key will be the key to choose a padding scheme for
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+BOTAN_DLL PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
+}
+
+#endif
diff --git a/src/lib/x509/x509_crl.cpp b/src/lib/x509/x509_crl.cpp
new file mode 100644
index 000000000..8eb4c01db
--- /dev/null
+++ b/src/lib/x509/x509_crl.cpp
@@ -0,0 +1,202 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* 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)
+ {
+ do_decode();
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* 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)
+ {
+ do_decode();
+ }
+#endif
+
+X509_CRL::X509_CRL(const std::vector<byte>& in, bool touc) :
+ X509_Object(in, "CRL/X509 CRL"), m_throw_on_unknown_critical(touc)
+ {
+ 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)
+ {
+ m_info.add(issuer.contents());
+ m_info.add("X509.CRL.start", thisUpdate.to_string());
+ m_info.add("X509.CRL.end", nextUpdate.to_string());
+ }
+
+/**
+* Check if this particular certificate is listed in the CRL
+*/
+bool X509_CRL::is_revoked(const X509_Certificate& cert) const
+ {
+ /*
+ If the cert wasn't issued by the CRL issuer, it's possible the cert
+ is revoked, but not by this CRL. Maybe throw an exception instead?
+ */
+ if(cert.issuer_dn() != issuer_dn())
+ return false;
+
+ std::vector<byte> crl_akid = authority_key_id();
+ std::vector<byte> cert_akid = cert.authority_key_id();
+
+ if(!crl_akid.empty() && !cert_akid.empty())
+ if(crl_akid != cert_akid)
+ return false;
+
+ std::vector<byte> cert_serial = cert.serial_number();
+
+ bool is_revoked = false;
+
+ for(size_t i = 0; i != m_revoked.size(); ++i)
+ {
+ if(cert_serial == m_revoked[i].serial_number())
+ {
+ if(m_revoked[i].reason_code() == REMOVE_FROM_CRL)
+ is_revoked = false;
+ else
+ is_revoked = true;
+ }
+ }
+
+ return is_revoked;
+ }
+
+/*
+* Decode the TBSCertList data
+*/
+void X509_CRL::force_decode()
+ {
+ BER_Decoder tbs_crl(m_tbs_bits);
+
+ size_t version;
+ tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+
+ if(version != 0 && version != 1)
+ throw X509_CRL_Error("Unknown X.509 CRL version " +
+ std::to_string(version+1));
+
+ AlgorithmIdentifier sig_algo_inner;
+ tbs_crl.decode(sig_algo_inner);
+
+ if(m_sig_algo != 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());
+
+ 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());
+
+ BER_Object next = tbs_crl.get_next_object();
+
+ if(next.type_tag == SEQUENCE && next.class_tag == CONSTRUCTED)
+ {
+ BER_Decoder cert_list(next.value);
+
+ while(cert_list.more_items())
+ {
+ CRL_Entry entry(m_throw_on_unknown_critical);
+ cert_list.decode(entry);
+ m_revoked.push_back(entry);
+ }
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.type_tag == 0 &&
+ 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);
+
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.type_tag != NO_OBJECT)
+ throw X509_CRL_Error("Unknown tag in CRL");
+
+ tbs_crl.verify_end();
+ }
+
+/*
+* Return the list of revoked certificates
+*/
+std::vector<CRL_Entry> X509_CRL::get_revoked() const
+ {
+ return m_revoked;
+ }
+
+/*
+* Return the distinguished name of the issuer
+*/
+X509_DN X509_CRL::issuer_dn() const
+ {
+ return create_dn(m_info);
+ }
+
+/*
+* Return the key identifier of the issuer
+*/
+std::vector<byte> X509_CRL::authority_key_id() const
+ {
+ return m_info.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ }
+
+/*
+* Return the CRL number of this CRL
+*/
+u32bit X509_CRL::crl_number() const
+ {
+ return m_info.get1_u32bit("X509v3.CRLNumber");
+ }
+
+/*
+* Return the issue data of the CRL
+*/
+X509_Time X509_CRL::this_update() const
+ {
+ return X509_Time(m_info.get1("X509.CRL.start"), ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Return the date when a new CRL will be issued
+*/
+X509_Time X509_CRL::next_update() const
+ {
+ return X509_Time(m_info.get1("X509.CRL.end"), ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+}
diff --git a/src/lib/x509/x509_crl.h b/src/lib/x509/x509_crl.h
new file mode 100644
index 000000000..e11ea8f48
--- /dev/null
+++ b/src/lib/x509/x509_crl.h
@@ -0,0 +1,125 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CRL_H__
+#define BOTAN_X509_CRL_H__
+
+#include <botan/x509_obj.h>
+#include <botan/x509_dn.h>
+#include <botan/crl_ent.h>
+#include <botan/datastor.h>
+#include <vector>
+
+namespace Botan {
+
+class X509_Certificate;
+
+/**
+* This class represents X.509 Certificate Revocation Lists (CRLs).
+*/
+class BOTAN_DLL X509_CRL final : public X509_Object
+ {
+ public:
+ /**
+ * This class represents CRL related errors.
+ */
+ struct BOTAN_DLL X509_CRL_Error : public Exception
+ {
+ explicit X509_CRL_Error(const std::string& error) :
+ Exception("X509_CRL: " + error) {}
+ };
+
+ /**
+ * Check if this particular certificate is listed in the CRL
+ */
+ bool is_revoked(const X509_Certificate& cert) const;
+
+ /**
+ * 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;
+
+ /**
+ * Get the issuer DN of this CRL.
+ * @return CRLs issuer DN
+ */
+ X509_DN issuer_dn() const;
+
+ /**
+ * Get the AuthorityKeyIdentifier of this CRL.
+ * @return this CRLs AuthorityKeyIdentifier
+ */
+ std::vector<byte> authority_key_id() const;
+
+ /**
+ * Get the serial number of this CRL.
+ * @return CRLs serial number
+ */
+ u32bit crl_number() const;
+
+ /**
+ * Get the CRL's thisUpdate value.
+ * @return CRLs thisUpdate
+ */
+ X509_Time this_update() const;
+
+ /**
+ * Get the CRL's nextUpdate value.
+ * @return CRLs nextdUpdate
+ */
+ 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);
+
+#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);
+#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<byte>& vec,
+ bool throw_on_unknown_critical = false);
+
+ /**
+ * Construct a CRL
+ * @param issuer issuer of this CRL
+ * @param thisUpdate valid from
+ * @param nextUpdate valid until
+ * @param revoked entries to be included in the CRL
+ */
+ X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+ const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked);
+
+ private:
+ void force_decode() override;
+
+ bool m_throw_on_unknown_critical;
+ std::vector<CRL_Entry> m_revoked;
+ Data_Store m_info;
+ };
+
+}
+
+#endif
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
new file mode 100644
index 000000000..23340f784
--- /dev/null
+++ b/src/lib/x509/x509_ext.cpp
@@ -0,0 +1,834 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2010,2012 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/sha160.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/charset.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+/*
+* List of X.509 Certificate Extensions
+*/
+Certificate_Extension* Extensions::get_extension(const OID& oid, bool critical)
+ {
+#define X509_EXTENSION(NAME, TYPE) \
+ if(OIDS::name_of(oid, NAME)) \
+ return new Cert_Extension::TYPE();
+
+ 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);
+
+ return critical ? new Cert_Extension::Unknown_Critical_Extension(oid) : nullptr;
+ }
+
+/*
+* Extensions Copy Constructor
+*/
+Extensions::Extensions(const Extensions& extensions) : ASN1_Object()
+ {
+ *this = extensions;
+ }
+
+/*
+* Extensions Assignment Operator
+*/
+Extensions& Extensions::operator=(const Extensions& other)
+ {
+ 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;
+
+ return (*this);
+ }
+
+/*
+* Return the OID of this extension
+*/
+OID Certificate_Extension::oid_of() const
+ {
+ return OIDS::lookup(oid_name());
+ }
+
+/*
+* 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)
+ {
+ }
+
+void Extensions::add(Certificate_Extension* extn, bool critical)
+ {
+ 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));
+ }
+
+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)
+ {
+ exts.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(ext.first->copy()), ext.second));
+ }
+ return exts;
+ }
+
+std::map<OID, std::pair<std::vector<byte>, bool>> Extensions::extensions_raw() const
+ {
+ return m_extensions_raw;
+ }
+
+/*
+* Encode an Extensions list
+*/
+void Extensions::encode_into(DER_Encoder& to_object) const
+ {
+ for(size_t i = 0; i != m_extensions.size(); ++i)
+ {
+ const Certificate_Extension* ext = m_extensions[i].first.get();
+ const bool is_critical = m_extensions[i].second;
+
+ const bool should_encode = ext->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();
+ }
+ }
+ }
+
+/*
+* Decode a list of Extensions
+*/
+void Extensions::decode_from(BER_Decoder& from_source)
+ {
+ m_extensions.clear();
+ m_extensions_raw.clear();
+
+ BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+
+ while(sequence.more_items())
+ {
+ OID oid;
+ std::vector<byte> value;
+ bool critical;
+
+ sequence.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+ .decode(value, OCTET_STRING)
+ .verify_end()
+ .end_cons();
+
+ m_extensions_raw.emplace(oid, std::make_pair(value, critical));
+
+ std::unique_ptr<Certificate_Extension> ext(get_extension(oid, critical));
+
+ if(!ext && critical && m_throw_on_unknown_critical)
+ throw Decoding_Error("Encountered unknown X.509 extension marked "
+ "as critical; OID = " + oid.as_string());
+
+ 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));
+ }
+ }
+
+ sequence.verify_end();
+ }
+
+/*
+* Write the extensions to an info store
+*/
+void Extensions::contents_to(Data_Store& subject_info,
+ Data_Store& issuer_info) const
+ {
+ for(size_t i = 0; i != m_extensions.size(); ++i)
+ {
+ 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));
+ }
+ }
+
+
+namespace Cert_Extension {
+
+/*
+* Checked accessor for the path_limit member
+*/
+size_t Basic_Constraints::get_path_limit() const
+ {
+ if(!m_is_ca)
+ throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA");
+ return m_path_limit;
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Basic_Constraints::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_if(m_is_ca,
+ DER_Encoder()
+ .encode(m_is_ca)
+ .encode_optional(m_path_limit, NO_CERT_PATH_LIMIT)
+ )
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Basic_Constraints::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false)
+ .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+ .verify_end()
+ .end_cons();
+
+ if(m_is_ca == false)
+ m_path_limit = 0;
+ }
+
+/*
+* Return a textual representation
+*/
+void Basic_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.BasicConstraints.is_ca", (m_is_ca ? 1 : 0));
+ subject.add("X509v3.BasicConstraints.path_constraint", static_cast<u32bit>(m_path_limit));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Key_Usage::encode_inner() const
+ {
+ if(m_constraints == NO_CONSTRAINTS)
+ throw Encoding_Error("Cannot encode zero usage constraints");
+
+ const size_t unused_bits = low_bit(m_constraints) - 1;
+
+ std::vector<byte> der;
+ der.push_back(BIT_STRING);
+ der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
+ der.push_back(unused_bits % 8);
+ der.push_back((m_constraints >> 8) & 0xFF);
+ if(m_constraints & 0xFF)
+ der.push_back(m_constraints & 0xFF);
+
+ return der;
+ }
+
+/*
+* Decode the extension
+*/
+void Key_Usage::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder ber(in);
+
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.type_tag != BIT_STRING || obj.class_tag != UNIVERSAL)
+ throw BER_Bad_Tag("Bad tag for usage constraint",
+ obj.type_tag, obj.class_tag);
+
+ if(obj.value.size() != 2 && obj.value.size() != 3)
+ throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint");
+
+ if(obj.value[0] >= 8)
+ throw BER_Decoding_Error("Invalid unused bits in usage constraint");
+
+ obj.value[obj.value.size()-1] &= (0xFF << obj.value[0]);
+
+ u16bit usage = 0;
+ for(size_t i = 1; i != obj.value.size(); ++i)
+ {
+ usage = (obj.value[i] << 8*(sizeof(usage)-i)) | usage;
+ }
+
+ m_constraints = Key_Constraints(usage);
+ }
+
+/*
+* Return a textual representation
+*/
+void Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.KeyUsage", m_constraints);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Subject_Key_ID::encode_inner() const
+ {
+ return DER_Encoder().encode(m_key_id, OCTET_STRING).get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Subject_Key_ID::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in).decode(m_key_id, OCTET_STRING).verify_end();
+ }
+
+/*
+* Return a textual representation
+*/
+void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.SubjectKeyIdentifier", m_key_id);
+ }
+
+/*
+* Subject_Key_ID Constructor
+*/
+Subject_Key_ID::Subject_Key_ID(const std::vector<byte>& pub_key) : m_key_id(unlock(SHA_160().process(pub_key)))
+ {}
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Authority_Key_ID::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(m_key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Authority_Key_ID::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional_string(m_key_id, OCTET_STRING, 0);
+ }
+
+/*
+* Return a textual representation
+*/
+void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
+ {
+ if(m_key_id.size())
+ issuer.add("X509v3.AuthorityKeyIdentifier", m_key_id);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Alternative_Name::encode_inner() const
+ {
+ return DER_Encoder().encode(m_alt_name).get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Alternative_Name::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
+
+/*
+* Return a textual representation
+*/
+void Alternative_Name::contents_to(Data_Store& subject_info,
+ Data_Store& issuer_info) const
+ {
+ 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);
+ }
+
+/*
+* Alternative_Name Constructor
+*/
+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)
+ {}
+
+/*
+* Subject_Alternative_Name Constructor
+*/
+Subject_Alternative_Name::Subject_Alternative_Name(
+ const AlternativeName& name) :
+ Alternative_Name(name, "X509v3.SubjectAlternativeName")
+ {
+ }
+
+/*
+* Issuer_Alternative_Name Constructor
+*/
+Issuer_Alternative_Name::Issuer_Alternative_Name(const AlternativeName& name) :
+ Alternative_Name(name, "X509v3.IssuerAlternativeName")
+ {
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Extended_Key_Usage::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(m_oids)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Extended_Key_Usage::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in).decode_list(m_oids);
+ }
+
+/*
+* Return a textual representation
+*/
+void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ subject.add("X509v3.ExtendedKeyUsage", m_oids[i].as_string());
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Name_Constraints::encode_inner() const
+ {
+ throw Not_Implemented("Name_Constraints encoding");
+ }
+
+
+/*
+* Decode the extension
+*/
+void Name_Constraints::decode_inner(const std::vector<byte>& in)
+ {
+ std::vector<GeneralSubtree> permit, exclude;
+ BER_Decoder ber(in);
+ BER_Decoder ext = ber.start_cons(SEQUENCE);
+ BER_Object per = ext.get_next_object();
+
+ ext.push_back(per);
+ if(per.type_tag == 0 && per.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ {
+ ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(permit.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ BER_Object exc = ext.get_next_object();
+ ext.push_back(exc);
+ if(per.type_tag == 1 && per.class_tag == ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ {
+ ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(exclude.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ ext.end_cons();
+
+ if(permit.empty() && exclude.empty())
+ throw Encoding_Error("Empty Name Contraint extension");
+
+ m_name_constraints = NameConstraints(std::move(permit),std::move(exclude));
+ }
+
+/*
+* Return a textual representation
+*/
+void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ std::stringstream ss;
+
+ for(const GeneralSubtree& gs: m_name_constraints.permitted())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.permitted", ss.str());
+ ss.str(std::string());
+ }
+ for(const GeneralSubtree& gs: m_name_constraints.excluded())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.excluded", ss.str());
+ ss.str(std::string());
+ }
+ }
+
+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+ {
+ if(!subject.is_CA_cert() || !subject.is_critical("X509v3.NameConstraints"))
+ cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+
+ const bool at_self_signed_root = (pos == cert_path.size() - 1);
+
+ // Check that all subordinate certs pass the name constraint
+ for(size_t j = 0; j <= pos; ++j)
+ {
+ if(pos == j && at_self_signed_root)
+ continue;
+
+ bool permitted = m_name_constraints.permitted().empty();
+ bool failed = false;
+
+ for(auto c: m_name_constraints.permitted())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::NotFound:
+ case GeneralName::MatchResult::All:
+ permitted = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer.is_critical("X509v3.NameConstraints");
+ permitted = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for(auto c: m_name_constraints.excluded())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::All:
+ case GeneralName::MatchResult::Some:
+ failed = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer.is_critical("X509v3.NameConstraints");
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(failed || !permitted)
+ {
+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+ }
+ }
+ }
+
+namespace {
+
+/*
+* A policy specifier
+*/
+class Policy_Information : public ASN1_Object
+ {
+ public:
+ Policy_Information() {}
+ explicit Policy_Information(const OID& oid) : m_oid(oid) {}
+
+ const OID& oid() const { return m_oid; }
+
+ void encode_into(DER_Encoder& codec) const override
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(m_oid)
+ .end_cons();
+ }
+
+ void decode_from(BER_Decoder& codec) override
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(m_oid)
+ .discard_remaining()
+ .end_cons();
+ }
+
+ private:
+ OID m_oid;
+ };
+
+}
+
+/*
+* Encode the extension
+*/
+std::vector<byte> Certificate_Policies::encode_inner() const
+ {
+ std::vector<Policy_Information> policies;
+
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ policies.push_back(Policy_Information(m_oids[i]));
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(policies)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void Certificate_Policies::decode_inner(const std::vector<byte>& in)
+ {
+ std::vector<Policy_Information> policies;
+
+ BER_Decoder(in).decode_list(policies);
+
+ m_oids.clear();
+ for(size_t i = 0; i != policies.size(); ++i)
+ m_oids.push_back(policies[i].oid());
+ }
+
+/*
+* Return a textual representation
+*/
+void Certificate_Policies::contents_to(Data_Store& info, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ info.add("X509v3.CertificatePolicies", m_oids[i].as_string());
+ }
+
+std::vector<byte> Authority_Information_Access::encode_inner() const
+ {
+ ASN1_String url(m_ocsp_responder, IA5_STRING);
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(OIDS::lookup("PKIX.OCSP"))
+ .add_object(ASN1_Tag(6), CONTEXT_SPECIFIC, url.iso_8859())
+ .end_cons()
+ .end_cons().get_contents_unlocked();
+ }
+
+void Authority_Information_Access::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder ber = BER_Decoder(in).start_cons(SEQUENCE);
+
+ while(ber.more_items())
+ {
+ OID oid;
+
+ BER_Decoder info = ber.start_cons(SEQUENCE);
+
+ info.decode(oid);
+
+ if(oid == OIDS::lookup("PKIX.OCSP"))
+ {
+ BER_Object name = info.get_next_object();
+
+ if(name.type_tag == 6 && name.class_tag == CONTEXT_SPECIFIC)
+ {
+ m_ocsp_responder = Charset::transcode(ASN1::to_string(name),
+ LATIN1_CHARSET,
+ LOCAL_CHARSET);
+ }
+
+ }
+ }
+ }
+
+void Authority_Information_Access::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ if(!m_ocsp_responder.empty())
+ subject.add("OCSP.responder", m_ocsp_responder);
+ }
+
+/*
+* Checked accessor for the crl_number member
+*/
+size_t CRL_Number::get_crl_number() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::get_crl_number: Not set");
+ return m_crl_number;
+ }
+
+/*
+* Copy a CRL_Number extension
+*/
+CRL_Number* CRL_Number::copy() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::copy: Not set");
+ return new CRL_Number(m_crl_number);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> CRL_Number::encode_inner() const
+ {
+ return DER_Encoder().encode(m_crl_number).get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_Number::decode_inner(const std::vector<byte>& in)
+ {
+ BER_Decoder(in).decode(m_crl_number);
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_Number::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLNumber", static_cast<u32bit>(m_crl_number));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<byte> CRL_ReasonCode::encode_inner() const
+ {
+ return DER_Encoder()
+ .encode(static_cast<size_t>(m_reason), ENUMERATED, UNIVERSAL)
+ .get_contents_unlocked();
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_ReasonCode::decode_inner(const std::vector<byte>& in)
+ {
+ size_t reason_code = 0;
+ BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+ m_reason = static_cast<CRL_Code>(reason_code);
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_ReasonCode::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLReasonCode", m_reason);
+ }
+
+std::vector<byte> CRL_Distribution_Points::encode_inner() const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::decode_inner(const std::vector<byte>& buf)
+ {
+ 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);
+ }
+ }
+
+void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .decode_optional_implicit(m_point, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED),
+ SEQUENCE, CONSTRUCTED)
+ .end_cons().end_cons();
+ }
+
+std::vector<byte> Unknown_Critical_Extension::encode_inner() const
+ {
+ throw Not_Implemented("Unknown_Critical_Extension encoding");
+ }
+
+void Unknown_Critical_Extension::decode_inner(const std::vector<byte>&)
+ {
+ }
+
+void Unknown_Critical_Extension::contents_to(Data_Store&, Data_Store&) const
+ {
+ }
+
+}
+
+}
diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h
new file mode 100644
index 000000000..b1984fa94
--- /dev/null
+++ b/src/lib/x509/x509_ext.h
@@ -0,0 +1,505 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_EXTENSIONS_H__
+#define BOTAN_X509_EXTENSIONS_H__
+
+#include <botan/asn1_obj.h>
+#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>
+
+namespace Botan {
+
+class X509_Certificate;
+
+/**
+* X.509 Certificate Extension
+*/
+class BOTAN_DLL Certificate_Extension
+ {
+ public:
+ /**
+ * @return OID representing this extension
+ */
+ virtual OID oid_of() const;
+
+ /**
+ * Make a copy of this extension
+ * @return copy of this
+ */
+ virtual Certificate_Extension* copy() const = 0;
+
+ /*
+ * Add the contents of this extension into the information
+ * for the subject and/or issuer, as necessary.
+ * @param subject the subject info
+ * @param issuer the issuer info
+ */
+ virtual void contents_to(Data_Store& subject,
+ 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
+ * the path during path validation.
+ *
+ * If an error occurs during validation of this extension,
+ * an appropriate status code shall be added to cert_status.
+ *
+ * @param subject Subject certificate that contains this extension
+ * @param issuer Issuer certificate
+ * @param status Certificate validation status codes for subject certificate
+ * @param cert_path Certificate path which is currently validated
+ * @param pos Position of subject certificate in cert_path
+ */
+ virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos);
+
+ virtual ~Certificate_Extension() {}
+ protected:
+ friend class Extensions;
+ virtual bool should_encode() const { return true; }
+ virtual std::vector<byte> encode_inner() const = 0;
+ virtual void decode_inner(const std::vector<byte>&) = 0;
+ };
+
+/**
+* X.509 Certificate Extension List
+*/
+class BOTAN_DLL Extensions : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ void add(Certificate_Extension* extn, bool critical = false);
+
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
+
+ std::map<OID, std::pair<std::vector<byte>, bool>> extensions_raw() const;
+
+ Extensions& operator=(const Extensions&);
+
+ Extensions(const Extensions&);
+
+ explicit Extensions(bool st = true) : m_throw_on_unknown_critical(st) {}
+
+ private:
+ static Certificate_Extension* get_extension(const OID&, bool);
+
+ 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<byte>, bool>> m_extensions_raw;
+ };
+
+namespace Cert_Extension {
+
+static const size_t NO_CERT_PATH_LIMIT = 0xFFFFFFF0;
+
+/**
+* Basic Constraints Extension
+*/
+class BOTAN_DLL Basic_Constraints final : public Certificate_Extension
+ {
+ public:
+ Basic_Constraints* copy() const override
+ { return new Basic_Constraints(m_is_ca, m_path_limit); }
+
+ Basic_Constraints(bool ca = false, size_t limit = 0) :
+ m_is_ca(ca), m_path_limit(limit) {}
+
+ bool get_is_ca() const { return m_is_ca; }
+ size_t get_path_limit() const;
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.BasicConstraints"; }
+
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_is_ca;
+ size_t m_path_limit;
+ };
+
+/**
+* Key Usage Constraints Extension
+*/
+class BOTAN_DLL Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Key_Usage* copy() const override { return new Key_Usage(m_constraints); }
+
+ explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
+
+ Key_Constraints get_constraints() const { return m_constraints; }
+
+ private:
+ std::string oid_name() const override { return "X509v3.KeyUsage"; }
+
+ bool should_encode() const override
+ { return (m_constraints != NO_CONSTRAINTS); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ Key_Constraints m_constraints;
+ };
+
+/**
+* Subject Key Identifier Extension
+*/
+class BOTAN_DLL Subject_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Subject_Key_ID* copy() const override
+ { return new Subject_Key_ID(m_key_id); }
+
+ Subject_Key_ID() {}
+ explicit Subject_Key_ID(const std::vector<byte>&);
+
+ std::vector<byte> get_key_id() const { return m_key_id; }
+ private:
+ std::string oid_name() const override
+ { return "X509v3.SubjectKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<byte> m_key_id;
+ };
+
+/**
+* Authority Key Identifier Extension
+*/
+class BOTAN_DLL Authority_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Authority_Key_ID* copy() const override
+ { return new Authority_Key_ID(m_key_id); }
+
+ Authority_Key_ID() {}
+ explicit Authority_Key_ID(const std::vector<byte>& k) : m_key_id(k) {}
+
+ std::vector<byte> get_key_id() const { return m_key_id; }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.AuthorityKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<byte> m_key_id;
+ };
+
+/**
+* Alternative Name Extension Base Class
+*/
+class BOTAN_DLL Alternative_Name : public Certificate_Extension
+ {
+ public:
+ AlternativeName get_alt_name() const { return m_alt_name; }
+
+ protected:
+ Alternative_Name(const AlternativeName&, const std::string& oid_name);
+
+ Alternative_Name(const std::string&, const std::string&);
+
+ private:
+ std::string oid_name() const override { return m_oid_name_str; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::string m_oid_name_str;
+ AlternativeName m_alt_name;
+ };
+
+/**
+* Subject Alternative Name Extension
+*/
+class BOTAN_DLL Subject_Alternative_Name : public Alternative_Name
+ {
+ public:
+ Subject_Alternative_Name* copy() const override
+ { return new Subject_Alternative_Name(get_alt_name()); }
+
+ explicit Subject_Alternative_Name(const AlternativeName& = AlternativeName());
+ };
+
+/**
+* Issuer Alternative Name Extension
+*/
+class BOTAN_DLL Issuer_Alternative_Name : 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());
+ };
+
+/**
+* Extended Key Usage Extension
+*/
+class BOTAN_DLL Extended_Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Extended_Key_Usage* copy() const override
+ { return new Extended_Key_Usage(m_oids); }
+
+ Extended_Key_Usage() {}
+ explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
+
+ std::vector<OID> get_oids() const { return m_oids; }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.ExtendedKeyUsage"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+/**
+* Name Constraints
+*/
+class BOTAN_DLL Name_Constraints : public Certificate_Extension
+ {
+ public:
+ Name_Constraints* copy() const override
+ { return new Name_Constraints(m_name_constraints); }
+
+ Name_Constraints() {}
+ Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
+
+ void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override;
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.NameConstraints"; }
+
+ bool should_encode() const override { return true; }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ NameConstraints m_name_constraints;
+ };
+
+/**
+* Certificate Policies Extension
+*/
+class BOTAN_DLL Certificate_Policies final : public Certificate_Extension
+ {
+ public:
+ Certificate_Policies* copy() const override
+ { return new Certificate_Policies(m_oids); }
+
+ Certificate_Policies() {}
+ explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
+
+ std::vector<OID> get_oids() const { return m_oids; }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CertificatePolicies"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+class BOTAN_DLL Authority_Information_Access final : public Certificate_Extension
+ {
+ public:
+ Authority_Information_Access* copy() const override
+ { return new Authority_Information_Access(m_ocsp_responder); }
+
+ Authority_Information_Access() {}
+
+ explicit Authority_Information_Access(const std::string& ocsp) :
+ m_ocsp_responder(ocsp) {}
+
+ private:
+ std::string oid_name() const override
+ { return "PKIX.AuthorityInformationAccess"; }
+
+ bool should_encode() const override { return (!m_ocsp_responder.empty()); }
+
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::string m_ocsp_responder;
+ };
+
+/**
+* CRL Number Extension
+*/
+class BOTAN_DLL CRL_Number final : public Certificate_Extension
+ {
+ public:
+ CRL_Number* copy() const override;
+
+ CRL_Number() : m_has_value(false), m_crl_number(0) {}
+ CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {}
+
+ size_t get_crl_number() const;
+
+ private:
+ std::string oid_name() const override { return "X509v3.CRLNumber"; }
+
+ bool should_encode() const override { return m_has_value; }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_has_value;
+ size_t m_crl_number;
+ };
+
+/**
+* CRL Entry Reason Code Extension
+*/
+class BOTAN_DLL CRL_ReasonCode final : public Certificate_Extension
+ {
+ public:
+ CRL_ReasonCode* copy() const override
+ { return new CRL_ReasonCode(m_reason); }
+
+ explicit CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : m_reason(r) {}
+
+ CRL_Code get_reason() const { return m_reason; }
+
+ private:
+ std::string oid_name() const override { return "X509v3.ReasonCode"; }
+
+ bool should_encode() const override { return (m_reason != UNSPECIFIED); }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ CRL_Code m_reason;
+ };
+
+/**
+* CRL Distribution Points Extension
+*/
+class BOTAN_DLL CRL_Distribution_Points final : public Certificate_Extension
+ {
+ public:
+ class BOTAN_DLL Distribution_Point final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ const AlternativeName& point() const { return m_point; }
+ private:
+ AlternativeName m_point;
+ };
+
+ CRL_Distribution_Points* copy() const override
+ { return new CRL_Distribution_Points(m_distribution_points); }
+
+ CRL_Distribution_Points() {}
+
+ explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) :
+ m_distribution_points(points) {}
+
+ std::vector<Distribution_Point> distribution_points() const
+ { return m_distribution_points; }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CRLDistributionPoints"; }
+
+ bool should_encode() const override
+ { return !m_distribution_points.empty(); }
+
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<Distribution_Point> m_distribution_points;
+ };
+
+/**
+* An unknown X.509 extension marked as critical
+* Will always add a failure to the path validation result.
+*/
+class BOTAN_DLL Unknown_Critical_Extension final : public Certificate_Extension
+ {
+ public:
+ explicit Unknown_Critical_Extension(OID oid) : m_oid(oid) {}
+
+ Unknown_Critical_Extension* copy() const override
+ { return new Unknown_Critical_Extension(m_oid); }
+
+ OID oid_of() const override
+ { return m_oid; };
+
+ 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);
+ }
+
+ private:
+ std::string oid_name() const override
+ { return "Unknown OID name"; }
+
+ bool should_encode() const override { return false; }
+ std::vector<byte> encode_inner() const override;
+ void decode_inner(const std::vector<byte>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ OID m_oid;
+ };
+
+}
+
+}
+
+#endif
diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp
new file mode 100644
index 000000000..3c5d2a9b4
--- /dev/null
+++ b/src/lib/x509/x509_obj.cpp
@@ -0,0 +1,249 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_obj.h>
+#include <botan/x509_key.h>
+#include <botan/pubkey.h>
+#include <botan/oids.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/pem.h>
+#include <algorithm>
+
+namespace Botan {
+
+/*
+* Create a generic X.509 object
+*/
+X509_Object::X509_Object(DataSource& stream, const std::string& labels)
+ {
+ init(stream, labels);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* Create a generic X.509 object
+*/
+X509_Object::X509_Object(const std::string& file, const std::string& labels)
+ {
+ DataSource_Stream stream(file, true);
+ init(stream, labels);
+ }
+#endif
+
+/*
+* Create a generic X.509 object
+*/
+X509_Object::X509_Object(const std::vector<byte>& vec, const std::string& labels)
+ {
+ DataSource_Memory stream(vec.data(), vec.size());
+ init(stream, labels);
+ }
+
+/*
+* Read a PEM or BER X.509 object
+*/
+void X509_Object::init(DataSource& in, const std::string& labels)
+ {
+ m_PEM_labels_allowed = split_on(labels, '/');
+ if(m_PEM_labels_allowed.size() < 1)
+ throw Invalid_Argument("Bad labels argument to X509_Object");
+
+ m_PEM_label_pref = m_PEM_labels_allowed[0];
+ std::sort(m_PEM_labels_allowed.begin(), m_PEM_labels_allowed.end());
+
+ try {
+ if(ASN1::maybe_BER(in) && !PEM_Code::matches(in))
+ {
+ BER_Decoder dec(in);
+ decode_from(dec);
+ }
+ else
+ {
+ std::string got_label;
+ DataSource_Memory ber(PEM_Code::decode(in, got_label));
+
+ if(!std::binary_search(m_PEM_labels_allowed.begin(),
+ m_PEM_labels_allowed.end(), got_label))
+ throw Decoding_Error("Invalid PEM label: " + got_label);
+
+ BER_Decoder dec(ber);
+ decode_from(dec);
+ }
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error(m_PEM_label_pref + " decoding failed: " + e.what());
+ }
+ }
+
+
+void X509_Object::encode_into(DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .encode(m_sig_algo)
+ .encode(m_sig, BIT_STRING)
+ .end_cons();
+ }
+
+/*
+* Read a BER encoded X.509 object
+*/
+void X509_Object::decode_from(BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .decode(m_sig_algo)
+ .decode(m_sig, BIT_STRING)
+ .verify_end()
+ .end_cons();
+ }
+
+/*
+* Return a BER encoded X.509 object
+*/
+std::vector<byte> X509_Object::BER_encode() const
+ {
+ DER_Encoder der;
+ encode_into(der);
+ return der.get_contents_unlocked();
+ }
+
+/*
+* Return a PEM encoded X.509 object
+*/
+std::string X509_Object::PEM_encode() const
+ {
+ return PEM_Code::encode(BER_encode(), m_PEM_label_pref);
+ }
+
+/*
+* Return the TBS data
+*/
+std::vector<byte> X509_Object::tbs_data() const
+ {
+ return ASN1::put_in_sequence(m_tbs_bits);
+ }
+
+/*
+* Return the signature of this object
+*/
+std::vector<byte> X509_Object::signature() const
+ {
+ return m_sig;
+ }
+
+/*
+* Return the algorithm used to sign this object
+*/
+AlgorithmIdentifier X509_Object::signature_algorithm() const
+ {
+ return m_sig_algo;
+ }
+
+/*
+* Return the hash used in generating the signature
+*/
+std::string X509_Object::hash_used_for_signature() const
+ {
+ std::vector<std::string> sig_info =
+ split_on(OIDS::lookup(m_sig_algo.oid), '/');
+
+ if(sig_info.size() != 2)
+ throw Internal_Error("Invalid name format found for " +
+ m_sig_algo.oid.as_string());
+
+ std::vector<std::string> pad_and_hash =
+ parse_algorithm_name(sig_info[1]);
+
+ if(pad_and_hash.size() != 2)
+ throw Internal_Error("Invalid name format " + sig_info[1]);
+
+ return pad_and_hash[1];
+ }
+
+/*
+* Check the signature on an object
+*/
+bool X509_Object::check_signature(const Public_Key* pub_key) const
+ {
+ if(!pub_key)
+ throw Exception("No key provided for " + m_PEM_label_pref + " signature check");
+ std::unique_ptr<const Public_Key> key(pub_key);
+ return check_signature(*key);
+}
+
+/*
+* Check the signature on an object
+*/
+bool X509_Object::check_signature(const Public_Key& pub_key) const
+ {
+ try {
+ std::vector<std::string> sig_info =
+ split_on(OIDS::lookup(m_sig_algo.oid), '/');
+
+ if(sig_info.size() != 2 || sig_info[0] != pub_key.algo_name())
+ return false;
+
+ std::string padding = sig_info[1];
+ Signature_Format format =
+ (pub_key.message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+
+ PK_Verifier verifier(pub_key, padding, format);
+
+ return verifier.verify_message(tbs_data(), signature());
+ }
+ catch(std::exception&)
+ {
+ return false;
+ }
+ }
+
+/*
+* Apply the X.509 SIGNED macro
+*/
+std::vector<byte> X509_Object::make_signed(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& algo,
+ const secure_vector<byte>& tbs_bits)
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .encode(algo)
+ .encode(signer->sign_message(tbs_bits, rng), BIT_STRING)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Try to decode the actual information
+*/
+void X509_Object::do_decode()
+ {
+ try {
+ force_decode();
+ }
+ catch(Decoding_Error& e)
+ {
+ 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() + ")");
+ }
+ }
+
+}
diff --git a/src/lib/x509/x509_obj.h b/src/lib/x509/x509_obj.h
new file mode 100644
index 000000000..40324775c
--- /dev/null
+++ b/src/lib/x509/x509_obj.h
@@ -0,0 +1,119 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_OBJECT_H__
+#define BOTAN_X509_OBJECT_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/x509_key.h>
+#include <botan/rng.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* This class represents abstract X.509 signed objects as
+* in the X.500 SIGNED macro
+*/
+class BOTAN_DLL X509_Object : public ASN1_Object
+ {
+ public:
+ /**
+ * The underlying data that is to be or was signed
+ * @return data that is or was signed
+ */
+ std::vector<byte> tbs_data() const;
+
+ /**
+ * @return signature on tbs_data()
+ */
+ std::vector<byte> signature() const;
+
+ /**
+ * @return signature algorithm that was used to generate signature
+ */
+ AlgorithmIdentifier signature_algorithm() const;
+
+ /**
+ * @return hash algorithm that was used to generate signature
+ */
+ std::string hash_used_for_signature() const;
+
+ /**
+ * Create a signed X509 object.
+ * @param signer the signer used to sign the object
+ * @param rng the random number generator to use
+ * @param alg_id the algorithm identifier of the signature scheme
+ * @param tbs the tbs bits to be signed
+ * @return signed X509 object
+ */
+ static std::vector<byte> make_signed(class PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& alg_id,
+ const secure_vector<byte>& tbs);
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key& key) const;
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * the pointer will be deleted after use
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key* key) const;
+
+ /**
+ * DER encode an X509_Object
+ * See @ref ASN1_Object::encode_into()
+ */
+ void encode_into(class DER_Encoder& to) const override;
+
+ /**
+ * Decode a BER encoded X509_Object
+ * See @ref ASN1_Object::decode_from()
+ */
+ void decode_from(class BER_Decoder& from) override;
+
+ /**
+ * @return BER encoding of this
+ */
+ std::vector<byte> BER_encode() const;
+
+ /**
+ * @return PEM encoding of this
+ */
+ std::string PEM_encode() const;
+
+ virtual ~X509_Object() {}
+ protected:
+ X509_Object(DataSource& src, const std::string& pem_labels);
+ X509_Object(const std::vector<byte>& vec, const std::string& labels);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ X509_Object(const std::string& file, const std::string& pem_labels);
+#endif
+
+ void do_decode();
+ X509_Object() {}
+ AlgorithmIdentifier m_sig_algo;
+ std::vector<byte> m_tbs_bits, m_sig;
+ private:
+ virtual void force_decode() = 0;
+ void init(DataSource&, const std::string&);
+
+ std::vector<std::string> m_PEM_labels_allowed;
+ std::string m_PEM_label_pref;
+ };
+
+}
+
+#endif
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
new file mode 100644
index 000000000..f56495a79
--- /dev/null
+++ b/src/lib/x509/x509cert.cpp
@@ -0,0 +1,672 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010,2015 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/internal/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<std::string>& in)
+ {
+ std::vector<std::string> out;
+
+ for(auto i = in.begin(); i != in.end(); ++i)
+ out.push_back(OIDS::lookup(OID(*i)));
+ return out;
+ }
+
+}
+
+/*
+* X509_Certificate Constructor
+*/
+X509_Certificate::X509_Certificate(DataSource& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
+ m_self_signed(false),
+ m_v3_extensions(false)
+ {
+ do_decode();
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+/*
+* 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)
+ {
+ do_decode();
+ }
+#endif
+
+/*
+* X509_Certificate Constructor
+*/
+X509_Certificate::X509_Certificate(const std::vector<byte>& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE"),
+ m_self_signed(false),
+ m_v3_extensions(false)
+ {
+ do_decode();
+ }
+
+/*
+* Decode the TBSCertificate data
+*/
+void X509_Certificate::force_decode()
+ {
+ size_t version;
+ BigInt serial_bn;
+ AlgorithmIdentifier sig_algo_inner;
+ X509_DN dn_issuer, dn_subject;
+ X509_Time start, end;
+
+ BER_Decoder tbs_cert(m_tbs_bits);
+
+ tbs_cert.decode_optional(version, ASN1_Tag(0),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ .decode(serial_bn)
+ .decode(sig_algo_inner)
+ .decode(dn_issuer)
+ .start_cons(SEQUENCE)
+ .decode(start)
+ .decode(end)
+ .verify_end()
+ .end_cons()
+ .decode(dn_subject);
+
+ if(version > 2)
+ throw Decoding_Error("Unknown X.509 cert version " + std::to_string(version));
+ if(m_sig_algo != sig_algo_inner)
+ throw Decoding_Error("Algorithm identifier mismatch");
+
+
+ m_subject.add(dn_subject.contents());
+ m_issuer.add(dn_issuer.contents());
+
+ 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()));
+
+ 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);
+
+ std::vector<byte> v2_issuer_key_id, v2_subject_key_id;
+
+ 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_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);
+ }
+ 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");
+
+ m_subject.add("X509.Certificate.version", static_cast<u32bit>(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());
+
+ m_issuer.add("X509.Certificate.v2.key_id", v2_issuer_key_id);
+ m_subject.add("X509.Certificate.v2.key_id", v2_subject_key_id);
+
+ m_subject.add("X509.Certificate.public_key",
+ hex_encode(public_key.value));
+
+ std::unique_ptr<Public_Key> pub_key(subject_public_key());
+ m_self_signed = (dn_subject == dn_issuer) && check_signature(*pub_key);
+
+ if(m_self_signed && version == 0)
+ {
+ m_subject.add("X509v3.BasicConstraints.is_ca", 1);
+ m_subject.add("X509v3.BasicConstraints.path_constraint", Cert_Extension::NO_CERT_PATH_LIMIT);
+ }
+
+ if(is_CA_cert() &&
+ !m_subject.has_value("X509v3.BasicConstraints.path_constraint"))
+ {
+ const size_t limit = (x509_version() < 3) ?
+ Cert_Extension::NO_CERT_PATH_LIMIT : 0;
+
+ m_subject.add("X509v3.BasicConstraints.path_constraint", static_cast<u32bit>(limit));
+ }
+ }
+
+/*
+* Return the X.509 version in use
+*/
+u32bit X509_Certificate::x509_version() const
+ {
+ return (m_subject.get1_u32bit("X509.Certificate.version") + 1);
+ }
+
+/*
+* Return the time this cert becomes valid
+*/
+std::string X509_Certificate::start_time() const
+ {
+ return m_subject.get1("X509.Certificate.start");
+ }
+
+/*
+* Return the time this cert becomes invalid
+*/
+std::string X509_Certificate::end_time() const
+ {
+ return m_subject.get1("X509.Certificate.end");
+ }
+
+/*
+* Return information about the subject
+*/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& what) const
+ {
+ return m_subject.get(X509_DN::deref_info_field(what));
+ }
+
+/*
+* Return information about the issuer
+*/
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& what) const
+ {
+ return m_issuer.get(X509_DN::deref_info_field(what));
+ }
+
+/*
+* Return the public key in this certificate
+*/
+Public_Key* X509_Certificate::subject_public_key() const
+ {
+ return X509::load_key(
+ ASN1::put_in_sequence(this->subject_public_key_bits()));
+ }
+
+std::vector<byte> X509_Certificate::subject_public_key_bits() const
+ {
+ return hex_decode(m_subject.get1("X509.Certificate.public_key"));
+ }
+
+/*
+* Check if the certificate is for a CA
+*/
+bool X509_Certificate::is_CA_cert() const
+ {
+ if(!m_subject.get1_u32bit("X509v3.BasicConstraints.is_ca"))
+ return false;
+
+ return allowed_usage(Key_Constraints(KEY_CERT_SIGN));
+ }
+
+bool X509_Certificate::allowed_usage(Key_Constraints usage) const
+ {
+ if(constraints() == NO_CONSTRAINTS)
+ return true;
+ return ((constraints() & usage) == usage);
+ }
+
+bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
+ {
+ const std::vector<std::string> ex = ex_constraints();
+
+ if(ex.empty())
+ return true;
+
+ if(std::find(ex.begin(), ex.end(), usage) != ex.end())
+ return true;
+
+ return false;
+ }
+
+bool X509_Certificate::allowed_usage(Usage_Type usage) const
+ {
+ // These follow suggestions in RFC 5280 4.2.1.12
+
+ switch(usage)
+ {
+ case Usage_Type::UNSPECIFIED:
+ return true;
+
+ case Usage_Type::TLS_SERVER_AUTH:
+ return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+
+ case Usage_Type::TLS_CLIENT_AUTH:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+
+ case Usage_Type::OCSP_RESPONDER:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning");
+
+ case Usage_Type::CERTIFICATE_AUTHORITY:
+ return is_CA_cert();
+ }
+
+ return false;
+ }
+
+bool X509_Certificate::has_constraints(Key_Constraints constraints) const
+ {
+ if(this->constraints() == NO_CONSTRAINTS)
+ {
+ return false;
+ }
+
+ return ((this->constraints() & constraints) != 0);
+ }
+
+bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
+ {
+ 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 the path length constraint
+*/
+u32bit X509_Certificate::path_limit() const
+ {
+ return m_subject.get1_u32bit("X509v3.BasicConstraints.path_constraint", 0);
+ }
+
+/*
+* Return if a certificate extension is marked critical
+*/
+bool X509_Certificate::is_critical(const std::string& ex_name) const
+ {
+ return !!m_subject.get1_u32bit(ex_name + ".is_critical",0);
+ }
+
+/*
+* Return the key usage constraints
+*/
+Key_Constraints X509_Certificate::constraints() const
+ {
+ return Key_Constraints(m_subject.get1_u32bit("X509v3.KeyUsage",
+ NO_CONSTRAINTS));
+ }
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
+ {
+ return lookup_oids(m_subject.get("X509v3.ExtendedKeyUsage"));
+ }
+
+/*
+* Return the name constraints
+*/
+NameConstraints X509_Certificate::name_constraints() 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 NameConstraints(std::move(permit),std::move(exclude));
+ }
+
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
+ {
+ return lookup_oids(m_subject.get("X509v3.CertificatePolicies"));
+ }
+
+Extensions X509_Certificate::v3_extensions() const
+ {
+ return m_v3_extensions;
+ }
+
+std::string X509_Certificate::ocsp_responder() const
+ {
+ return m_subject.get1("OCSP.responder", "");
+ }
+
+std::string X509_Certificate::crl_distribution_point() const
+ {
+ return m_subject.get1("CRL.DistributionPoint", "");
+ }
+
+/*
+* Return the authority key id
+*/
+std::vector<byte> X509_Certificate::authority_key_id() const
+ {
+ return m_issuer.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ }
+
+/*
+* Return the subject key id
+*/
+std::vector<byte> X509_Certificate::subject_key_id() const
+ {
+ return m_subject.get1_memvec("X509v3.SubjectKeyIdentifier");
+ }
+
+/*
+* Return the certificate serial number
+*/
+std::vector<byte> X509_Certificate::serial_number() const
+ {
+ return m_subject.get1_memvec("X509.Certificate.serial");
+ }
+
+X509_DN X509_Certificate::issuer_dn() const
+ {
+ return create_dn(m_issuer);
+ }
+
+std::vector<byte> X509_Certificate::raw_issuer_dn() const
+ {
+ return m_issuer.get1_memvec("X509.Certificate.dn_bits");
+ }
+
+X509_DN X509_Certificate::subject_dn() const
+ {
+ return create_dn(m_subject);
+ }
+
+std::vector<byte> X509_Certificate::raw_subject_dn() const
+ {
+ return m_subject.get1_memvec("X509.Certificate.dn_bits");
+ }
+
+std::string X509_Certificate::fingerprint(const std::string& hash_name) const
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create(hash_name));
+ hash->update(this->BER_encode());
+ const auto hex_print = hex_encode(hash->final());
+
+ std::string formatted_print;
+
+ for(size_t i = 0; i != hex_print.size(); i += 2)
+ {
+ formatted_print.push_back(hex_print[i]);
+ formatted_print.push_back(hex_print[i+1]);
+
+ if(i != hex_print.size() - 2)
+ formatted_print.push_back(':');
+ }
+
+ return formatted_print;
+ }
+
+bool X509_Certificate::matches_dns_name(const std::string& name) const
+ {
+ if(name.empty())
+ return false;
+
+ std::vector<std::string> issued_names = subject_info("DNS");
+
+ // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+ if(issued_names.empty())
+ issued_names = subject_info("Name");
+
+ for(size_t i = 0; i != issued_names.size(); ++i)
+ {
+ if(host_wildcard_match(issued_names[i], name))
+ return true;
+ }
+
+ return false;
+ }
+
+/*
+* Compare two certificates for equality
+*/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+ {
+ return (m_sig == other.m_sig &&
+ m_sig_algo == other.m_sig_algo &&
+ m_self_signed == other.m_self_signed &&
+ m_issuer == other.m_issuer &&
+ m_subject == other.m_subject);
+ }
+
+bool X509_Certificate::operator<(const X509_Certificate& other) const
+ {
+ /* If signature values are not equal, sort by lexicographic ordering of that */
+ if(m_sig != other.m_sig)
+ {
+ if(m_sig < other.m_sig)
+ return true;
+ return false;
+ }
+
+ // Then compare the signed contents
+ return m_tbs_bits < other.m_tbs_bits;
+ }
+
+/*
+* X.509 Certificate Comparison
+*/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+ {
+ return !(cert1 == 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 << "Constraints:\n";
+ Key_Constraints constraints = this->constraints();
+ if(constraints == NO_CONSTRAINTS)
+ out << " None\n";
+ else
+ {
+ if(constraints & DIGITAL_SIGNATURE)
+ out << " Digital Signature\n";
+ if(constraints & NON_REPUDIATION)
+ out << " Non-Repudiation\n";
+ if(constraints & KEY_ENCIPHERMENT)
+ out << " Key Encipherment\n";
+ if(constraints & DATA_ENCIPHERMENT)
+ out << " Data Encipherment\n";
+ if(constraints & KEY_AGREEMENT)
+ out << " Key Agreement\n";
+ if(constraints & KEY_CERT_SIGN)
+ out << " Cert Sign\n";
+ if(constraints & CRL_SIGN)
+ out << " CRL Sign\n";
+ if(constraints & ENCIPHER_ONLY)
+ out << " Encipher Only\n";
+ if(constraints & DECIPHER_ONLY)
+ out << " Decipher Only\n";
+ }
+
+ std::vector<std::string> policies = this->policies();
+ if(!policies.empty())
+ {
+ out << "Policies: " << "\n";
+ for(size_t i = 0; i != policies.size(); i++)
+ out << " " << policies[i] << "\n";
+ }
+
+ std::vector<std::string> ex_constraints = this->ex_constraints();
+ if(!ex_constraints.empty())
+ {
+ out << "Extended Constraints:\n";
+ for(size_t i = 0; i != ex_constraints.size(); i++)
+ out << " " << ex_constraints[i] << "\n";
+ }
+
+ NameConstraints name_constraints = this->name_constraints();
+ if(!name_constraints.permitted().empty() ||
+ !name_constraints.excluded().empty())
+ {
+ out << "Name Constraints:\n";
+
+ if(!name_constraints.permitted().empty())
+ {
+ out << " Permit";
+ for(auto st: name_constraints.permitted())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+
+ if(!name_constraints.excluded().empty())
+ {
+ out << " Exclude";
+ for(auto st: name_constraints.excluded())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+ }
+
+ if(!ocsp_responder().empty())
+ out << "OCSP responder " << ocsp_responder() << "\n";
+ if(!crl_distribution_point().empty())
+ out << "CRL " << crl_distribution_point() << "\n";
+
+ out << "Signature algorithm: " <<
+ OIDS::lookup(this->signature_algorithm().oid) << "\n";
+
+ out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+ if(this->authority_key_id().size())
+ out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+ if(this->subject_key_id().size())
+ out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+ std::unique_ptr<X509_PublicKey> pubkey(this->subject_public_key());
+ out << "Public Key:\n" << 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
new file mode 100644
index 000000000..acdba7e02
--- /dev/null
+++ b/src/lib/x509/x509cert.h
@@ -0,0 +1,330 @@
+/*
+* X.509 Certificates
+* (C) 1999-2007,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERTS_H__
+#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/key_constraint.h>
+#include <botan/name_constraint.h>
+#include <map>
+#include <memory>
+
+namespace Botan {
+
+enum class Usage_Type
+ {
+ UNSPECIFIED, // no restrictions
+ TLS_SERVER_AUTH,
+ TLS_CLIENT_AUTH,
+ CERTIFICATE_AUTHORITY,
+ OCSP_RESPONDER
+ };
+
+/**
+* This class represents X.509 Certificate
+*/
+class BOTAN_DLL X509_Certificate : public X509_Object
+ {
+ public:
+ /**
+ * Get the public key associated with this certificate.
+ * @return subject public key of this certificate
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Get the public key associated with this certificate.
+ * @return subject public key of this certificate
+ */
+ std::vector<byte> subject_public_key_bits() const;
+
+ /**
+ * Get the certificate's issuer distinguished name (DN).
+ * @return issuer DN of this certificate
+ */
+ X509_DN issuer_dn() const;
+
+ /**
+ * Get the certificate's subject distinguished name (DN).
+ * @return subject DN of this certificate
+ */
+ X509_DN subject_dn() const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names are
+ * "X509.Certificate.version", "X509.Certificate.serial",
+ * "X509.Certificate.start", "X509.Certificate.end",
+ * "X509.Certificate.v2.key_id", "X509.Certificate.public_key",
+ * "X509v3.BasicConstraints.path_constraint",
+ * "X509v3.BasicConstraints.is_ca", "X509v3.NameConstraints",
+ * "X509v3.ExtendedKeyUsage", "X509v3.CertificatePolicies",
+ * "X509v3.SubjectKeyIdentifier" or "X509.Certificate.serial".
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> subject_info(const std::string& name) const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names are
+ * "X509.Certificate.v2.key_id" or "X509v3.AuthorityKeyIdentifier".
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> issuer_info(const std::string& name) const;
+
+ /**
+ * Raw subject DN
+ */
+ std::vector<byte> raw_issuer_dn() const;
+
+ /**
+ * Raw issuer DN
+ */
+ std::vector<byte> raw_subject_dn() const;
+
+ /**
+ * Get the notBefore of the certificate.
+ * @return notBefore of the certificate
+ */
+ std::string start_time() const;
+
+ /**
+ * Get the notAfter of the certificate.
+ * @return notAfter of the certificate
+ */
+ std::string end_time() const;
+
+ /**
+ * Get the X509 version of this certificate object.
+ * @return X509 version
+ */
+ u32bit x509_version() const;
+
+ /**
+ * Get the serial number of this certificate.
+ * @return certificates serial number
+ */
+ std::vector<byte> serial_number() const;
+
+ /**
+ * Get the DER encoded AuthorityKeyIdentifier of this certificate.
+ * @return DER encoded AuthorityKeyIdentifier
+ */
+ std::vector<byte> authority_key_id() const;
+
+ /**
+ * Get the DER encoded SubjectKeyIdentifier of this certificate.
+ * @return DER encoded SubjectKeyIdentifier
+ */
+ std::vector<byte> subject_key_id() const;
+
+ /**
+ * Check whether this certificate is self signed.
+ * @return true if this certificate is self signed
+ */
+ bool is_self_signed() const { return m_self_signed; }
+
+ /**
+ * Check whether this certificate is a CA certificate.
+ * @return true if this certificate is a CA certificate
+ */
+ bool is_CA_cert() const;
+
+ /**
+ * Returns true if the specified @param usage is set in the key usage extension
+ * or if no key usage constraints are set at all.
+ * To check if a certain key constraint is set in the certificate
+ * use @see X509_Certificate#has_constraints.
+ */
+ bool allowed_usage(Key_Constraints usage) const;
+
+ /**
+ * Returns true if the specified @param 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 std::string& 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.
+ 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.
+ */
+ bool has_ex_constraint(const std::string& ex_constraint) const;
+
+ /**
+ * Get the path limit as defined in the BasicConstraints extension of
+ * this certificate.
+ * @return path limit
+ */
+ u32bit path_limit() const;
+
+ /**
+ * Check whenever a given X509 Extension is marked critical in this
+ * certificate.
+ */
+ bool is_critical(const std::string& ex_name) const;
+
+ /**
+ * Get the key constraints as defined in the KeyUsage extension of this
+ * certificate.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the key constraints as defined in the ExtendedKeyUsage
+ * extension of this certificate.
+ * @return key constraints
+ */
+ std::vector<std::string> ex_constraints() const;
+
+ /**
+ * Get the name constraints as defined in the NameConstraints
+ * extension of this certificate.
+ * @return name constraints
+ */
+ NameConstraints name_constraints() const;
+
+ /**
+ * Get the policies as defined in the CertificatePolicies extension
+ * of this certificate.
+ * @return certificate policies
+ */
+ std::vector<std::string> policies() const;
+
+ /**
+ * Get all extensions of this certificate.
+ * @return certificate extensions
+ */
+ Extensions v3_extensions() const;
+
+ /**
+ * Return the listed address of an OCSP responder, or empty if not set
+ */
+ std::string ocsp_responder() const;
+
+ /**
+ * Return the CRL distribution point, or empty if not set
+ */
+ std::string crl_distribution_point() const;
+
+ /**
+ * @return a string describing the certificate
+ */
+ std::string to_string() const;
+
+ /**
+ * @return a fingerprint of the certificate
+ * @param hash_name hash function used to calculate the fingerprint
+ */
+ std::string fingerprint(const std::string& hash_name = "SHA-1") const;
+
+ /**
+ * Check if a certain DNS name matches up with the information in
+ * the cert
+ * @param name DNS name to match
+ */
+ bool matches_dns_name(const std::string& name) const;
+
+ /**
+ * Check to certificates for equality.
+ * @return true both certificates are (binary) equal
+ */
+ bool operator==(const X509_Certificate& other) const;
+
+ /**
+ * Impose an arbitrary (but consistent) ordering
+ * @return true if this is less than other by some unspecified criteria
+ */
+ bool operator<(const X509_Certificate& other) const;
+
+ /**
+ * Create a certificate from a data source providing the DER or
+ * PEM encoded certificate.
+ * @param source the data source
+ */
+ explicit X509_Certificate(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a certificate from a file containing the DER or PEM
+ * encoded certificate.
+ * @param filename the name of the certificate file
+ */
+ explicit X509_Certificate(const std::string& filename);
+#endif
+
+ /**
+ * Create a certificate from a buffer
+ * @param in the buffer containing the DER-encoded certificate
+ */
+ explicit X509_Certificate(const std::vector<byte>& in);
+
+ X509_Certificate(const X509_Certificate& other) = default;
+
+ X509_Certificate& operator=(const X509_Certificate& other) = default;
+
+ private:
+ void force_decode() override;
+ friend class X509_CA;
+ friend class BER_Decoder;
+
+ X509_Certificate() {}
+
+ Data_Store m_subject, m_issuer;
+ bool m_self_signed;
+ Extensions m_v3_extensions;
+ };
+
+/**
+* Check two certificates for inequality
+* @param cert1 The first certificate
+* @param cert2 The second certificate
+* @return true if the arguments represent different certificates,
+* false if they are binary identical
+*/
+BOTAN_DLL 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_DLL 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_DLL AlternativeName create_alt_name(const Data_Store& info);
+
+}
+
+#endif
diff --git a/src/lib/x509/x509opt.cpp b/src/lib/x509/x509opt.cpp
new file mode 100644
index 000000000..2dd2098fe
--- /dev/null
+++ b/src/lib/x509/x509opt.cpp
@@ -0,0 +1,94 @@
+/*
+* X.509 Certificate Options
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/oids.h>
+#include <botan/parsing.h>
+#include <chrono>
+
+namespace Botan {
+
+/*
+* Set when the certificate should become valid
+*/
+void X509_Cert_Options::not_before(const std::string& time_string)
+ {
+ start = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set when the certificate should expire
+*/
+void X509_Cert_Options::not_after(const std::string& time_string)
+ {
+ end = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_constraints(Key_Constraints usage)
+ {
+ constraints = usage;
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const OID& oid)
+ {
+ ex_constraints.push_back(oid);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const std::string& oid_str)
+ {
+ ex_constraints.push_back(OIDS::lookup(oid_str));
+ }
+
+/*
+* Mark this certificate for CA usage
+*/
+void X509_Cert_Options::CA_key(size_t limit)
+ {
+ is_CA = true;
+ path_limit = limit;
+ }
+
+/*
+* Initialize the certificate options
+*/
+X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
+ u32bit expiration_time)
+ {
+ is_CA = false;
+ path_limit = 0;
+ constraints = NO_CONSTRAINTS;
+
+ auto now = std::chrono::system_clock::now();
+
+ start = X509_Time(now);
+ end = X509_Time(now + std::chrono::seconds(expiration_time));
+
+ if(initial_opts.empty())
+ return;
+
+ std::vector<std::string> parsed = split_on(initial_opts, '/');
+
+ if(parsed.size() > 4)
+ throw Invalid_Argument("X.509 cert options: Too many names: "
+ + initial_opts);
+
+ if(parsed.size() >= 1) common_name = parsed[0];
+ if(parsed.size() >= 2) country = parsed[1];
+ if(parsed.size() >= 3) organization = parsed[2];
+ if(parsed.size() == 4) org_unit = parsed[3];
+ }
+
+}
diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
new file mode 100644
index 000000000..f0b07e5fc
--- /dev/null
+++ b/src/lib/x509/x509path.cpp
@@ -0,0 +1,454 @@
+/*
+* X.509 Certificate Path Validation
+* (C) 2010,2011,2012,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+#include <botan/http_util.h>
+#include <botan/parsing.h>
+#include <botan/pubkey.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <chrono>
+#include <vector>
+#include <set>
+#include <future>
+
+namespace Botan {
+
+namespace {
+
+std::shared_ptr<const X509_Certificate>
+find_issuing_cert(const X509_Certificate& cert,
+ Certificate_Store& end_certs,
+ const std::vector<Certificate_Store*>& certstores)
+ {
+ const X509_DN issuer_dn = cert.issuer_dn();
+ const std::vector<byte> auth_key_id = cert.authority_key_id();
+
+ if(std::shared_ptr<const X509_Certificate> c = end_certs.find_cert(issuer_dn, auth_key_id))
+ {
+ if(*c != cert)
+ return c;
+ }
+
+ for(size_t i = 0; i != certstores.size(); ++i)
+ {
+ if(std::shared_ptr<const X509_Certificate> c = certstores[i]->find_cert(issuer_dn, auth_key_id))
+ return c;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_CRL> find_crls_for(const X509_Certificate& cert,
+ const std::vector<Certificate_Store*>& certstores)
+ {
+ for(size_t i = 0; i != certstores.size(); ++i)
+ {
+ if(std::shared_ptr<const X509_CRL> crl = certstores[i]->find_crl_for(cert))
+ return crl;
+ }
+
+#if 0
+ const std::string crl_url = cert.crl_distribution_point();
+ if(crl_url != "")
+ {
+ std::cout << "Downloading CRL " << crl_url << "\n";
+ auto http = HTTP::GET_sync(crl_url);
+
+ std::cout << http.status_message() << "\n";
+
+ http.throw_unless_ok();
+ // check the mime type
+
+ std::unique_ptr<X509_CRL> crl(new X509_CRL(http.body()));
+
+ return crl.release();
+ }
+#endif
+
+ return nullptr;
+ }
+
+std::vector<std::set<Certificate_Status_Code>>
+check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time)
+ {
+ const std::set<std::string>& trusted_hashes = restrictions.trusted_hashes();
+
+ const bool self_signed_ee_cert = (cert_path.size() == 1);
+
+ X509_Time validation_time(ref_time);
+
+ std::vector<std::future<OCSP::Response>> ocsp_responses;
+
+ std::vector<std::set<Certificate_Status_Code>> cert_status(cert_path.size());
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ const bool at_self_signed_root = (i == cert_path.size() - 1);
+
+ std::shared_ptr<const X509_Certificate> subject = cert_path[i];
+
+ std::shared_ptr<const X509_Certificate> issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+
+ if(i == 0 || restrictions.ocsp_all_intermediates())
+ {
+ // certstore[0] is treated as trusted for OCSP (FIXME)
+ if(certstores.size() > 1)
+ ocsp_responses.push_back(
+ std::async(std::launch::async,
+ OCSP::online_check, *issuer, *subject, certstores[0]));
+ }
+
+ // Check all certs for valid time range
+ if(validation_time < X509_Time(subject->start_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
+
+ if(validation_time > X509_Time(subject->end_time(), ASN1_Tag::UTC_OR_GENERALIZED_TIME))
+ status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
+
+ // Check issuer constraints
+
+ if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+
+ if(issuer->path_limit() < i)
+ status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
+
+ std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+
+ if(!issuer_key)
+ {
+ status.insert(Certificate_Status_Code::SIGNATURE_ERROR);
+ }
+ else
+ {
+ if(subject->check_signature(*issuer_key) == false)
+ status.insert(Certificate_Status_Code::SIGNATURE_ERROR);
+
+ if(issuer_key->estimated_strength() < restrictions.minimum_key_strength())
+ status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+ }
+
+ // Allow untrusted hashes on self-signed roots
+ if(!trusted_hashes.empty() && !at_self_signed_root)
+ {
+ if(!trusted_hashes.count(subject->hash_used_for_signature()))
+ status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+ }
+
+ // Check cert extensions
+ Extensions extensions = subject->v3_extensions();
+ for(auto& extension : extensions.extensions())
+ {
+ extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
+ }
+ }
+
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+ if(i < ocsp_responses.size())
+ {
+ try
+ {
+ OCSP::Response ocsp = ocsp_responses[i].get();
+
+ auto ocsp_status = ocsp.status_for(*ca, *subject);
+
+ status.insert(ocsp_status);
+
+ //std::cout << "OCSP status: " << Path_Validation_Result::status_string(ocsp_status) << "\n";
+
+ // Either way we have a definitive answer, no need to check CRLs
+ if(ocsp_status == Certificate_Status_Code::CERT_IS_REVOKED)
+ return cert_status;
+ else if(ocsp_status == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
+ continue;
+ }
+ catch(std::exception&)
+ {
+ //std::cout << "OCSP error: " << e.what() << "\n";
+ }
+ }
+
+ std::shared_ptr<const X509_CRL> crl_p = find_crls_for(*subject, certstores);
+
+ if(!crl_p)
+ {
+ if(restrictions.require_revocation_information())
+ status.insert(Certificate_Status_Code::NO_REVOCATION_DATA);
+ continue;
+ }
+
+ const X509_CRL& crl = *crl_p;
+
+ if(!ca->allowed_usage(CRL_SIGN))
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
+
+ if(validation_time < X509_Time(crl.this_update()))
+ status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
+
+ if(validation_time > X509_Time(crl.next_update()))
+ status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
+
+ if(crl.check_signature(ca->subject_public_key()) == false)
+ status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
+
+ if(crl.is_revoked(*subject))
+ status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
+ }
+
+ if(self_signed_ee_cert)
+ cert_status.back().insert(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+
+ return cert_status;
+ }
+
+}
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point validation_time)
+ {
+ if(end_certs.empty())
+ throw Invalid_Argument("x509_path_validate called with no subjects");
+
+ std::vector<std::shared_ptr<const X509_Certificate>> cert_path;
+ std::vector<std::shared_ptr<const X509_Certificate>> end_certs_sharedptr;
+ cert_path.push_back(std::make_shared<X509_Certificate>(end_certs[0]));
+
+ for(auto c: end_certs)
+ end_certs_sharedptr.push_back(std::make_shared<const X509_Certificate>(c));
+
+ /*
+ * This is an inelegant but functional way of preventing path loops
+ * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
+ * fingerprints in the path. If there is a duplicate, we error out.
+ */
+ std::set<std::string> certs_seen;
+
+ Certificate_Store_Overlay extra(end_certs_sharedptr);
+
+ // iterate until we reach a root or cannot find the issuer
+ while(!cert_path.back()->is_self_signed())
+ {
+ std::shared_ptr<const X509_Certificate> cert = find_issuing_cert(*cert_path.back(), extra, certstores);
+ if(!cert)
+ return Path_Validation_Result(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+
+ const std::string fprint = cert->fingerprint("SHA-256");
+ if(certs_seen.count(fprint) > 0)
+ return Path_Validation_Result(Certificate_Status_Code::CERT_CHAIN_LOOP);
+ certs_seen.insert(fprint);
+ cert_path.push_back(cert);
+ }
+
+ std::vector<std::set<Certificate_Status_Code>> res =
+ check_chain(cert_path, restrictions, certstores, validation_time);
+
+ if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
+ res[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
+
+ if(!cert_path[0]->allowed_usage(usage))
+ res[0].insert(Certificate_Status_Code::INVALID_USAGE);
+
+ return Path_Validation_Result(res, std::move(cert_path));
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+ return x509_path_validate(certs, restrictions, certstores, hostname, usage, when);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when)
+ {
+ std::vector<Certificate_Store*> certstores;
+ certstores.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(end_certs, restrictions, certstores, hostname, usage, when);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+
+ std::vector<Certificate_Store*> certstores;
+ certstores.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(certs, restrictions, certstores, hostname, usage, when);
+ }
+
+Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
+ size_t key_strength,
+ bool ocsp_all) :
+ m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_all),
+ m_minimum_key_strength(key_strength)
+ {
+ if(key_strength <= 80)
+ m_trusted_hashes.insert("SHA-160");
+
+ m_trusted_hashes.insert("SHA-224");
+ m_trusted_hashes.insert("SHA-256");
+ m_trusted_hashes.insert("SHA-384");
+ m_trusted_hashes.insert("SHA-512");
+ }
+
+Path_Validation_Result::Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
+ m_overall(Certificate_Status_Code::VERIFIED),
+ m_all_status(status),
+ m_cert_path(cert_chain)
+ {
+ // take the "worst" error as overall
+ for(const auto& s : m_all_status)
+ {
+ if(!s.empty())
+ {
+ auto worst = *s.rbegin();
+ // Leave OCSP confirmations on cert-level status only
+ if(worst != Certificate_Status_Code::OCSP_RESPONSE_GOOD)
+ m_overall = worst;
+ }
+ }
+ }
+
+const X509_Certificate& Path_Validation_Result::trust_root() const
+ {
+ if(m_cert_path.empty())
+ throw Exception("Path_Validation_Result::trust_root no path set");
+ if(result() != Certificate_Status_Code::VERIFIED)
+ throw Exception("Path_Validation_Result::trust_root meaningless with invalid status");
+
+ return *m_cert_path[m_cert_path.size()-1];
+ }
+
+std::set<std::string> Path_Validation_Result::trusted_hashes() const
+ {
+ std::set<std::string> hashes;
+ for(size_t i = 0; i != m_cert_path.size(); ++i)
+ hashes.insert(m_cert_path[i]->hash_used_for_signature());
+ return hashes;
+ }
+
+bool Path_Validation_Result::successful_validation() const
+ {
+ if(result() == Certificate_Status_Code::VERIFIED ||
+ result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD)
+ return true;
+ return false;
+ }
+
+std::string Path_Validation_Result::result_string() const
+ {
+ return status_string(result());
+ }
+
+const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
+ {
+ switch(code)
+ {
+ case Certificate_Status_Code::VERIFIED:
+ return "Verified";
+ case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
+ return "OCSP response good";
+ case Certificate_Status_Code::NO_REVOCATION_DATA:
+ return "No revocation data";
+ case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
+ return "Signature method too weak";
+ case Certificate_Status_Code::UNTRUSTED_HASH:
+ return "Untrusted hash";
+
+ case Certificate_Status_Code::CERT_NOT_YET_VALID:
+ return "Certificate is not yet valid";
+ case Certificate_Status_Code::CERT_HAS_EXPIRED:
+ return "Certificate has expired";
+ case Certificate_Status_Code::OCSP_NOT_YET_VALID:
+ return "OCSP is not yet valid";
+ case Certificate_Status_Code::OCSP_HAS_EXPIRED:
+ return "OCSP has expired";
+ case Certificate_Status_Code::CRL_NOT_YET_VALID:
+ return "CRL is not yet valid";
+ case Certificate_Status_Code::CRL_HAS_EXPIRED:
+ return "CRL has expired";
+
+ case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
+ return "Certificate issuer not found";
+ case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
+ return "Cannot establish trust";
+ case Certificate_Status_Code::CERT_CHAIN_LOOP:
+ return "Loop in certificate chain";
+
+ case Certificate_Status_Code::POLICY_ERROR:
+ return "Policy error";
+ case Certificate_Status_Code::INVALID_USAGE:
+ return "Invalid usage";
+ case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
+ return "Certificate chain too long";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
+ return "CA certificate not allowed to issue certs";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
+ return "CA certificate not allowed to issue CRLs";
+ case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
+ return "OCSP cert not listed";
+ case Certificate_Status_Code::OCSP_BAD_STATUS:
+ return "OCSP bad status";
+ case Certificate_Status_Code::CERT_NAME_NOMATCH:
+ return "Certificate does not match provided name";
+ case Certificate_Status_Code::NAME_CONSTRAINT_ERROR:
+ return "Certificate does not pass name constraint";
+ case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION:
+ return "Unknown critical extension encountered";
+
+ case Certificate_Status_Code::CERT_IS_REVOKED:
+ return "Certificate is revoked";
+ case Certificate_Status_Code::CRL_BAD_SIGNATURE:
+ return "CRL bad signature";
+ case Certificate_Status_Code::SIGNATURE_ERROR:
+ return "Signature error";
+ // intentionally no default so we are warned
+ }
+
+ return "Unknown error";
+ }
+
+}
diff --git a/src/lib/x509/x509path.h b/src/lib/x509/x509path.h
new file mode 100644
index 000000000..f65652e59
--- /dev/null
+++ b/src/lib/x509/x509path.h
@@ -0,0 +1,239 @@
+/*
+* X.509 Cert Path Validation
+* (C) 2010-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERT_PATH_VALIDATION_H__
+#define BOTAN_X509_CERT_PATH_VALIDATION_H__
+
+#include <botan/cert_status.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <set>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* Specifies restrictions on the PKIX path validation
+*/
+class BOTAN_DLL Path_Validation_Restrictions
+ {
+ public:
+ /**
+ * @param require_rev if true, revocation information is required
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures
+ * weaker than this are rejected. If more than 80, SHA-1
+ * signatures are also rejected.
+ * @param ocsp_all_intermediates
+ */
+ Path_Validation_Restrictions(bool require_rev = false,
+ size_t minimum_key_strength = 80,
+ bool ocsp_all_intermediates = false);
+
+ /**
+ * @param require_rev if true, revocation information is required
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures
+ * weaker than this are rejected.
+ * @param ocsp_all_intermediates
+ * @param trusted_hashes a set of trusted hashes. Any signatures
+ * created using a hash other than one of these will be
+ * rejected.
+ */
+ Path_Validation_Restrictions(bool require_rev,
+ size_t minimum_key_strength,
+ bool ocsp_all_intermediates,
+ const std::set<std::string>& trusted_hashes) :
+ m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_all_intermediates),
+ m_trusted_hashes(trusted_hashes),
+ m_minimum_key_strength(minimum_key_strength) {}
+
+ /**
+ * @return whether revocation information is required
+ */
+ bool require_revocation_information() const
+ { return m_require_revocation_information; }
+
+ /**
+ * FIXME add doc
+ */
+ bool ocsp_all_intermediates() const
+ { return m_ocsp_all_intermediates; }
+
+ /**
+ * @return trusted signature hash functions
+ */
+ const std::set<std::string>& trusted_hashes() const
+ { return m_trusted_hashes; }
+
+ /**
+ * @return minimum required key strength
+ */
+ size_t minimum_key_strength() const
+ { return m_minimum_key_strength; }
+
+ private:
+ bool m_require_revocation_information;
+ bool m_ocsp_all_intermediates;
+ std::set<std::string> m_trusted_hashes;
+ size_t m_minimum_key_strength;
+ };
+
+/**
+* Represents the result of a PKIX path validation
+*/
+class BOTAN_DLL Path_Validation_Result
+ {
+ public:
+ typedef Certificate_Status_Code Code;
+
+ /**
+ * @return the set of hash functions you are implicitly
+ * trusting by trusting this result.
+ */
+ std::set<std::string> trusted_hashes() const;
+
+ /**
+ * @return the trust root of the validation if successful
+ * throws an exception if the validation failed
+ */
+ const X509_Certificate& trust_root() const;
+
+ /**
+ * @return the full path from subject to trust root
+ */
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; }
+
+ /**
+ * @return true iff the validation was successful
+ */
+ bool successful_validation() const;
+
+ /**
+ * @return overall validation result code
+ */
+ Certificate_Status_Code result() const { return m_overall; }
+
+ /**
+ * @return a set of status codes for each certificate in the chain
+ */
+ const std::vector<std::set<Certificate_Status_Code>>& all_statuses() const
+ { return m_all_status; }
+
+ /**
+ * @return string representation of the validation result
+ */
+ std::string result_string() const;
+
+ /**
+ * @param code validation status code
+ * @return corresponding validation status message
+ */
+ static const char* status_string(Certificate_Status_Code code);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status list of validation status codes
+ * @param cert_chain the certificate chain that was validated
+ */
+ Path_Validation_Result(std::vector<std::set<Certificate_Status_Code>> status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status validation status code
+ */
+ explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {}
+
+ private:
+ friend Path_Validation_Result BOTAN_DLL x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores);
+
+ Certificate_Status_Code m_overall;
+ std::vector<std::set<Certificate_Status_Code>> m_all_status;
+ std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path;
+ };
+
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate
+* @param restrictions path validation restrictions
+* @param certstores list of certificate stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_DLL x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now());
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param certstores list of stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_DLL x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& certstores,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now());
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_DLL x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now());
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_DLL x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now());
+
+}
+
+#endif
diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp
new file mode 100644
index 000000000..a59632858
--- /dev/null
+++ b/src/lib/x509/x509self.cpp
@@ -0,0 +1,176 @@
+/*
+* PKCS #10/Self Signed Cert Creation
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/x509_ext.h>
+#include <botan/x509_ca.h>
+#include <botan/der_enc.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Load information from the X509_Cert_Options
+*/
+void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
+ AlternativeName& subject_alt)
+ {
+ subject_dn.add_attribute("X520.CommonName", opts.common_name);
+ subject_dn.add_attribute("X520.Country", opts.country);
+ subject_dn.add_attribute("X520.State", opts.state);
+ subject_dn.add_attribute("X520.Locality", opts.locality);
+ subject_dn.add_attribute("X520.Organization", opts.organization);
+ subject_dn.add_attribute("X520.OrganizationalUnit", opts.org_unit);
+ subject_dn.add_attribute("X520.SerialNumber", opts.serial_number);
+ subject_alt = AlternativeName(opts.email, opts.uri, opts.dns, opts.ip);
+ subject_alt.add_othername(OIDS::lookup("PKIX.XMPPAddr"),
+ opts.xmpp, UTF8_STRING);
+ }
+
+}
+
+namespace X509 {
+
+/*
+* Create a new self-signed X.509 certificate
+*/
+X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ std::vector<byte> pub_key = X509::BER_encode(key);
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, rng, hash_fn, sig_algo));
+ load_info(opts, subject_dn, subject_alt);
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ Extensions extensions;
+
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ extensions.add(new Cert_Extension::Subject_Key_ID(pub_key));
+
+ extensions.add(
+ new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ extensions.add(
+ new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+
+ return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key,
+ opts.start, opts.end,
+ subject_dn, subject_dn,
+ extensions);
+ }
+
+/*
+* Create a PKCS #10 certificate request
+*/
+PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ std::vector<byte> pub_key = X509::BER_encode(key);
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, rng, hash_fn, sig_algo));
+ load_info(opts, subject_dn, subject_alt);
+
+ const size_t PKCS10_VERSION = 0;
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ Extensions extensions;
+
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add(
+ new Cert_Extension::Key_Usage(constraints));
+ }
+ extensions.add(
+ new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+ extensions.add(
+ new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ DER_Encoder tbs_req;
+
+ tbs_req.start_cons(SEQUENCE)
+ .encode(PKCS10_VERSION)
+ .encode(subject_dn)
+ .raw_bytes(pub_key)
+ .start_explicit(0);
+
+ if(!opts.challenge.empty())
+ {
+ ASN1_String challenge(opts.challenge, DIRECTORY_STRING);
+
+ tbs_req.encode(
+ Attribute("PKCS9.ChallengePassword",
+ DER_Encoder().encode(challenge).get_contents_unlocked()
+ )
+ );
+ }
+
+ tbs_req.encode(
+ Attribute("PKCS9.ExtensionRequest",
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .get_contents_unlocked()
+ )
+ )
+ .end_explicit()
+ .end_cons();
+
+ const std::vector<byte> req =
+ X509_Object::make_signed(signer.get(), rng, sig_algo,
+ tbs_req.get_contents());
+
+ return PKCS10_Request(req);
+ }
+
+}
+
+}
diff --git a/src/lib/x509/x509self.h b/src/lib/x509/x509self.h
new file mode 100644
index 000000000..401b2eb2f
--- /dev/null
+++ b/src/lib/x509/x509self.h
@@ -0,0 +1,197 @@
+/*
+* X.509 Self-Signed Certificate
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_SELF_H__
+#define BOTAN_X509_SELF_H__
+
+#include <botan/x509cert.h>
+#include <botan/pkcs10.h>
+#include <botan/asn1_time.h>
+
+namespace Botan {
+
+/**
+* Options for X.509 certificates.
+*/
+class BOTAN_DLL X509_Cert_Options
+ {
+ public:
+ /**
+ * the subject common name
+ */
+ std::string common_name;
+
+ /**
+ * the subject counry
+ */
+ std::string country;
+
+ /**
+ * the subject organization
+ */
+ std::string organization;
+
+ /**
+ * the subject organizational unit
+ */
+ std::string org_unit;
+
+ /**
+ * the subject locality
+ */
+ std::string locality;
+
+ /**
+ * the subject state
+ */
+ std::string state;
+
+ /**
+ * the subject serial number
+ */
+ std::string serial_number;
+
+ /**
+ * the subject email adress
+ */
+ std::string email;
+
+ /**
+ * the subject URI
+ */
+ std::string uri;
+
+ /**
+ * the subject IPv4 address
+ */
+ std::string ip;
+
+ /**
+ * the subject DNS
+ */
+ std::string dns;
+
+ /**
+ * the subject XMPP
+ */
+ std::string xmpp;
+
+ /**
+ * the subject challenge password
+ */
+ std::string challenge;
+
+ /**
+ * the subject notBefore
+ */
+ X509_Time start;
+ /**
+ * the subject notAfter
+ */
+ X509_Time end;
+
+ /**
+ * Indicates whether the certificate request
+ */
+ bool is_CA;
+
+ /**
+ * Indicates the BasicConstraints path limit
+ */
+ size_t path_limit;
+
+ /**
+ * The key constraints for the subject public key
+ */
+ Key_Constraints constraints;
+
+ /**
+ * The key extended constraints for the subject public key
+ */
+ std::vector<OID> ex_constraints;
+
+ /**
+ * Mark the certificate as a CA certificate and set the path limit.
+ * @param limit the path limit to be set in the BasicConstraints extension.
+ */
+ void CA_key(size_t limit = 1);
+
+ /**
+ * Set the notBefore of the certificate.
+ * @param time the notBefore value of the certificate
+ */
+ void not_before(const std::string& time);
+
+ /**
+ * Set the notAfter of the certificate.
+ * @param time the notAfter value of the certificate
+ */
+ void not_after(const std::string& time);
+
+ /**
+ * Add the key constraints of the KeyUsage extension.
+ * @param constr the constraints to set
+ */
+ void add_constraints(Key_Constraints constr);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param oid the oid to add
+ */
+ void add_ex_constraint(const OID& oid);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param name the name to look up the oid to add
+ */
+ void add_ex_constraint(const std::string& name);
+
+ /**
+ * Construct a new options object
+ * @param opts define the common name of this object. An example for this
+ * parameter would be "common_name/country/organization/organizational_unit".
+ * @param expire_time the expiration time (from the current clock in seconds)
+ */
+ X509_Cert_Options(const std::string& opts = "",
+ u32bit expire_time = 365 * 24 * 60 * 60);
+ };
+
+namespace X509 {
+
+/**
+* Create a self-signed X.509 certificate.
+* @param opts the options defining the certificate to create
+* @param key the private key used for signing, i.e. the key
+* associated with this self-signed certificate
+* @param hash_fn the hash function to use
+* @param rng the rng to use
+* @return newly created self-signed certificate
+*/
+BOTAN_DLL X509_Certificate
+create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+/**
+* Create a PKCS#10 certificate request.
+* @param opts the options defining the request to create
+* @param key the key used to sign this request
+* @param rng the rng to use
+* @param hash_fn the hash function to use
+* @return newly created PKCS#10 request
+*/
+BOTAN_DLL PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+}
+
+}
+
+#endif