aboutsummaryrefslogtreecommitdiffstats
path: root/src/cert/x509
diff options
context:
space:
mode:
authorlloyd <[email protected]>2008-09-29 16:44:06 +0000
committerlloyd <[email protected]>2008-09-29 16:44:06 +0000
commitb67b7501e740c69e53e2ffcfa72538653eedf3b6 (patch)
tree96e0c0feab632f7ecdcc995311130a69b5265336 /src/cert/x509
parentfaa26383122ad694aa9ed82f7feb2e3ed3a7625e (diff)
Move x509 into cert/ subdir (in prep for InSiTo adding cert/cvc)
Diffstat (limited to 'src/cert/x509')
-rw-r--r--src/cert/x509/certstor.cpp46
-rw-r--r--src/cert/x509/certstor.h37
-rw-r--r--src/cert/x509/crl_ent.cpp105
-rw-r--r--src/cert/x509/crl_ent.h45
-rw-r--r--src/cert/x509/info.txt36
-rw-r--r--src/cert/x509/pkcs10.cpp198
-rw-r--r--src/cert/x509/pkcs10.h46
-rw-r--r--src/cert/x509/x509_ca.cpp278
-rw-r--r--src/cert/x509/x509_ca.h68
-rw-r--r--src/cert/x509/x509_crl.cpp145
-rw-r--r--src/cert/x509/x509_crl.h48
-rw-r--r--src/cert/x509/x509_ext.cpp579
-rw-r--r--src/cert/x509/x509_ext.h315
-rw-r--r--src/cert/x509/x509_obj.cpp233
-rw-r--r--src/cert/x509/x509_obj.h56
-rw-r--r--src/cert/x509/x509cert.cpp364
-rw-r--r--src/cert/x509/x509cert.h72
-rw-r--r--src/cert/x509/x509find.cpp109
-rw-r--r--src/cert/x509/x509find.h58
-rw-r--r--src/cert/x509/x509opt.cpp106
-rw-r--r--src/cert/x509/x509self.cpp174
-rw-r--r--src/cert/x509/x509self.h75
-rw-r--r--src/cert/x509/x509stor.cpp693
-rw-r--r--src/cert/x509/x509stor.h137
24 files changed, 4023 insertions, 0 deletions
diff --git a/src/cert/x509/certstor.cpp b/src/cert/x509/certstor.cpp
new file mode 100644
index 000000000..2b51590ad
--- /dev/null
+++ b/src/cert/x509/certstor.cpp
@@ -0,0 +1,46 @@
+/*************************************************
+* Certificate Store Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+/*************************************************
+* Search by name *
+*************************************************/
+std::vector<X509_Certificate>
+Certificate_Store::by_name(const std::string&) const
+ {
+ return std::vector<X509_Certificate>();
+ }
+
+/*************************************************
+* Search by email *
+*************************************************/
+std::vector<X509_Certificate>
+Certificate_Store::by_email(const std::string&) const
+ {
+ return std::vector<X509_Certificate>();
+ }
+
+/*************************************************
+* Search by X.500 distinguished name *
+*************************************************/
+std::vector<X509_Certificate>
+Certificate_Store::by_dn(const X509_DN&) const
+ {
+ return std::vector<X509_Certificate>();
+ }
+
+/*************************************************
+* Find any CRLs that might be useful *
+*************************************************/
+std::vector<X509_CRL>
+Certificate_Store::get_crls_for(const X509_Certificate&) const
+ {
+ return std::vector<X509_CRL>();
+ }
+
+}
diff --git a/src/cert/x509/certstor.h b/src/cert/x509/certstor.h
new file mode 100644
index 000000000..ca0480496
--- /dev/null
+++ b/src/cert/x509/certstor.h
@@ -0,0 +1,37 @@
+/*************************************************
+* Certificate Store Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#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 std::vector<X509_Certificate>
+ by_SKID(const MemoryRegion<byte>&) const = 0;
+
+ virtual std::vector<X509_Certificate> by_name(const std::string&) const;
+ virtual std::vector<X509_Certificate> by_email(const std::string&) const;
+ virtual std::vector<X509_Certificate> by_dn(const X509_DN&) const;
+
+ virtual std::vector<X509_CRL>
+ get_crls_for(const X509_Certificate&) const;
+
+ virtual Certificate_Store* clone() const = 0;
+
+ virtual ~Certificate_Store() {}
+ };
+
+}
+
+#endif
diff --git a/src/cert/x509/crl_ent.cpp b/src/cert/x509/crl_ent.cpp
new file mode 100644
index 000000000..dbf550f13
--- /dev/null
+++ b/src/cert/x509/crl_ent.cpp
@@ -0,0 +1,105 @@
+/*************************************************
+* CRL Entry Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/crl_ent.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/util.h>
+
+namespace Botan {
+
+/*************************************************
+* Create a CRL_Entry *
+*************************************************/
+CRL_Entry::CRL_Entry(bool t_on_unknown_crit) :
+ throw_on_unknown_critical(t_on_unknown_crit)
+ {
+ reason = UNSPECIFIED;
+ }
+
+/*************************************************
+* Create a CRL_Entry *
+*************************************************/
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) :
+ throw_on_unknown_critical(false)
+ {
+ serial = cert.serial_number();
+ time = X509_Time(system_time());
+ 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);
+ }
+
+/*************************************************
+* Compare two CRL_Entrys *
+*************************************************/
+bool operator<(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ return (a1.expire_time().cmp(a2.expire_time()) < 0);
+ }
+
+/*************************************************
+* DER encode a CRL_Entry *
+*************************************************/
+void CRL_Entry::encode_into(DER_Encoder& der) const
+ {
+ Extensions extensions;
+
+ extensions.add(new Cert_Extension::CRL_ReasonCode(reason));
+
+ der.start_cons(SEQUENCE)
+ .encode(BigInt::decode(serial, serial.size()))
+ .encode(time)
+ .encode(extensions)
+ .end_cons();
+ }
+
+/*************************************************
+* Decode a BER encoded CRL_Entry *
+*************************************************/
+void CRL_Entry::decode_from(BER_Decoder& source)
+ {
+ BigInt serial_number_bn;
+
+ source.start_cons(SEQUENCE)
+ .decode(serial_number_bn)
+ .decode(time);
+
+ if(source.more_items())
+ {
+ Extensions extensions(throw_on_unknown_critical);
+ source.decode(extensions);
+ Data_Store info;
+ extensions.contents_to(info, info);
+ reason = CRL_Code(info.get1_u32bit("X509v3.CRLReasonCode"));
+ }
+
+ serial = BigInt::encode(serial_number_bn);
+ }
+
+}
diff --git a/src/cert/x509/crl_ent.h b/src/cert/x509/crl_ent.h
new file mode 100644
index 000000000..8aa567a2a
--- /dev/null
+++ b/src/cert/x509/crl_ent.h
@@ -0,0 +1,45 @@
+/*************************************************
+* CRL Entry Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_CRL_ENTRY_H__
+#define BOTAN_CRL_ENTRY_H__
+
+#include <botan/x509cert.h>
+
+namespace Botan {
+
+/*************************************************
+* CRL Entry *
+*************************************************/
+class BOTAN_DLL CRL_Entry : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ MemoryVector<byte> serial_number() const { return serial; }
+ X509_Time expire_time() const { return time; }
+ CRL_Code reason_code() const { return reason; }
+
+ CRL_Entry(bool throw_on_unknown_critical_extension = false);
+ CRL_Entry(const X509_Certificate&, CRL_Code = UNSPECIFIED);
+
+ private:
+ bool throw_on_unknown_critical;
+ MemoryVector<byte> serial;
+ X509_Time time;
+ CRL_Code reason;
+ };
+
+/*************************************************
+* Comparison Operations *
+*************************************************/
+BOTAN_DLL bool operator==(const CRL_Entry&, const CRL_Entry&);
+BOTAN_DLL bool operator!=(const CRL_Entry&, const CRL_Entry&);
+BOTAN_DLL bool operator<(const CRL_Entry&, const CRL_Entry&);
+
+}
+
+#endif
diff --git a/src/cert/x509/info.txt b/src/cert/x509/info.txt
new file mode 100644
index 000000000..84f6a9a7f
--- /dev/null
+++ b/src/cert/x509/info.txt
@@ -0,0 +1,36 @@
+realname "X.509"
+
+define X509
+
+load_on auto
+
+<add>
+certstor.h
+certstor.cpp
+crl_ent.cpp
+crl_ent.h
+pkcs10.h
+pkcs10.cpp
+x509_ca.cpp
+x509_ca.h
+x509_crl.cpp
+x509_crl.h
+x509_ext.cpp
+x509_ext.h
+x509_obj.cpp
+x509_obj.h
+x509cert.cpp
+x509cert.h
+x509find.cpp
+x509find.h
+x509opt.cpp
+x509self.cpp
+x509self.h
+x509stor.cpp
+x509stor.h
+</add>
+
+<requires>
+asn1
+bigint
+</requires>
diff --git a/src/cert/x509/pkcs10.cpp b/src/cert/x509/pkcs10.cpp
new file mode 100644
index 000000000..bd21cca7d
--- /dev/null
+++ b/src/cert/x509/pkcs10.cpp
@@ -0,0 +1,198 @@
+/*************************************************
+* PKCS #10 Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/pkcs10.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/x509stor.h>
+#include <botan/x509_ext.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();
+ }
+
+/*************************************************
+* PKCS10_Request Constructor *
+*************************************************/
+PKCS10_Request::PKCS10_Request(const std::string& in) :
+ X509_Object(in, "CERTIFICATE REQUEST/NEW CERTIFICATE REQUEST")
+ {
+ do_decode();
+ }
+
+/*************************************************
+* Deocde the CertificateRequestInfo *
+*************************************************/
+void PKCS10_Request::force_decode()
+ {
+ BER_Decoder cert_req_info(tbs_bits);
+
+ u32bit version;
+ cert_req_info.decode(version);
+ if(version != 0)
+ throw Decoding_Error("Unknown version code in PKCS #10 request: " +
+ to_string(version));
+
+ X509_DN dn_subject;
+ cert_req_info.decode(dn_subject);
+
+ 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);
+
+ info.add("X509.Certificate.public_key",
+ PEM_Code::encode(
+ ASN1::put_in_sequence(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();
+
+ X509_Code sig_check = X509_Store::check_sig(*this, subject_public_key());
+ if(sig_check != VERIFIED)
+ 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);
+ info.add("RFC822", email.value());
+ }
+ else if(attr.oid == OIDS::lookup("PKCS9.ChallengePassword"))
+ {
+ ASN1_String challenge_password;
+ value.decode(challenge_password);
+ 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(info, issuer_info);
+ }
+ }
+
+/*************************************************
+* Return the challenge password (if any) *
+*************************************************/
+std::string PKCS10_Request::challenge_password() const
+ {
+ return info.get1("PKCS9.ChallengePassword");
+ }
+
+/*************************************************
+* Return the name of the requestor *
+*************************************************/
+X509_DN PKCS10_Request::subject_dn() const
+ {
+ return create_dn(info);
+ }
+
+/*************************************************
+* Return the public key of the requestor *
+*************************************************/
+MemoryVector<byte> PKCS10_Request::raw_public_key() const
+ {
+ DataSource_Memory source(info.get1("X509.Certificate.public_key"));
+ return 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(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(info);
+ }
+
+/*************************************************
+* Return the key constraints (if any) *
+*************************************************/
+Key_Constraints PKCS10_Request::constraints() const
+ {
+ return Key_Constraints(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 = info.get("X509v3.ExtendedKeyUsage");
+
+ std::vector<OID> result;
+ for(u32bit j = 0; j != oids.size(); ++j)
+ result.push_back(OID(oids[j]));
+ return result;
+ }
+
+/*************************************************
+* Return is a CA certificate is requested *
+*************************************************/
+bool PKCS10_Request::is_CA() const
+ {
+ return info.get1_u32bit("X509v3.BasicConstraints.is_ca");
+ }
+
+/*************************************************
+* Return the desired path limit (if any) *
+*************************************************/
+u32bit PKCS10_Request::path_limit() const
+ {
+ return info.get1_u32bit("X509v3.BasicConstraints.path_constraint", 0);
+ }
+
+}
diff --git a/src/cert/x509/pkcs10.h b/src/cert/x509/pkcs10.h
new file mode 100644
index 000000000..f688688ee
--- /dev/null
+++ b/src/cert/x509/pkcs10.h
@@ -0,0 +1,46 @@
+/*************************************************
+* PKCS #10 Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_PKCS10_H__
+#define BOTAN_PKCS10_H__
+
+#include <botan/x509_obj.h>
+#include <botan/pkcs8.h>
+#include <botan/datastor.h>
+#include <vector>
+
+namespace Botan {
+
+/*************************************************
+* PKCS #10 Certificate Request *
+*************************************************/
+class BOTAN_DLL PKCS10_Request : public X509_Object
+ {
+ public:
+ Public_Key* subject_public_key() const;
+
+ MemoryVector<byte> raw_public_key() const;
+ X509_DN subject_dn() const;
+ AlternativeName subject_alt_name() const;
+ Key_Constraints constraints() const;
+ std::vector<OID> ex_constraints() const;
+
+ bool is_CA() const;
+ u32bit path_limit() const;
+
+ std::string challenge_password() const;
+
+ PKCS10_Request(DataSource&);
+ PKCS10_Request(const std::string&);
+ private:
+ void force_decode();
+ void handle_attribute(const Attribute&);
+
+ Data_Store info;
+ };
+
+}
+
+#endif
diff --git a/src/cert/x509/x509_ca.cpp b/src/cert/x509/x509_ca.cpp
new file mode 100644
index 000000000..16cfc662c
--- /dev/null
+++ b/src/cert/x509/x509_ca.cpp
@@ -0,0 +1,278 @@
+/*************************************************
+* X.509 Certificate Authority Source File *
+* (C) 1999-2008 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509_ca.h>
+#include <botan/x509stor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/lookup.h>
+#include <botan/look_pk.h>
+#include <botan/numthry.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/util.h>
+#include <algorithm>
+#include <typeinfo>
+#include <iterator>
+#include <memory>
+#include <set>
+
+namespace Botan {
+
+/*************************************************
+* Load the certificate and private key *
+*************************************************/
+X509_CA::X509_CA(const X509_Certificate& c,
+ const Private_Key& key) : cert(c)
+ {
+ const Private_Key* key_pointer = &key;
+ if(!dynamic_cast<const PK_Signing_Key*>(key_pointer))
+ throw Invalid_Argument("X509_CA: " + key.algo_name() + " cannot sign");
+
+ if(!cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ signer = choose_sig_format(key, ca_sig_algo);
+ }
+
+/*************************************************
+* 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::auto_ptr<Public_Key> key(req.subject_public_key());
+ constraints = X509::find_constraints(*key, req.constraints());
+ }
+
+ Extensions extensions;
+
+ extensions.add(new Cert_Extension::Authority_Key_ID(cert.subject_key_id()));
+ extensions.add(new Cert_Extension::Subject_Key_ID(req.raw_public_key()));
+
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()));
+
+ extensions.add(new Cert_Extension::Key_Usage(constraints));
+ extensions.add(
+ new Cert_Extension::Extended_Key_Usage(req.ex_constraints()));
+
+ extensions.add(
+ new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name()));
+
+ return make_cert(signer, rng, ca_sig_algo, req.raw_public_key(),
+ not_before, not_after,
+ 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 MemoryRegion<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 u32bit X509_CERT_VERSION = 3;
+ const u32bit SERIAL_BITS = 128;
+
+ BigInt serial_no(rng, SERIAL_BITS);
+
+ DataSource_Memory source(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()
+ ));
+
+ return X509_Certificate(source);
+ }
+
+/*************************************************
+* 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> already_revoked = crl.get_revoked();
+ std::vector<CRL_Entry> all_revoked;
+
+ X509_Store store;
+ store.add_cert(cert, true);
+ if(store.add_crl(crl) != VERIFIED)
+ throw Invalid_Argument("X509_CA::update_crl: Invalid CRL provided");
+
+ std::set<SecureVector<byte> > removed_from_crl;
+ for(u32bit j = 0; j != new_revoked.size(); ++j)
+ {
+ if(new_revoked[j].reason_code() == DELETE_CRL_ENTRY)
+ removed_from_crl.insert(new_revoked[j].serial_number());
+ else
+ all_revoked.push_back(new_revoked[j]);
+ }
+
+ for(u32bit j = 0; j != already_revoked.size(); ++j)
+ {
+ std::set<SecureVector<byte> >::const_iterator i;
+ i = removed_from_crl.find(already_revoked[j].serial_number());
+
+ if(i == removed_from_crl.end())
+ all_revoked.push_back(already_revoked[j]);
+ }
+ std::sort(all_revoked.begin(), all_revoked.end());
+
+ std::vector<CRL_Entry> cert_list;
+ std::unique_copy(all_revoked.begin(), all_revoked.end(),
+ std::back_inserter(cert_list));
+
+ return make_crl(cert_list, 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 u32bit 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!!
+ const u64bit current_time = system_time();
+
+ Extensions extensions;
+ extensions.add(
+ new Cert_Extension::Authority_Key_ID(cert.subject_key_id()));
+ extensions.add(new Cert_Extension::CRL_Number(crl_number));
+
+ DataSource_Memory source(X509_Object::make_signed(signer, rng, ca_sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .encode(X509_CRL_VERSION-1)
+ .encode(ca_sig_algo)
+ .encode(cert.issuer_dn())
+ .encode(X509_Time(current_time))
+ .encode(X509_Time(current_time + next_update))
+ .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()
+ ));
+
+ return X509_CRL(source);
+ }
+
+/*************************************************
+* Return the CA's certificate *
+*************************************************/
+X509_Certificate X509_CA::ca_certificate() const
+ {
+ return cert;
+ }
+
+/*************************************************
+* X509_CA Destructor *
+*************************************************/
+X509_CA::~X509_CA()
+ {
+ delete signer;
+ }
+
+/*************************************************
+* Choose a signing format for the key *
+*************************************************/
+PK_Signer* choose_sig_format(const Private_Key& key,
+ AlgorithmIdentifier& sig_algo)
+ {
+ std::string padding;
+ Signature_Format format;
+
+ const std::string algo_name = key.algo_name();
+
+ if(algo_name == "RSA")
+ {
+ padding = "EMSA3(SHA-160)";
+ format = IEEE_1363;
+ }
+ else if(algo_name == "DSA")
+ {
+ padding = "EMSA1(SHA-160)";
+ format = DER_SEQUENCE;
+ }
+ else
+ throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
+
+ sig_algo.oid = OIDS::lookup(algo_name + "/" + padding);
+
+ std::auto_ptr<X509_Encoder> encoding(key.x509_encoder());
+ if(!encoding.get())
+ throw Encoding_Error("Key " + algo_name + " does not support "
+ "X.509 encoding");
+
+ sig_algo.parameters = encoding->alg_id().parameters;
+
+ const PK_Signing_Key& sig_key = dynamic_cast<const PK_Signing_Key&>(key);
+
+ return get_pk_signer(sig_key, padding, format);
+ }
+
+}
diff --git a/src/cert/x509/x509_ca.h b/src/cert/x509/x509_ca.h
new file mode 100644
index 000000000..969e62558
--- /dev/null
+++ b/src/cert/x509/x509_ca.h
@@ -0,0 +1,68 @@
+/*************************************************
+* X.509 Certificate Authority Header File *
+* (C) 1999-2008 Jack Lloyd *
+*************************************************/
+
+#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/pkcs8.h>
+#include <botan/pkcs10.h>
+#include <botan/pubkey.h>
+
+namespace Botan {
+
+/*************************************************
+* X.509 Certificate Authority *
+*************************************************/
+class BOTAN_DLL X509_CA
+ {
+ public:
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after);
+
+ X509_Certificate ca_certificate() const;
+
+ X509_CRL new_crl(RandomNumberGenerator& rng, u32bit = 0) const;
+ X509_CRL update_crl(const X509_CRL&,
+ const std::vector<CRL_Entry>&,
+ RandomNumberGenerator& rng,
+ u32bit = 0) const;
+
+ static X509_Certificate make_cert(PK_Signer*,
+ RandomNumberGenerator&,
+ const AlgorithmIdentifier&,
+ const MemoryRegion<byte>&,
+ const X509_Time&, const X509_Time&,
+ const X509_DN&, const X509_DN&,
+ const Extensions&);
+
+ X509_CA(const X509_Certificate&, const Private_Key&);
+ ~X509_CA();
+ private:
+ X509_CA(const X509_CA&) {}
+ X509_CA& operator=(const X509_CA&) { return (*this); }
+
+ X509_CRL make_crl(const std::vector<CRL_Entry>&,
+ u32bit, u32bit, RandomNumberGenerator&) const;
+
+ AlgorithmIdentifier ca_sig_algo;
+ X509_Certificate cert;
+ PK_Signer* signer;
+ };
+
+/*************************************************
+* Choose a signing format for the key *
+*************************************************/
+BOTAN_DLL PK_Signer* choose_sig_format(const Private_Key&,
+ AlgorithmIdentifier&);
+
+
+}
+
+#endif
diff --git a/src/cert/x509/x509_crl.cpp b/src/cert/x509/x509_crl.cpp
new file mode 100644
index 000000000..6805abef6
--- /dev/null
+++ b/src/cert/x509/x509_crl.cpp
@@ -0,0 +1,145 @@
+/*************************************************
+* X.509 CRL Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.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"), throw_on_unknown_critical(touc)
+ {
+ do_decode();
+ }
+
+/*************************************************
+* Load a X.509 CRL *
+*************************************************/
+X509_CRL::X509_CRL(const std::string& in, bool touc) :
+ X509_Object(in, "CRL/X509 CRL"), throw_on_unknown_critical(touc)
+ {
+ do_decode();
+ }
+
+/*************************************************
+* Decode the TBSCertList data *
+*************************************************/
+void X509_CRL::force_decode()
+ {
+ BER_Decoder tbs_crl(tbs_bits);
+
+ u32bit version;
+ tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+
+ if(version != 0 && version != 1)
+ throw X509_CRL_Error("Unknown X.509 CRL version " +
+ to_string(version+1));
+
+ AlgorithmIdentifier sig_algo_inner;
+ tbs_crl.decode(sig_algo_inner);
+
+ if(sig_algo != sig_algo_inner)
+ throw X509_CRL_Error("Algorithm identifier mismatch");
+
+ X509_DN dn_issuer;
+ tbs_crl.decode(dn_issuer);
+ info.add(dn_issuer.contents());
+
+ X509_Time start, end;
+ tbs_crl.decode(start).decode(end);
+ info.add("X509.CRL.start", start.readable_string());
+ info.add("X509.CRL.end", end.readable_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(throw_on_unknown_critical);
+ cert_list.decode(entry);
+ 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(throw_on_unknown_critical);
+
+ crl_options.decode(extensions).verify_end();
+
+ extensions.contents_to(info, 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 revoked;
+ }
+
+/*************************************************
+* Return the distinguished name of the issuer *
+*************************************************/
+X509_DN X509_CRL::issuer_dn() const
+ {
+ return create_dn(info);
+ }
+
+/*************************************************
+* Return the key identifier of the issuer *
+*************************************************/
+MemoryVector<byte> X509_CRL::authority_key_id() const
+ {
+ return info.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ }
+
+/*************************************************
+* Return the CRL number of this CRL *
+*************************************************/
+u32bit X509_CRL::crl_number() const
+ {
+ return info.get1_u32bit("X509v3.CRLNumber");
+ }
+
+/*************************************************
+* Return the issue data of the CRL *
+*************************************************/
+X509_Time X509_CRL::this_update() const
+ {
+ return info.get1("X509.CRL.start");
+ }
+
+/*************************************************
+* Return the date when a new CRL will be issued *
+*************************************************/
+X509_Time X509_CRL::next_update() const
+ {
+ return info.get1("X509.CRL.end");
+ }
+
+}
diff --git a/src/cert/x509/x509_crl.h b/src/cert/x509/x509_crl.h
new file mode 100644
index 000000000..ec73e9fdf
--- /dev/null
+++ b/src/cert/x509/x509_crl.h
@@ -0,0 +1,48 @@
+/*************************************************
+* X.509 CRL Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_CRL_H__
+#define BOTAN_X509_CRL_H__
+
+#include <botan/x509_obj.h>
+#include <botan/crl_ent.h>
+#include <vector>
+
+namespace Botan {
+
+/*************************************************
+* X.509 CRL *
+*************************************************/
+class BOTAN_DLL X509_CRL : public X509_Object
+ {
+ public:
+ struct X509_CRL_Error : public Exception
+ {
+ X509_CRL_Error(const std::string& error) :
+ Exception("X509_CRL: " + error) {}
+ };
+
+ std::vector<CRL_Entry> get_revoked() const;
+
+ X509_DN issuer_dn() const;
+ MemoryVector<byte> authority_key_id() const;
+
+ u32bit crl_number() const;
+ X509_Time this_update() const;
+ X509_Time next_update() const;
+
+ X509_CRL(DataSource&, bool throw_on_unknown_critical = false);
+ X509_CRL(const std::string&, bool throw_on_unknown_critical = false);
+ private:
+ void force_decode();
+
+ bool throw_on_unknown_critical;
+ std::vector<CRL_Entry> revoked;
+ Data_Store info;
+ };
+
+}
+
+#endif
diff --git a/src/cert/x509/x509_ext.cpp b/src/cert/x509/x509_ext.cpp
new file mode 100644
index 000000000..3595a0492
--- /dev/null
+++ b/src/cert/x509/x509_ext.cpp
@@ -0,0 +1,579 @@
+/*************************************************
+* X.509 Certificate Extensions Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/lookup.h>
+#include <botan/oids.h>
+#include <botan/libstate.h>
+#include <botan/bit_ops.h>
+#include <algorithm>
+#include <memory>
+
+namespace Botan {
+
+/*************************************************
+* List of X.509 Certificate Extensions *
+*************************************************/
+Certificate_Extension* Extensions::get_extension(const OID& oid)
+ {
+#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.CRLNumber", CRL_Number);
+ X509_EXTENSION("X509v3.CertificatePolicies", Certificate_Policies);
+
+ return 0;
+ }
+
+/*************************************************
+* Extensions Copy Constructor *
+*************************************************/
+Extensions::Extensions(const Extensions& extensions) : ASN1_Object()
+ {
+ *this = extensions;
+ }
+
+/*************************************************
+* Extensions Assignment Operator *
+*************************************************/
+Extensions& Extensions::operator=(const Extensions& other)
+ {
+ for(u32bit j = 0; j != extensions.size(); ++j)
+ delete extensions[j];
+ extensions.clear();
+
+ for(u32bit j = 0; j != other.extensions.size(); ++j)
+ extensions.push_back(other.extensions[j]->copy());
+
+ return (*this);
+ }
+
+/*************************************************
+* Return the OID of this extension *
+*************************************************/
+OID Certificate_Extension::oid_of() const
+ {
+ return OIDS::lookup(oid_name());
+ }
+
+/*************************************************
+* Encode an Extensions list *
+*************************************************/
+void Extensions::encode_into(DER_Encoder& to_object) const
+ {
+ for(u32bit j = 0; j != extensions.size(); ++j)
+ {
+ const Certificate_Extension* ext = extensions[j];
+
+ std::string setting;
+
+ if(ext->config_id() != "")
+ setting = global_state().option("x509/exts/" + ext->config_id());
+
+ if(setting == "")
+ setting = "yes";
+
+ if(setting != "yes" && setting != "no" && setting != "critical")
+ throw Invalid_Argument("X509_CA:: Invalid value for option "
+ "x509/exts/" + ext->config_id() + " of " +
+ setting);
+
+ bool is_critical = (setting == "critical");
+ bool should_encode = ext->should_encode() && (setting != "no");
+
+ 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)
+ {
+ for(u32bit j = 0; j != extensions.size(); ++j)
+ delete extensions[j];
+ extensions.clear();
+
+ BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+ while(sequence.more_items())
+ {
+ OID oid;
+ MemoryVector<byte> value;
+ bool critical;
+
+ sequence.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+ .decode(value, OCTET_STRING)
+ .verify_end()
+ .end_cons();
+
+ Certificate_Extension* ext = get_extension(oid);
+
+ if(!ext)
+ {
+ if(!critical || !should_throw)
+ continue;
+
+ throw Decoding_Error("Encountered unknown X.509 extension marked "
+ "as critical; OID = " + oid.as_string());
+ }
+
+ ext->decode_inner(value);
+
+ extensions.push_back(ext);
+ }
+ sequence.verify_end();
+ }
+
+/*************************************************
+* Write the extensions to an info store *
+*************************************************/
+void Extensions::contents_to(Data_Store& subject_info,
+ Data_Store& issuer_info) const
+ {
+ for(u32bit j = 0; j != extensions.size(); ++j)
+ extensions[j]->contents_to(subject_info, issuer_info);
+ }
+
+/*************************************************
+* Delete an Extensions list *
+*************************************************/
+Extensions::~Extensions()
+ {
+ for(u32bit j = 0; j != extensions.size(); ++j)
+ delete extensions[j];
+ }
+
+namespace Cert_Extension {
+
+/*************************************************
+* Checked accessor for the path_limit member *
+*************************************************/
+u32bit Basic_Constraints::get_path_limit() const
+ {
+ if(!is_ca)
+ throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA");
+ return path_limit;
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Basic_Constraints::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_if(is_ca,
+ DER_Encoder()
+ .encode(is_ca)
+ .encode_optional(path_limit, NO_CERT_PATH_LIMIT)
+ )
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Basic_Constraints::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional(is_ca, BOOLEAN, UNIVERSAL, false)
+ .decode_optional(path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+ .verify_end()
+ .end_cons();
+
+ if(is_ca == false)
+ path_limit = 0;
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void Basic_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.BasicConstraints.is_ca", (is_ca ? 1 : 0));
+ subject.add("X509v3.BasicConstraints.path_constraint", path_limit);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Key_Usage::encode_inner() const
+ {
+ if(constraints == NO_CONSTRAINTS)
+ throw Encoding_Error("Cannot encode zero usage constraints");
+
+ const u32bit unused_bits = low_bit(constraints) - 1;
+
+ SecureVector<byte> der;
+ der.append(BIT_STRING);
+ der.append(2 + ((unused_bits < 8) ? 1 : 0));
+ der.append(unused_bits % 8);
+ der.append((constraints >> 8) & 0xFF);
+ if(constraints & 0xFF)
+ der.append(constraints & 0xFF);
+
+ return der;
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Key_Usage::decode_inner(const MemoryRegion<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(u32bit j = 1; j != obj.value.size(); ++j)
+ usage = (obj.value[j] << 8) | usage;
+
+ constraints = Key_Constraints(usage);
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.KeyUsage", constraints);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Subject_Key_ID::encode_inner() const
+ {
+ return DER_Encoder().encode(key_id, OCTET_STRING).get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Subject_Key_ID::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in).decode(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", key_id);
+ }
+
+/*************************************************
+* Subject_Key_ID Constructor *
+*************************************************/
+Subject_Key_ID::Subject_Key_ID(const MemoryRegion<byte>& pub_key)
+ {
+ std::auto_ptr<HashFunction> hash(get_hash("SHA-1"));
+ key_id = hash->process(pub_key);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Authority_Key_ID::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Authority_Key_ID::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional_string(key_id, OCTET_STRING, 0);
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
+ {
+ if(key_id.size())
+ issuer.add("X509v3.AuthorityKeyIdentifier", key_id);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Alternative_Name::encode_inner() const
+ {
+ return DER_Encoder().encode(alt_name).get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Alternative_Name::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in).decode(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(oid_name_str == "X509v3.SubjectAlternativeName")
+ subject_info.add(contents);
+ else if(oid_name_str == "X509v3.IssuerAlternativeName")
+ issuer_info.add(contents);
+ else
+ throw Internal_Error("In Alternative_Name, unknown type " +
+ oid_name_str);
+ }
+
+/*************************************************
+* Alternative_Name Constructor *
+*************************************************/
+Alternative_Name::Alternative_Name(const AlternativeName& alt_name,
+ const std::string& oid_name_str,
+ const std::string& config_name_str)
+ {
+ this->alt_name = alt_name;
+ this->oid_name_str = oid_name_str;
+ this->config_name_str = config_name_str;
+ }
+
+/*************************************************
+* Subject_Alternative_Name Constructor *
+*************************************************/
+Subject_Alternative_Name::Subject_Alternative_Name(
+ const AlternativeName& name) :
+
+ Alternative_Name(name, "X509v3.SubjectAlternativeName",
+ "subject_alternative_name")
+ {
+ }
+
+/*************************************************
+* Issuer_Alternative_Name Constructor *
+*************************************************/
+Issuer_Alternative_Name::Issuer_Alternative_Name(const AlternativeName& name) :
+ Alternative_Name(name, "X509v3.IssuerAlternativeName",
+ "issuer_alternative_name")
+ {
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Extended_Key_Usage::encode_inner() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(oids)
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Extended_Key_Usage::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_list(oids)
+ .end_cons();
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(u32bit j = 0; j != oids.size(); ++j)
+ subject.add("X509v3.ExtendedKeyUsage", oids[j].as_string());
+ }
+
+namespace {
+
+/*************************************************
+* A policy specifier *
+*************************************************/
+class Policy_Information : public ASN1_Object
+ {
+ public:
+ OID oid;
+
+ void encode_into(DER_Encoder& codec) const
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(oid)
+ .end_cons();
+ }
+
+ void decode_from(BER_Decoder& codec)
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(oid)
+ .discard_remaining()
+ .end_cons();
+ }
+ };
+
+}
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> Certificate_Policies::encode_inner() const
+ {
+ throw Exception("Certificate_Policies::encode_inner: Bugged");
+
+ std::vector<Policy_Information> policies;
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(policies)
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void Certificate_Policies::decode_inner(const MemoryRegion<byte>& in)
+ {
+ std::vector<Policy_Information> policies;
+
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_list(policies)
+ .end_cons();
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void Certificate_Policies::contents_to(Data_Store& info, Data_Store&) const
+ {
+ for(u32bit j = 0; j != oids.size(); ++j)
+ info.add("X509v3.ExtendedKeyUsage", oids[j].as_string());
+ }
+
+/*************************************************
+* Checked accessor for the crl_number member *
+*************************************************/
+u32bit CRL_Number::get_crl_number() const
+ {
+ if(!has_value)
+ throw Invalid_State("CRL_Number::get_crl_number: Not set");
+ return crl_number;
+ }
+
+/*************************************************
+* Copy a CRL_Number extension *
+*************************************************/
+CRL_Number* CRL_Number::copy() const
+ {
+ if(!has_value)
+ throw Invalid_State("CRL_Number::copy: Not set");
+ return new CRL_Number(crl_number);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> CRL_Number::encode_inner() const
+ {
+ return DER_Encoder().encode(crl_number).get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void CRL_Number::decode_inner(const MemoryRegion<byte>& in)
+ {
+ BER_Decoder(in).decode(crl_number);
+ }
+
+/*************************************************
+* Return a textual representation *
+*************************************************/
+void CRL_Number::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLNumber", crl_number);
+ }
+
+/*************************************************
+* Encode the extension *
+*************************************************/
+MemoryVector<byte> CRL_ReasonCode::encode_inner() const
+ {
+ return DER_Encoder()
+ .encode(static_cast<u32bit>(reason), ENUMERATED, UNIVERSAL)
+ .get_contents();
+ }
+
+/*************************************************
+* Decode the extension *
+*************************************************/
+void CRL_ReasonCode::decode_inner(const MemoryRegion<byte>& in)
+ {
+ u32bit reason_code = 0;
+ BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+ 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", reason);
+ }
+
+}
+
+}
diff --git a/src/cert/x509/x509_ext.h b/src/cert/x509/x509_ext.h
new file mode 100644
index 000000000..5b302df0d
--- /dev/null
+++ b/src/cert/x509/x509_ext.h
@@ -0,0 +1,315 @@
+/*************************************************
+* X.509 Certificate Extensions Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_EXTENSIONS_H__
+#define BOTAN_X509_EXTENSIONS_H__
+
+#include <botan/asn1_int.h>
+#include <botan/asn1_oid.h>
+#include <botan/asn1_obj.h>
+#include <botan/datastor.h>
+#include <botan/enums.h>
+
+namespace Botan {
+
+/*************************************************
+* X.509 Certificate Extension *
+*************************************************/
+class BOTAN_DLL Certificate_Extension
+ {
+ public:
+ OID oid_of() const;
+
+ virtual Certificate_Extension* copy() const = 0;
+
+ virtual void contents_to(Data_Store&, Data_Store&) const = 0;
+ virtual std::string config_id() const = 0;
+ virtual std::string oid_name() const = 0;
+
+ virtual ~Certificate_Extension() {}
+ protected:
+ friend class Extensions;
+ virtual bool should_encode() const { return true; }
+ virtual MemoryVector<byte> encode_inner() const = 0;
+ virtual void decode_inner(const MemoryRegion<byte>&) = 0;
+ };
+
+/*************************************************
+* X.509 Certificate Extension List *
+*************************************************/
+class BOTAN_DLL Extensions : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ void add(Certificate_Extension* extn)
+ { extensions.push_back(extn); }
+
+ Extensions& operator=(const Extensions&);
+
+ Extensions(const Extensions&);
+ Extensions(bool st = true) : should_throw(st) {}
+ ~Extensions();
+ private:
+ static Certificate_Extension* get_extension(const OID&);
+
+ std::vector<Certificate_Extension*> extensions;
+ bool should_throw;
+ };
+
+namespace Cert_Extension {
+
+/*************************************************
+* Basic Constraints Extension *
+*************************************************/
+class BOTAN_DLL Basic_Constraints : public Certificate_Extension
+ {
+ public:
+ Basic_Constraints* copy() const
+ { return new Basic_Constraints(is_ca, path_limit); }
+
+ Basic_Constraints(bool ca = false, u32bit limit = 0) :
+ is_ca(ca), path_limit(limit) {}
+
+ bool get_is_ca() const { return is_ca; }
+ u32bit get_path_limit() const;
+ private:
+ std::string config_id() const { return "basic_constraints"; }
+ std::string oid_name() const { return "X509v3.BasicConstraints"; }
+
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ bool is_ca;
+ u32bit path_limit;
+ };
+
+/*************************************************
+* Key Usage Constraints Extension *
+*************************************************/
+class BOTAN_DLL Key_Usage : public Certificate_Extension
+ {
+ public:
+ Key_Usage* copy() const { return new Key_Usage(constraints); }
+
+ Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : constraints(c) {}
+
+ Key_Constraints get_constraints() const { return constraints; }
+ private:
+ std::string config_id() const { return "key_usage"; }
+ std::string oid_name() const { return "X509v3.KeyUsage"; }
+
+ bool should_encode() const { return (constraints != NO_CONSTRAINTS); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ Key_Constraints constraints;
+ };
+
+/*************************************************
+* Subject Key Identifier Extension *
+*************************************************/
+class BOTAN_DLL Subject_Key_ID : public Certificate_Extension
+ {
+ public:
+ Subject_Key_ID* copy() const { return new Subject_Key_ID(key_id); }
+
+ Subject_Key_ID() {}
+ Subject_Key_ID(const MemoryRegion<byte>&);
+
+ MemoryVector<byte> get_key_id() const { return key_id; }
+ private:
+ std::string config_id() const { return "subject_key_id"; }
+ std::string oid_name() const { return "X509v3.SubjectKeyIdentifier"; }
+
+ bool should_encode() const { return (key_id.size() > 0); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ MemoryVector<byte> key_id;
+ };
+
+/*************************************************
+* Authority Key Identifier Extension *
+*************************************************/
+class BOTAN_DLL Authority_Key_ID : public Certificate_Extension
+ {
+ public:
+ Authority_Key_ID* copy() const { return new Authority_Key_ID(key_id); }
+
+ Authority_Key_ID() {}
+ Authority_Key_ID(const MemoryRegion<byte>& k) : key_id(k) {}
+
+ MemoryVector<byte> get_key_id() const { return key_id; }
+ private:
+ std::string config_id() const { return "authority_key_id"; }
+ std::string oid_name() const { return "X509v3.AuthorityKeyIdentifier"; }
+
+ bool should_encode() const { return (key_id.size() > 0); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ MemoryVector<byte> key_id;
+ };
+
+/*************************************************
+* Alternative Name Extension Base Class *
+*************************************************/
+class BOTAN_DLL Alternative_Name : public Certificate_Extension
+ {
+ public:
+ AlternativeName get_alt_name() const { return alt_name; }
+
+ protected:
+ Alternative_Name(const AlternativeName&,
+ const std::string&, const std::string&);
+
+ Alternative_Name(const std::string&, const std::string&);
+ private:
+ std::string config_id() const { return config_name_str; }
+ std::string oid_name() const { return oid_name_str; }
+
+ bool should_encode() const { return alt_name.has_items(); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ std::string config_name_str, oid_name_str;
+ AlternativeName alt_name;
+ };
+
+/*************************************************
+* Subject Alternative Name Extension *
+*************************************************/
+class BOTAN_DLL Subject_Alternative_Name : public Alternative_Name
+ {
+ public:
+ Subject_Alternative_Name* copy() const
+ { return new Subject_Alternative_Name(get_alt_name()); }
+
+ 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
+ { return new Issuer_Alternative_Name(get_alt_name()); }
+
+ Issuer_Alternative_Name(const AlternativeName& = AlternativeName());
+ };
+
+/*************************************************
+* Extended Key Usage Extension *
+*************************************************/
+class BOTAN_DLL Extended_Key_Usage : public Certificate_Extension
+ {
+ public:
+ Extended_Key_Usage* copy() const { return new Extended_Key_Usage(oids); }
+
+ Extended_Key_Usage() {}
+ Extended_Key_Usage(const std::vector<OID>& o) : oids(o) {}
+
+ std::vector<OID> get_oids() const { return oids; }
+ private:
+ std::string config_id() const { return "extended_key_usage"; }
+ std::string oid_name() const { return "X509v3.ExtendedKeyUsage"; }
+
+ bool should_encode() const { return (oids.size() > 0); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ std::vector<OID> oids;
+ };
+
+/*************************************************
+* Certificate Policies Extension *
+*************************************************/
+class BOTAN_DLL Certificate_Policies : public Certificate_Extension
+ {
+ public:
+ Certificate_Policies* copy() const
+ { return new Certificate_Policies(oids); }
+
+ Certificate_Policies() {}
+ Certificate_Policies(const std::vector<OID>& o) : oids(o) {}
+
+ std::vector<OID> get_oids() const { return oids; }
+ private:
+ std::string config_id() const { return "policy_info"; }
+ std::string oid_name() const { return "X509v3.CertificatePolicies"; }
+
+ bool should_encode() const { return (oids.size() > 0); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ std::vector<OID> oids;
+ };
+
+/*************************************************
+* CRL Number Extension *
+*************************************************/
+class BOTAN_DLL CRL_Number : public Certificate_Extension
+ {
+ public:
+ CRL_Number* copy() const;
+
+ CRL_Number() : has_value(false), crl_number(0) {}
+ CRL_Number(u32bit n) : has_value(true), crl_number(n) {}
+
+ u32bit get_crl_number() const;
+ private:
+ std::string config_id() const { return "crl_number"; }
+ std::string oid_name() const { return "X509v3.CRLNumber"; }
+
+ bool should_encode() const { return has_value; }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ bool has_value;
+ u32bit crl_number;
+ };
+
+/*************************************************
+* CRL Entry Reason Code Extension *
+*************************************************/
+class BOTAN_DLL CRL_ReasonCode : public Certificate_Extension
+ {
+ public:
+ CRL_ReasonCode* copy() const { return new CRL_ReasonCode(reason); }
+
+ CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : reason(r) {}
+
+ CRL_Code get_reason() const { return reason; }
+ private:
+ std::string config_id() const { return "crl_reason"; }
+ std::string oid_name() const { return "X509v3.ReasonCode"; }
+
+ bool should_encode() const { return (reason != UNSPECIFIED); }
+ MemoryVector<byte> encode_inner() const;
+ void decode_inner(const MemoryRegion<byte>&);
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ CRL_Code reason;
+ };
+
+}
+
+}
+
+#endif
diff --git a/src/cert/x509/x509_obj.cpp b/src/cert/x509/x509_obj.cpp
new file mode 100644
index 000000000..e78790949
--- /dev/null
+++ b/src/cert/x509/x509_obj.cpp
@@ -0,0 +1,233 @@
+/*************************************************
+* X.509 SIGNED Object Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509_obj.h>
+#include <botan/x509_key.h>
+#include <botan/look_pk.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>
+#include <memory>
+
+namespace Botan {
+
+/*************************************************
+* Create a generic X.509 object *
+*************************************************/
+X509_Object::X509_Object(DataSource& stream, const std::string& labels)
+ {
+ init(stream, labels);
+ }
+
+/*************************************************
+* Createa 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);
+ }
+
+/*************************************************
+* Read a PEM or BER X.509 object *
+*************************************************/
+void X509_Object::init(DataSource& in, const std::string& labels)
+ {
+ PEM_labels_allowed = split_on(labels, '/');
+ if(PEM_labels_allowed.size() < 1)
+ throw Invalid_Argument("Bad labels argument to X509_Object");
+
+ PEM_label_pref = PEM_labels_allowed[0];
+ std::sort(PEM_labels_allowed.begin(), PEM_labels_allowed.end());
+
+ try {
+ if(ASN1::maybe_BER(in) && !PEM_Code::matches(in))
+ decode_info(in);
+ else
+ {
+ std::string got_label;
+ DataSource_Memory ber(PEM_Code::decode(in, got_label));
+
+ if(!std::binary_search(PEM_labels_allowed.begin(),
+ PEM_labels_allowed.end(), got_label))
+ throw Decoding_Error("Invalid PEM label: " + got_label);
+ decode_info(ber);
+ }
+ }
+ catch(Decoding_Error)
+ {
+ throw Decoding_Error(PEM_label_pref + " decoding failed");
+ }
+ }
+
+/*************************************************
+* Read a BER encoded X.509 object *
+*************************************************/
+void X509_Object::decode_info(DataSource& source)
+ {
+ BER_Decoder(source)
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .end_cons()
+ .decode(sig_algo)
+ .decode(sig, BIT_STRING)
+ .verify_end()
+ .end_cons();
+ }
+
+/*************************************************
+* Return a BER or PEM encoded X.509 object *
+*************************************************/
+void X509_Object::encode(Pipe& out, X509_Encoding encoding) const
+ {
+ SecureVector<byte> der = DER_Encoder()
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .end_cons()
+ .encode(sig_algo)
+ .encode(sig, BIT_STRING)
+ .end_cons()
+ .get_contents();
+
+ if(encoding == PEM)
+ out.write(PEM_Code::encode(der, PEM_label_pref));
+ else
+ out.write(der);
+ }
+
+/*************************************************
+* Return a BER encoded X.509 object *
+*************************************************/
+SecureVector<byte> X509_Object::BER_encode() const
+ {
+ Pipe ber;
+ ber.start_msg();
+ encode(ber, RAW_BER);
+ ber.end_msg();
+ return ber.read_all();
+ }
+
+/*************************************************
+* Return a PEM encoded X.509 object *
+*************************************************/
+std::string X509_Object::PEM_encode() const
+ {
+ Pipe pem;
+ pem.start_msg();
+ encode(pem, PEM);
+ pem.end_msg();
+ return pem.read_all_as_string();
+ }
+
+/*************************************************
+* Return the TBS data *
+*************************************************/
+SecureVector<byte> X509_Object::tbs_data() const
+ {
+ return ASN1::put_in_sequence(tbs_bits);
+ }
+
+/*************************************************
+* Return the signature of this object *
+*************************************************/
+SecureVector<byte> X509_Object::signature() const
+ {
+ return sig;
+ }
+
+/*************************************************
+* Return the algorithm used to sign this object *
+*************************************************/
+AlgorithmIdentifier X509_Object::signature_algorithm() const
+ {
+ return sig_algo;
+ }
+
+/*************************************************
+* Check the signature on an object *
+*************************************************/
+bool X509_Object::check_signature(Public_Key& pub_key) const
+ {
+ try {
+ 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())
+ return false;
+
+ std::string padding = sig_info[1];
+ Signature_Format format =
+ (pub_key.message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363;
+
+ std::auto_ptr<PK_Verifier> verifier;
+
+ if(dynamic_cast<PK_Verifying_with_MR_Key*>(&pub_key))
+ {
+ PK_Verifying_with_MR_Key& sig_key =
+ dynamic_cast<PK_Verifying_with_MR_Key&>(pub_key);
+ verifier.reset(get_pk_verifier(sig_key, padding, format));
+ }
+ else if(dynamic_cast<PK_Verifying_wo_MR_Key*>(&pub_key))
+ {
+ PK_Verifying_wo_MR_Key& sig_key =
+ dynamic_cast<PK_Verifying_wo_MR_Key&>(pub_key);
+ verifier.reset(get_pk_verifier(sig_key, padding, format));
+ }
+ else
+ return false;
+
+ return verifier->verify_message(tbs_data(), signature());
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+/*************************************************
+* Apply the X.509 SIGNED macro *
+*************************************************/
+MemoryVector<byte> X509_Object::make_signed(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& algo,
+ const MemoryRegion<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();
+ }
+
+/*************************************************
+* Try to decode the actual information *
+*************************************************/
+void X509_Object::do_decode()
+ {
+ try {
+ force_decode();
+ }
+ catch(Decoding_Error& e)
+ {
+ const std::string what = e.what();
+ throw Decoding_Error(PEM_label_pref + " decoding failed (" +
+ what.substr(23, std::string::npos) + ")");
+ }
+ catch(Invalid_Argument& e)
+ {
+ const std::string what = e.what();
+ throw Decoding_Error(PEM_label_pref + " decoding failed (" +
+ what.substr(7, std::string::npos) + ")");
+ }
+ }
+
+}
diff --git a/src/cert/x509/x509_obj.h b/src/cert/x509/x509_obj.h
new file mode 100644
index 000000000..8808fd686
--- /dev/null
+++ b/src/cert/x509/x509_obj.h
@@ -0,0 +1,56 @@
+/*************************************************
+* X.509 SIGNED Object Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_OBJECT_H__
+#define BOTAN_X509_OBJECT_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/pipe.h>
+#include <botan/enums.h>
+#include <botan/rng.h>
+#include <vector>
+
+namespace Botan {
+
+/*************************************************
+* Generic X.509 SIGNED Object *
+*************************************************/
+class BOTAN_DLL X509_Object
+ {
+ public:
+ SecureVector<byte> tbs_data() const;
+ SecureVector<byte> signature() const;
+ AlgorithmIdentifier signature_algorithm() const;
+
+ static MemoryVector<byte> make_signed(class PK_Signer*,
+ RandomNumberGenerator&,
+ const AlgorithmIdentifier&,
+ const MemoryRegion<byte>&);
+
+ bool check_signature(class Public_Key&) const;
+
+ void encode(Pipe&, X509_Encoding = PEM) const;
+ SecureVector<byte> BER_encode() const;
+ std::string PEM_encode() const;
+
+ X509_Object(DataSource&, const std::string&);
+ X509_Object(const std::string&, const std::string&);
+ virtual ~X509_Object() {}
+ protected:
+ void do_decode();
+ X509_Object() {}
+ AlgorithmIdentifier sig_algo;
+ SecureVector<byte> tbs_bits, sig;
+ private:
+ virtual void force_decode() = 0;
+ void init(DataSource&, const std::string&);
+ void decode_info(DataSource&);
+ std::vector<std::string> PEM_labels_allowed;
+ std::string PEM_label_pref;
+ };
+
+}
+
+#endif
diff --git a/src/cert/x509/x509cert.cpp b/src/cert/x509/x509cert.cpp
new file mode 100644
index 000000000..b24e6a036
--- /dev/null
+++ b/src/cert/x509/x509cert.cpp
@@ -0,0 +1,364 @@
+/*************************************************
+* X.509 Certificates Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <algorithm>
+
+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;
+
+ std::vector<std::string>::const_iterator i = in.begin();
+ while(i != in.end())
+ {
+ out.push_back(OIDS::lookup(OID(*i)));
+ ++i;
+ }
+ return out;
+ }
+
+}
+
+/*************************************************
+* X509_Certificate Constructor *
+*************************************************/
+X509_Certificate::X509_Certificate(DataSource& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
+ {
+ self_signed = false;
+ do_decode();
+ }
+
+/*************************************************
+* X509_Certificate Constructor *
+*************************************************/
+X509_Certificate::X509_Certificate(const std::string& in) :
+ X509_Object(in, "CERTIFICATE/X509 CERTIFICATE")
+ {
+ self_signed = false;
+ do_decode();
+ }
+
+/*************************************************
+* Decode the TBSCertificate data *
+*************************************************/
+void X509_Certificate::force_decode()
+ {
+ u32bit version;
+ BigInt serial_bn;
+ AlgorithmIdentifier sig_algo_inner;
+ X509_DN dn_issuer, dn_subject;
+ X509_Time start, end;
+
+ BER_Decoder tbs_cert(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 " + to_string(version));
+ if(sig_algo != sig_algo_inner)
+ throw Decoding_Error("Algorithm identifier mismatch");
+
+ self_signed = (dn_subject == dn_issuer);
+
+ subject.add(dn_subject.contents());
+ issuer.add(dn_issuer.contents());
+
+ 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);
+
+ MemoryVector<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))
+ {
+ Extensions extensions;
+
+ BER_Decoder(v3_exts_data.value).decode(extensions).verify_end();
+
+ extensions.contents_to(subject, 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");
+
+ subject.add("X509.Certificate.version", version);
+ subject.add("X509.Certificate.serial", BigInt::encode(serial_bn));
+ subject.add("X509.Certificate.start", start.readable_string());
+ subject.add("X509.Certificate.end", end.readable_string());
+
+ issuer.add("X509.Certificate.v2.key_id", v2_issuer_key_id);
+ subject.add("X509.Certificate.v2.key_id", v2_subject_key_id);
+
+ subject.add("X509.Certificate.public_key",
+ PEM_Code::encode(
+ ASN1::put_in_sequence(public_key.value),
+ "PUBLIC KEY"
+ )
+ );
+
+ if(is_CA_cert() &&
+ !subject.has_value("X509v3.BasicConstraints.path_constraint"))
+ {
+ u32bit limit = (x509_version() < 3) ? NO_CERT_PATH_LIMIT : 0;
+ subject.add("X509v3.BasicConstraints.path_constraint", limit);
+ }
+ }
+
+/*************************************************
+* Return the X.509 version in use *
+*************************************************/
+u32bit X509_Certificate::x509_version() const
+ {
+ return (subject.get1_u32bit("X509.Certificate.version") + 1);
+ }
+
+/*************************************************
+* Return the time this cert becomes valid *
+*************************************************/
+std::string X509_Certificate::start_time() const
+ {
+ return subject.get1("X509.Certificate.start");
+ }
+
+/*************************************************
+* Return the time this cert becomes invalid *
+*************************************************/
+std::string X509_Certificate::end_time() const
+ {
+ return subject.get1("X509.Certificate.end");
+ }
+
+/*************************************************
+* Return information about the subject *
+*************************************************/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& what) const
+ {
+ return 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 issuer.get(X509_DN::deref_info_field(what));
+ }
+
+/*************************************************
+* Return the public key in this certificate *
+*************************************************/
+Public_Key* X509_Certificate::subject_public_key() const
+ {
+ DataSource_Memory source(subject.get1("X509.Certificate.public_key"));
+ return X509::load_key(source);
+ }
+
+/*************************************************
+* Check if the certificate is for a CA *
+*************************************************/
+bool X509_Certificate::is_CA_cert() const
+ {
+ if(!subject.get1_u32bit("X509v3.BasicConstraints.is_ca"))
+ return false;
+ if((constraints() & KEY_CERT_SIGN) || (constraints() == NO_CONSTRAINTS))
+ return true;
+ return false;
+ }
+
+/*************************************************
+* Return the path length constraint *
+*************************************************/
+u32bit X509_Certificate::path_limit() const
+ {
+ return subject.get1_u32bit("X509v3.BasicConstraints.path_constraint", 0);
+ }
+
+/*************************************************
+* Return the key usage constraints *
+*************************************************/
+Key_Constraints X509_Certificate::constraints() const
+ {
+ return Key_Constraints(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(subject.get("X509v3.ExtendedKeyUsage"));
+ }
+
+/*************************************************
+* Return the list of certificate policies *
+*************************************************/
+std::vector<std::string> X509_Certificate::policies() const
+ {
+ return lookup_oids(subject.get("X509v3.CertificatePolicies"));
+ }
+
+/*************************************************
+* Return the authority key id *
+*************************************************/
+MemoryVector<byte> X509_Certificate::authority_key_id() const
+ {
+ return issuer.get1_memvec("X509v3.AuthorityKeyIdentifier");
+ }
+
+/*************************************************
+* Return the subject key id *
+*************************************************/
+MemoryVector<byte> X509_Certificate::subject_key_id() const
+ {
+ return subject.get1_memvec("X509v3.SubjectKeyIdentifier");
+ }
+
+/*************************************************
+* Return the certificate serial number *
+*************************************************/
+MemoryVector<byte> X509_Certificate::serial_number() const
+ {
+ return subject.get1_memvec("X509.Certificate.serial");
+ }
+
+/*************************************************
+* Return the distinguished name of the issuer *
+*************************************************/
+X509_DN X509_Certificate::issuer_dn() const
+ {
+ return create_dn(issuer);
+ }
+
+/*************************************************
+* Return the distinguished name of the subject *
+*************************************************/
+X509_DN X509_Certificate::subject_dn() const
+ {
+ return create_dn(subject);
+ }
+
+/*************************************************
+* Compare two certificates for equality *
+*************************************************/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+ {
+ return (sig == other.sig &&
+ sig_algo == other.sig_algo &&
+ self_signed == other.self_signed &&
+ issuer == other.issuer &&
+ subject == other.subject);
+ }
+
+/*************************************************
+* X.509 Certificate Comparison *
+*************************************************/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+ {
+ return !(cert1 == cert2);
+ }
+
+/*************************************************
+* Create and populate a X509_DN *
+*************************************************/
+X509_DN create_dn(const Data_Store& info)
+ {
+ class DN_Matcher : public Data_Store::Matcher
+ {
+ public:
+ bool operator()(const std::string& key, const std::string&) const
+ {
+ if(key.find("X520.") != std::string::npos)
+ return true;
+ return false;
+ }
+ };
+
+ std::multimap<std::string, std::string> names =
+ info.search_with(DN_Matcher());
+
+ X509_DN dn;
+
+ std::multimap<std::string, std::string>::iterator j;
+ for(j = names.begin(); j != names.end(); ++j)
+ dn.add_attribute(j->first, j->second);
+
+ return dn;
+ }
+
+/*************************************************
+* Create and populate an AlternativeName *
+*************************************************/
+AlternativeName create_alt_name(const Data_Store& info)
+ {
+ class AltName_Matcher : public Data_Store::Matcher
+ {
+ public:
+ bool operator()(const std::string& key, const std::string&) const
+ {
+ for(u32bit j = 0; j != matches.size(); ++j)
+ if(key.compare(matches[j]) == 0)
+ return true;
+ return false;
+ }
+
+ AltName_Matcher(const std::string& match_any_of)
+ {
+ matches = split_on(match_any_of, '/');
+ }
+ private:
+ std::vector<std::string> matches;
+ };
+
+ std::multimap<std::string, std::string> names =
+ info.search_with(AltName_Matcher("RFC822/DNS/URI/IP"));
+
+ AlternativeName alt_name;
+
+ std::multimap<std::string, std::string>::iterator j;
+ for(j = names.begin(); j != names.end(); ++j)
+ alt_name.add_attribute(j->first, j->second);
+
+ return alt_name;
+ }
+
+}
diff --git a/src/cert/x509/x509cert.h b/src/cert/x509/x509cert.h
new file mode 100644
index 000000000..c8dc2c435
--- /dev/null
+++ b/src/cert/x509/x509cert.h
@@ -0,0 +1,72 @@
+/*************************************************
+* X.509 Certificates Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_CERTS_H__
+#define BOTAN_X509_CERTS_H__
+
+#include <botan/x509_obj.h>
+#include <botan/x509_key.h>
+#include <botan/datastor.h>
+#include <botan/enums.h>
+#include <map>
+
+namespace Botan {
+
+/*************************************************
+* X.509 Certificate *
+*************************************************/
+class BOTAN_DLL X509_Certificate : public X509_Object
+ {
+ public:
+ Public_Key* subject_public_key() const;
+
+ X509_DN issuer_dn() const;
+ X509_DN subject_dn() const;
+ std::vector<std::string> subject_info(const std::string&) const;
+ std::vector<std::string> issuer_info(const std::string&) const;
+
+ std::string start_time() const;
+ std::string end_time() const;
+
+ u32bit x509_version() const;
+ MemoryVector<byte> serial_number() const;
+
+ MemoryVector<byte> authority_key_id() const;
+ MemoryVector<byte> subject_key_id() const;
+ bool is_self_signed() const { return self_signed; }
+ bool is_CA_cert() const;
+
+ u32bit path_limit() const;
+ Key_Constraints constraints() const;
+ std::vector<std::string> ex_constraints() const;
+ std::vector<std::string> policies() const;
+
+ bool operator==(const X509_Certificate&) const;
+
+ X509_Certificate(DataSource&);
+ X509_Certificate(const std::string&);
+ private:
+ void force_decode();
+ friend class X509_CA;
+ X509_Certificate() {}
+
+ Data_Store subject, issuer;
+ bool self_signed;
+ };
+
+/*************************************************
+* X.509 Certificate Comparison *
+*************************************************/
+BOTAN_DLL bool operator!=(const X509_Certificate&, const X509_Certificate&);
+
+/*************************************************
+* Data Store Extraction Operations *
+*************************************************/
+BOTAN_DLL X509_DN create_dn(const Data_Store&);
+BOTAN_DLL AlternativeName create_alt_name(const Data_Store&);
+
+}
+
+#endif
diff --git a/src/cert/x509/x509find.cpp b/src/cert/x509/x509find.cpp
new file mode 100644
index 000000000..83d02449d
--- /dev/null
+++ b/src/cert/x509/x509find.cpp
@@ -0,0 +1,109 @@
+/*************************************************
+* X.509 Certificate Store Searching Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509find.h>
+#include <botan/charset.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Compare based on case-insensive substrings *
+*************************************************/
+bool substring_match(const std::string& searching_for,
+ const std::string& found)
+ {
+ if(std::search(found.begin(), found.end(), searching_for.begin(),
+ searching_for.end(), Charset::caseless_cmp) != found.end())
+ return true;
+ return false;
+ }
+
+/*************************************************
+* Compare based on case-insensive match *
+*************************************************/
+bool ignore_case(const std::string& searching_for, const std::string& found)
+ {
+ if(searching_for.size() != found.size())
+ return false;
+
+ return std::equal(found.begin(), found.end(),
+ searching_for.begin(), Charset::caseless_cmp);
+ }
+
+}
+
+/*************************************************
+* Search based on the contents of a DN entry *
+*************************************************/
+bool DN_Check::match(const X509_Certificate& cert) const
+ {
+ std::vector<std::string> info = cert.subject_info(dn_entry);
+
+ for(u32bit j = 0; j != info.size(); ++j)
+ if(compare(info[j], looking_for))
+ return true;
+ return false;
+ }
+
+/*************************************************
+* DN_Check Constructor *
+*************************************************/
+DN_Check::DN_Check(const std::string& dn_entry, const std::string& looking_for,
+ compare_fn func)
+ {
+ this->dn_entry = dn_entry;
+ this->looking_for = looking_for;
+ compare = func;
+ }
+
+/*************************************************
+* DN_Check Constructor *
+*************************************************/
+DN_Check::DN_Check(const std::string& dn_entry, const std::string& looking_for,
+ Search_Type method)
+ {
+ this->dn_entry = dn_entry;
+ this->looking_for = looking_for;
+
+ if(method == SUBSTRING_MATCHING)
+ compare = &substring_match;
+ else if(method == IGNORE_CASE)
+ compare = &ignore_case;
+ else
+ throw Invalid_Argument("Unknown method argument to DN_Check()");
+ }
+
+/*************************************************
+* Match by issuer and serial number *
+*************************************************/
+bool IandS_Match::match(const X509_Certificate& cert) const
+ {
+ if(cert.serial_number() != serial)
+ return false;
+ return (cert.issuer_dn() == issuer);
+ }
+
+/*************************************************
+* IandS_Match Constructor *
+*************************************************/
+IandS_Match::IandS_Match(const X509_DN& issuer,
+ const MemoryRegion<byte>& serial)
+ {
+ this->issuer = issuer;
+ this->serial = serial;
+ }
+
+/*************************************************
+* Match by subject key identifier *
+*************************************************/
+bool SKID_Match::match(const X509_Certificate& cert) const
+ {
+ return (cert.subject_key_id() == skid);
+ }
+
+}
diff --git a/src/cert/x509/x509find.h b/src/cert/x509/x509find.h
new file mode 100644
index 000000000..65781199f
--- /dev/null
+++ b/src/cert/x509/x509find.h
@@ -0,0 +1,58 @@
+/*************************************************
+* X.509 Certificate Store Searching Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_CERT_STORE_SEARCH_H__
+#define BOTAN_X509_CERT_STORE_SEARCH_H__
+
+#include <botan/x509stor.h>
+
+namespace Botan {
+
+/*************************************************
+* Search based on the contents of a DN entry *
+*************************************************/
+class BOTAN_DLL DN_Check : public X509_Store::Search_Func
+ {
+ public:
+ typedef bool (*compare_fn)(const std::string&, const std::string&);
+ enum Search_Type { SUBSTRING_MATCHING, IGNORE_CASE };
+
+ bool match(const X509_Certificate& cert) const;
+
+ DN_Check(const std::string&, const std::string&, compare_fn);
+ DN_Check(const std::string&, const std::string&, Search_Type);
+ private:
+ std::string dn_entry, looking_for;
+ compare_fn compare;
+ };
+
+/*************************************************
+* Search for a certificate by issuer/serial *
+*************************************************/
+class BOTAN_DLL IandS_Match : public X509_Store::Search_Func
+ {
+ public:
+ bool match(const X509_Certificate& cert) const;
+ IandS_Match(const X509_DN&, const MemoryRegion<byte>&);
+ private:
+ X509_DN issuer;
+ MemoryVector<byte> serial;
+ };
+
+/*************************************************
+* Search for a certificate by subject keyid *
+*************************************************/
+class BOTAN_DLL SKID_Match : public X509_Store::Search_Func
+ {
+ public:
+ bool match(const X509_Certificate& cert) const;
+ SKID_Match(const MemoryRegion<byte>& s) : skid(s) {}
+ private:
+ MemoryVector<byte> skid;
+ };
+
+}
+
+#endif
diff --git a/src/cert/x509/x509opt.cpp b/src/cert/x509/x509opt.cpp
new file mode 100644
index 000000000..716884ed5
--- /dev/null
+++ b/src/cert/x509/x509opt.cpp
@@ -0,0 +1,106 @@
+/*************************************************
+* X.509 Certificate Options Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509self.h>
+#include <botan/util.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <ctime>
+
+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);
+ }
+
+/*************************************************
+* Set when the certificate should expire *
+*************************************************/
+void X509_Cert_Options::not_after(const std::string& time_string)
+ {
+ end = X509_Time(time_string);
+ }
+
+/*************************************************
+* 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(u32bit limit)
+ {
+ is_CA = true;
+ path_limit = limit;
+ }
+
+/*************************************************
+* Do basic sanity checks *
+*************************************************/
+void X509_Cert_Options::sanity_check() const
+ {
+ if(common_name == "" || country == "")
+ throw Encoding_Error("X.509 certificate: name and country MUST be set");
+ if(country.size() != 2)
+ throw Encoding_Error("Invalid ISO country code: " + country);
+ if(start >= end)
+ throw Encoding_Error("X509_Cert_Options: invalid time constraints");
+ }
+
+/*************************************************
+* Initialize the certificate options *
+*************************************************/
+X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
+ u32bit expiration_time_in_seconds)
+ {
+ is_CA = false;
+ path_limit = 0;
+ constraints = NO_CONSTRAINTS;
+
+ const u32bit now = system_time();
+
+ start = X509_Time(now);
+ end = X509_Time(now + expiration_time_in_seconds);
+
+ if(initial_opts == "")
+ 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/cert/x509/x509self.cpp b/src/cert/x509/x509self.cpp
new file mode 100644
index 000000000..6c3baae9b
--- /dev/null
+++ b/src/cert/x509/x509self.cpp
@@ -0,0 +1,174 @@
+/*************************************************
+* PKCS #10/Self Signed Cert Creation Source File *
+* (C) 1999-2008 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509self.h>
+#include <botan/x509_ext.h>
+#include <botan/x509_ca.h>
+#include <botan/der_enc.h>
+#include <botan/look_pk.h>
+#include <botan/oids.h>
+#include <botan/pipe.h>
+#include <memory>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Shared setup for self-signed items *
+*************************************************/
+MemoryVector<byte> shared_setup(const X509_Cert_Options& opts,
+ const Private_Key& key)
+ {
+ const Private_Key* key_pointer = &key;
+ if(!dynamic_cast<const PK_Signing_Key*>(key_pointer))
+ throw Invalid_Argument("Key type " + key.algo_name() + " cannot sign");
+
+ opts.sanity_check();
+
+ Pipe key_encoder;
+ key_encoder.start_msg();
+ X509::encode(key, key_encoder, RAW_BER);
+ key_encoder.end_msg();
+
+ return key_encoder.read_all();
+ }
+
+/*************************************************
+* 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,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ MemoryVector<byte> pub_key = shared_setup(opts, key);
+ std::auto_ptr<PK_Signer> signer(choose_sig_format(key, 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
+ constraints = find_constraints(key, opts.constraints);
+
+ Extensions extensions;
+
+ extensions.add(new Cert_Extension::Subject_Key_ID(pub_key));
+ 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));
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+
+ 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,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ MemoryVector<byte> pub_key = shared_setup(opts, key);
+ std::auto_ptr<PK_Signer> signer(choose_sig_format(key, sig_algo));
+ load_info(opts, subject_dn, subject_alt);
+
+ const u32bit PKCS10_VERSION = 0;
+
+ Extensions extensions;
+
+ extensions.add(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+ extensions.add(
+ new Cert_Extension::Key_Usage(
+ opts.is_CA ? Key_Constraints(KEY_CERT_SIGN | CRL_SIGN) :
+ find_constraints(key, opts.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 != "")
+ {
+ ASN1_String challenge(opts.challenge, DIRECTORY_STRING);
+
+ tbs_req.encode(
+ Attribute("PKCS9.ChallengePassword",
+ DER_Encoder().encode(challenge).get_contents()
+ )
+ );
+ }
+
+ tbs_req.encode(
+ Attribute("PKCS9.ExtensionRequest",
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .get_contents()
+ )
+ )
+ .end_explicit()
+ .end_cons();
+
+ DataSource_Memory source(
+ X509_Object::make_signed(signer.get(),
+ rng,
+ sig_algo,
+ tbs_req.get_contents())
+ );
+
+ return PKCS10_Request(source);
+ }
+
+}
+
+}
diff --git a/src/cert/x509/x509self.h b/src/cert/x509/x509self.h
new file mode 100644
index 000000000..2f83a12be
--- /dev/null
+++ b/src/cert/x509/x509self.h
@@ -0,0 +1,75 @@
+/*************************************************
+* X.509 Self-Signed Certificate Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_SELF_H__
+#define BOTAN_X509_SELF_H__
+
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/pkcs10.h>
+
+namespace Botan {
+
+/*************************************************
+* Options for X.509 Certificates *
+*************************************************/
+class BOTAN_DLL X509_Cert_Options
+ {
+ public:
+ std::string common_name;
+ std::string country;
+ std::string organization;
+ std::string org_unit;
+ std::string locality;
+ std::string state;
+ std::string serial_number;
+
+ std::string email, uri, dns, ip, xmpp;
+
+ std::string challenge;
+
+ X509_Time start, end;
+
+ bool is_CA;
+ u32bit path_limit;
+ Key_Constraints constraints;
+ std::vector<OID> ex_constraints;
+
+ void sanity_check() const;
+
+ void CA_key(u32bit = 8);
+ void not_before(const std::string&);
+ void not_after(const std::string&);
+
+ void add_constraints(Key_Constraints);
+ void add_ex_constraint(const OID&);
+ void add_ex_constraint(const std::string&);
+
+ X509_Cert_Options(const std::string& = "",
+ u32bit expire = 365 * 24 * 60 * 60);
+ };
+
+namespace X509 {
+
+/*************************************************
+* Create a self-signed X.509 certificate *
+*************************************************/
+BOTAN_DLL X509_Certificate
+create_self_signed_cert(const X509_Cert_Options&,
+ const Private_Key&,
+ RandomNumberGenerator& rng);
+
+/*************************************************
+* Create a PKCS #10 certificate request *
+*************************************************/
+BOTAN_DLL PKCS10_Request create_cert_req(const X509_Cert_Options&,
+ const Private_Key&,
+ RandomNumberGenerator& rng);
+
+}
+
+}
+
+#endif
diff --git a/src/cert/x509/x509stor.cpp b/src/cert/x509/x509stor.cpp
new file mode 100644
index 000000000..f050b33b1
--- /dev/null
+++ b/src/cert/x509/x509stor.cpp
@@ -0,0 +1,693 @@
+/*************************************************
+* X.509 Certificate Store Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/x509stor.h>
+#include <botan/parsing.h>
+#include <botan/pubkey.h>
+#include <botan/look_pk.h>
+#include <botan/oids.h>
+#include <botan/util.h>
+#include <algorithm>
+#include <memory>
+
+namespace Botan {
+
+namespace {
+
+/*************************************************
+* Do a validity check *
+*************************************************/
+s32bit validity_check(const X509_Time& start, const X509_Time& end,
+ u64bit current_time, u32bit slack)
+ {
+ const s32bit NOT_YET_VALID = -1, VALID_TIME = 0, EXPIRED = 1;
+
+ if(start.cmp(current_time + slack) > 0)
+ return NOT_YET_VALID;
+ if(end.cmp(current_time - slack) < 0)
+ return EXPIRED;
+ return VALID_TIME;
+ }
+
+/*************************************************
+* Compare the value of unique ID fields *
+*************************************************/
+bool compare_ids(const MemoryVector<byte>& id1,
+ const MemoryVector<byte>& id2)
+ {
+ if(!id1.size() || !id2.size())
+ return true;
+ return (id1 == id2);
+ }
+
+/*************************************************
+* Check a particular usage restriction *
+*************************************************/
+bool check_usage(const X509_Certificate& cert, X509_Store::Cert_Usage usage,
+ X509_Store::Cert_Usage check_for, Key_Constraints constraints)
+ {
+ if((usage & check_for) == 0)
+ return true;
+ if(cert.constraints() == NO_CONSTRAINTS)
+ return true;
+ if(cert.constraints() & constraints)
+ return true;
+ return false;
+ }
+
+/*************************************************
+* Check a particular usage restriction *
+*************************************************/
+bool check_usage(const X509_Certificate& cert, X509_Store::Cert_Usage usage,
+ X509_Store::Cert_Usage check_for,
+ const std::string& usage_oid)
+ {
+ if((usage & check_for) == 0)
+ return true;
+
+ const std::vector<std::string> constraints = cert.ex_constraints();
+
+ if(constraints.empty())
+ return true;
+
+ return std::binary_search(constraints.begin(), constraints.end(),
+ usage_oid);
+ }
+
+/*************************************************
+* Check the usage restrictions *
+*************************************************/
+X509_Code usage_check(const X509_Certificate& cert,
+ X509_Store::Cert_Usage usage)
+ {
+ if(usage == X509_Store::ANY)
+ return VERIFIED;
+
+ if(!check_usage(cert, usage, X509_Store::CRL_SIGNING, CRL_SIGN))
+ return CA_CERT_NOT_FOR_CRL_ISSUER;
+
+ if(!check_usage(cert, usage, X509_Store::TLS_SERVER, "PKIX.ServerAuth"))
+ return INVALID_USAGE;
+ if(!check_usage(cert, usage, X509_Store::TLS_CLIENT, "PKIX.ClientAuth"))
+ return INVALID_USAGE;
+ if(!check_usage(cert, usage, X509_Store::CODE_SIGNING, "PKIX.CodeSigning"))
+ return INVALID_USAGE;
+ if(!check_usage(cert, usage, X509_Store::EMAIL_PROTECTION,
+ "PKIX.EmailProtection"))
+ return INVALID_USAGE;
+ if(!check_usage(cert, usage, X509_Store::TIME_STAMPING,
+ "PKIX.TimeStamping"))
+ return INVALID_USAGE;
+
+ return VERIFIED;
+ }
+
+}
+
+/*************************************************
+* Define equality for revocation data *
+*************************************************/
+bool X509_Store::CRL_Data::operator==(const CRL_Data& other) const
+ {
+ if(issuer != other.issuer)
+ return false;
+ if(serial != other.serial)
+ return false;
+ return compare_ids(auth_key_id, other.auth_key_id);
+ }
+
+/*************************************************
+* Define inequality for revocation data *
+*************************************************/
+bool X509_Store::CRL_Data::operator!=(const CRL_Data& other) const
+ {
+ return !((*this) == other);
+ }
+
+/*************************************************
+* Define an ordering for revocation data *
+*************************************************/
+bool X509_Store::CRL_Data::operator<(const X509_Store::CRL_Data& other) const
+ {
+ if(*this == other)
+ return false;
+
+ const MemoryVector<byte>& serial1 = serial;
+ const MemoryVector<byte>& key_id1 = auth_key_id;
+ const MemoryVector<byte>& serial2 = other.serial;
+ const MemoryVector<byte>& key_id2 = other.auth_key_id;
+
+ if(compare_ids(key_id1, key_id2) == false)
+ {
+ if(std::lexicographical_compare(key_id1.begin(), key_id1.end(),
+ key_id2.begin(), key_id2.end()))
+ return true;
+
+ if(std::lexicographical_compare(key_id2.begin(), key_id2.end(),
+ key_id1.begin(), key_id1.end()))
+ return false;
+ }
+
+ if(compare_ids(serial1, serial2) == false)
+ {
+ if(std::lexicographical_compare(serial1.begin(), serial1.end(),
+ serial2.begin(), serial2.end()))
+ return true;
+
+ if(std::lexicographical_compare(serial2.begin(), serial2.end(),
+ serial1.begin(), serial1.end()))
+ return false;
+ }
+
+ return (issuer < other.issuer);
+ }
+
+/*************************************************
+* X509_Store Constructor *
+*************************************************/
+X509_Store::X509_Store(u32bit slack, u32bit cache_timeout)
+ {
+ revoked_info_valid = true;
+
+ validation_cache_timeout = cache_timeout;
+ time_slack = slack;
+ }
+
+/*************************************************
+* X509_Store Copy Constructor *
+*************************************************/
+X509_Store::X509_Store(const X509_Store& other)
+ {
+ certs = other.certs;
+ revoked = other.revoked;
+ revoked_info_valid = other.revoked_info_valid;
+ for(u32bit j = 0; j != other.stores.size(); ++j)
+ stores[j] = other.stores[j]->clone();
+ time_slack = other.time_slack;
+ }
+
+/*************************************************
+* X509_Store Destructor *
+*************************************************/
+X509_Store::~X509_Store()
+ {
+ for(u32bit j = 0; j != stores.size(); ++j)
+ delete stores[j];
+ }
+
+/*************************************************
+* Verify a certificate's authenticity *
+*************************************************/
+X509_Code X509_Store::validate_cert(const X509_Certificate& cert,
+ Cert_Usage cert_usage)
+ {
+ recompute_revoked_info();
+
+ std::vector<u32bit> indexes;
+ X509_Code chaining_result = construct_cert_chain(cert, indexes);
+ if(chaining_result != VERIFIED)
+ return chaining_result;
+
+ const u64bit current_time = system_time();
+
+ s32bit time_check = validity_check(cert.start_time(), cert.end_time(),
+ current_time, time_slack);
+ if(time_check < 0) return CERT_NOT_YET_VALID;
+ else if(time_check > 0) return CERT_HAS_EXPIRED;
+
+ X509_Code sig_check_result = check_sig(cert, certs[indexes[0]]);
+ if(sig_check_result != VERIFIED)
+ return sig_check_result;
+
+ if(is_revoked(cert))
+ return CERT_IS_REVOKED;
+
+ for(u32bit j = 0; j != indexes.size() - 1; ++j)
+ {
+ const X509_Certificate& current_cert = certs[indexes[j]].cert;
+
+ time_check = validity_check(current_cert.start_time(),
+ current_cert.end_time(),
+ current_time,
+ time_slack);
+
+ if(time_check < 0) return CERT_NOT_YET_VALID;
+ else if(time_check > 0) return CERT_HAS_EXPIRED;
+
+ sig_check_result = check_sig(certs[indexes[j]], certs[indexes[j+1]]);
+ if(sig_check_result != VERIFIED)
+ return sig_check_result;
+ }
+
+ return usage_check(cert, cert_usage);
+ }
+
+/*************************************************
+* Find this certificate *
+*************************************************/
+u32bit X509_Store::find_cert(const X509_DN& subject_dn,
+ const MemoryRegion<byte>& subject_key_id) const
+ {
+ for(u32bit j = 0; j != certs.size(); ++j)
+ {
+ const X509_Certificate& this_cert = certs[j].cert;
+ if(compare_ids(this_cert.subject_key_id(), subject_key_id) &&
+ this_cert.subject_dn() == subject_dn)
+ return j;
+ }
+ return NO_CERT_FOUND;
+ }
+
+/*************************************************
+* Find the parent of this certificate *
+*************************************************/
+u32bit X509_Store::find_parent_of(const X509_Certificate& cert)
+ {
+ const X509_DN issuer_dn = cert.issuer_dn();
+ const MemoryVector<byte> auth_key_id = cert.authority_key_id();
+
+ u32bit index = find_cert(issuer_dn, auth_key_id);
+
+ if(index != NO_CERT_FOUND)
+ return index;
+
+ if(auth_key_id.size())
+ {
+ for(u32bit j = 0; j != stores.size(); ++j)
+ {
+ std::vector<X509_Certificate> got = stores[j]->by_SKID(auth_key_id);
+
+ if(got.empty())
+ continue;
+
+ for(u32bit k = 0; k != got.size(); ++k)
+ add_cert(got[k]);
+ return find_cert(issuer_dn, auth_key_id);
+ }
+ }
+
+ return NO_CERT_FOUND;
+ }
+
+/*************************************************
+* Construct a chain of certificate relationships *
+*************************************************/
+X509_Code X509_Store::construct_cert_chain(const X509_Certificate& end_cert,
+ std::vector<u32bit>& indexes,
+ bool need_full_chain)
+ {
+ u32bit parent = find_parent_of(end_cert);
+
+ while(true)
+ {
+ if(parent == NO_CERT_FOUND)
+ return CERT_ISSUER_NOT_FOUND;
+ indexes.push_back(parent);
+
+ if(certs[parent].is_verified(validation_cache_timeout))
+ if(certs[parent].verify_result() != VERIFIED)
+ return certs[parent].verify_result();
+
+ const X509_Certificate& parent_cert = certs[parent].cert;
+ if(!parent_cert.is_CA_cert())
+ return CA_CERT_NOT_FOR_CERT_ISSUER;
+
+ if(certs[parent].is_trusted())
+ break;
+ if(parent_cert.is_self_signed())
+ return CANNOT_ESTABLISH_TRUST;
+
+ if(parent_cert.path_limit() < indexes.size() - 1)
+ return CERT_CHAIN_TOO_LONG;
+
+ parent = find_parent_of(parent_cert);
+ }
+
+ if(need_full_chain)
+ return VERIFIED;
+
+ while(true)
+ {
+ if(indexes.size() < 2)
+ break;
+
+ const u32bit cert = indexes.back();
+
+ if(certs[cert].is_verified(validation_cache_timeout))
+ {
+ if(certs[cert].verify_result() != VERIFIED)
+ throw Internal_Error("X509_Store::construct_cert_chain");
+ indexes.pop_back();
+ }
+ else
+ break;
+ }
+
+ const u32bit last_cert = indexes.back();
+ const u32bit parent_of_last_cert = find_parent_of(certs[last_cert].cert);
+ if(parent_of_last_cert == NO_CERT_FOUND)
+ return CERT_ISSUER_NOT_FOUND;
+ indexes.push_back(parent_of_last_cert);
+
+ return VERIFIED;
+ }
+
+/*************************************************
+* Check the CAs signature on a certificate *
+*************************************************/
+X509_Code X509_Store::check_sig(const Cert_Info& cert_info,
+ const Cert_Info& ca_cert_info) const
+ {
+ if(cert_info.is_verified(validation_cache_timeout))
+ return cert_info.verify_result();
+
+ const X509_Certificate& cert = cert_info.cert;
+ const X509_Certificate& ca_cert = ca_cert_info.cert;
+
+ X509_Code verify_code = check_sig(cert, ca_cert.subject_public_key());
+
+ cert_info.set_result(verify_code);
+
+ return verify_code;
+ }
+
+/*************************************************
+* Check a CA's signature *
+*************************************************/
+X509_Code X509_Store::check_sig(const X509_Object& object, Public_Key* key)
+ {
+ std::auto_ptr<Public_Key> pub_key(key);
+ std::auto_ptr<PK_Verifier> verifier;
+
+ try {
+ std::vector<std::string> sig_info =
+ split_on(OIDS::lookup(object.signature_algorithm().oid), '/');
+
+ if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+ return SIGNATURE_ERROR;
+
+ std::string padding = sig_info[1];
+ Signature_Format format;
+ if(key->message_parts() >= 2) format = DER_SEQUENCE;
+ else format = IEEE_1363;
+
+ if(dynamic_cast<PK_Verifying_with_MR_Key*>(pub_key.get()))
+ {
+ PK_Verifying_with_MR_Key* sig_key =
+ dynamic_cast<PK_Verifying_with_MR_Key*>(pub_key.get());
+ verifier.reset(get_pk_verifier(*sig_key, padding, format));
+ }
+ else if(dynamic_cast<PK_Verifying_wo_MR_Key*>(pub_key.get()))
+ {
+ PK_Verifying_wo_MR_Key* sig_key =
+ dynamic_cast<PK_Verifying_wo_MR_Key*>(pub_key.get());
+ verifier.reset(get_pk_verifier(*sig_key, padding, format));
+ }
+ else
+ return CA_CERT_CANNOT_SIGN;
+
+ bool valid = verifier->verify_message(object.tbs_data(),
+ object.signature());
+
+ if(valid)
+ return VERIFIED;
+ else
+ return SIGNATURE_ERROR;
+ }
+ catch(Decoding_Error) { return CERT_FORMAT_ERROR; }
+ catch(Exception) {}
+
+ return UNKNOWN_X509_ERROR;
+ }
+
+/*************************************************
+* Recompute the revocation status of the certs *
+*************************************************/
+void X509_Store::recompute_revoked_info() const
+ {
+ if(revoked_info_valid)
+ return;
+
+ for(u32bit j = 0; j != certs.size(); ++j)
+ {
+ if((certs[j].is_verified(validation_cache_timeout)) &&
+ (certs[j].verify_result() != VERIFIED))
+ continue;
+
+ if(is_revoked(certs[j].cert))
+ certs[j].set_result(CERT_IS_REVOKED);
+ }
+
+ revoked_info_valid = true;
+ }
+
+/*************************************************
+* Check if a certificate is revoked *
+*************************************************/
+bool X509_Store::is_revoked(const X509_Certificate& cert) const
+ {
+ CRL_Data revoked_info;
+ revoked_info.issuer = cert.issuer_dn();
+ revoked_info.serial = cert.serial_number();
+ revoked_info.auth_key_id = cert.authority_key_id();
+
+ if(std::binary_search(revoked.begin(), revoked.end(), revoked_info))
+ return true;
+ return false;
+ }
+
+/*************************************************
+* Retrieve all the certificates in the store *
+*************************************************/
+std::vector<X509_Certificate>
+X509_Store::get_certs(const Search_Func& search) const
+ {
+ std::vector<X509_Certificate> found_certs;
+ for(u32bit j = 0; j != certs.size(); ++j)
+ {
+ if(search.match(certs[j].cert))
+ found_certs.push_back(certs[j].cert);
+ }
+ return found_certs;
+ }
+
+/*************************************************
+* Construct a path back to a root for this cert *
+*************************************************/
+std::vector<X509_Certificate>
+X509_Store::get_cert_chain(const X509_Certificate& cert)
+ {
+ std::vector<X509_Certificate> result;
+ std::vector<u32bit> indexes;
+ X509_Code chaining_result = construct_cert_chain(cert, indexes, true);
+
+ if(chaining_result != VERIFIED)
+ throw Invalid_State("X509_Store::get_cert_chain: Can't construct chain");
+
+ for(u32bit j = 0; j != indexes.size(); ++j)
+ result.push_back(certs[indexes[j]].cert);
+ return result;
+ }
+
+/*************************************************
+* Add a certificate store to the list of stores *
+*************************************************/
+void X509_Store::add_new_certstore(Certificate_Store* certstore)
+ {
+ stores.push_back(certstore);
+ }
+
+/*************************************************
+* Add a certificate to the store *
+*************************************************/
+void X509_Store::add_cert(const X509_Certificate& cert, bool trusted)
+ {
+ if(trusted && !cert.is_self_signed())
+ throw Invalid_Argument("X509_Store: Trusted certs must be self-signed");
+
+ if(find_cert(cert.subject_dn(), cert.subject_key_id()) == NO_CERT_FOUND)
+ {
+ revoked_info_valid = false;
+ Cert_Info info(cert, trusted);
+ certs.push_back(info);
+ }
+ else if(trusted)
+ {
+ for(u32bit j = 0; j != certs.size(); ++j)
+ {
+ const X509_Certificate& this_cert = certs[j].cert;
+ if(this_cert == cert)
+ certs[j].trusted = trusted;
+ }
+ }
+ }
+
+/*************************************************
+* Add one or more certificates to the store *
+*************************************************/
+void X509_Store::do_add_certs(DataSource& source, bool trusted)
+ {
+ while(!source.end_of_data())
+ {
+ try {
+ X509_Certificate cert(source);
+ add_cert(cert, trusted);
+ }
+ catch(Decoding_Error) {}
+ catch(Invalid_Argument) {}
+ }
+ }
+
+/*************************************************
+* Add one or more certificates to the store *
+*************************************************/
+void X509_Store::add_certs(DataSource& source)
+ {
+ do_add_certs(source, false);
+ }
+
+/*************************************************
+* Add one or more certificates to the store *
+*************************************************/
+void X509_Store::add_trusted_certs(DataSource& source)
+ {
+ do_add_certs(source, true);
+ }
+
+/*************************************************
+* Add one or more certificates to the store *
+*************************************************/
+X509_Code X509_Store::add_crl(const X509_CRL& crl)
+ {
+ s32bit time_check = validity_check(crl.this_update(), crl.next_update(),
+ system_time(), time_slack);
+
+ if(time_check < 0) return CRL_NOT_YET_VALID;
+ else if(time_check > 0) return CRL_HAS_EXPIRED;
+
+ u32bit cert_index = NO_CERT_FOUND;
+
+ for(u32bit j = 0; j != certs.size(); ++j)
+ {
+ const X509_Certificate& this_cert = certs[j].cert;
+ if(compare_ids(this_cert.subject_key_id(), crl.authority_key_id()))
+ {
+ if(this_cert.subject_dn() == crl.issuer_dn())
+ cert_index = j;
+ }
+ }
+
+ if(cert_index == NO_CERT_FOUND)
+ return CRL_ISSUER_NOT_FOUND;
+
+ const X509_Certificate& ca_cert = certs[cert_index].cert;
+
+ X509_Code verify_result = validate_cert(ca_cert, CRL_SIGNING);
+ if(verify_result != VERIFIED)
+ return verify_result;
+
+ verify_result = check_sig(crl, ca_cert.subject_public_key());
+ if(verify_result != VERIFIED)
+ return verify_result;
+
+ std::vector<CRL_Entry> revoked_certs = crl.get_revoked();
+
+ for(u32bit j = 0; j != revoked_certs.size(); ++j)
+ {
+ CRL_Data revoked_info;
+ revoked_info.issuer = crl.issuer_dn();
+ revoked_info.serial = revoked_certs[j].serial_number();
+ revoked_info.auth_key_id = crl.authority_key_id();
+
+ std::vector<CRL_Data>::iterator p =
+ std::find(revoked.begin(), revoked.end(), revoked_info);
+
+ if(revoked_certs[j].reason_code() == REMOVE_FROM_CRL)
+ {
+ if(p == revoked.end()) continue;
+ revoked.erase(p);
+ }
+ else
+ {
+ if(p != revoked.end()) continue;
+ revoked.push_back(revoked_info);
+ }
+ }
+
+ std::sort(revoked.begin(), revoked.end());
+ revoked_info_valid = false;
+
+ return VERIFIED;
+ }
+
+/*************************************************
+* PEM encode the set of certificates *
+*************************************************/
+std::string X509_Store::PEM_encode() const
+ {
+ std::string cert_store;
+ for(u32bit j = 0; j != certs.size(); ++j)
+ cert_store += certs[j].cert.PEM_encode();
+ return cert_store;
+ }
+
+/*************************************************
+* Create a Cert_Info structure *
+*************************************************/
+X509_Store::Cert_Info::Cert_Info(const X509_Certificate& c,
+ bool t) : cert(c), trusted(t)
+ {
+ checked = false;
+ result = UNKNOWN_X509_ERROR;
+ last_checked = 0;
+ }
+
+/*************************************************
+* Return the verification results *
+*************************************************/
+X509_Code X509_Store::Cert_Info::verify_result() const
+ {
+ if(!checked)
+ throw Invalid_State("Cert_Info::verify_result() called; not checked");
+ return result;
+ }
+
+/*************************************************
+* Set the verification results *
+*************************************************/
+void X509_Store::Cert_Info::set_result(X509_Code code) const
+ {
+ result = code;
+ last_checked = system_time();
+ checked = true;
+ }
+
+/*************************************************
+* Check if this certificate can be trusted *
+*************************************************/
+bool X509_Store::Cert_Info::is_trusted() const
+ {
+ return trusted;
+ }
+
+/*************************************************
+* Check if this certificate has been verified *
+*************************************************/
+bool X509_Store::Cert_Info::is_verified(u32bit timeout) const
+ {
+ if(!checked)
+ return false;
+ if(result != VERIFIED && result != CERT_NOT_YET_VALID)
+ return true;
+
+ const u64bit current_time = system_time();
+
+ if(current_time > last_checked + timeout)
+ checked = false;
+
+ return checked;
+ }
+
+}
diff --git a/src/cert/x509/x509stor.h b/src/cert/x509/x509stor.h
new file mode 100644
index 000000000..3f3f38ea0
--- /dev/null
+++ b/src/cert/x509/x509stor.h
@@ -0,0 +1,137 @@
+/*************************************************
+* X.509 Certificate Store Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_X509_CERT_STORE_H__
+#define BOTAN_X509_CERT_STORE_H__
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/certstor.h>
+
+namespace Botan {
+
+/*************************************************
+* X.509 Certificate Validation Result *
+*************************************************/
+enum X509_Code {
+ VERIFIED,
+ UNKNOWN_X509_ERROR,
+ CANNOT_ESTABLISH_TRUST,
+ CERT_CHAIN_TOO_LONG,
+ SIGNATURE_ERROR,
+ POLICY_ERROR,
+ INVALID_USAGE,
+
+ CERT_FORMAT_ERROR,
+ CERT_ISSUER_NOT_FOUND,
+ CERT_NOT_YET_VALID,
+ CERT_HAS_EXPIRED,
+ CERT_IS_REVOKED,
+
+ CRL_FORMAT_ERROR,
+ CRL_ISSUER_NOT_FOUND,
+ CRL_NOT_YET_VALID,
+ CRL_HAS_EXPIRED,
+
+ CA_CERT_CANNOT_SIGN,
+ CA_CERT_NOT_FOR_CERT_ISSUER,
+ CA_CERT_NOT_FOR_CRL_ISSUER
+};
+
+/*************************************************
+* X.509 Certificate Store *
+*************************************************/
+class BOTAN_DLL X509_Store
+ {
+ public:
+ class BOTAN_DLL Search_Func
+ {
+ public:
+ virtual bool match(const X509_Certificate&) const = 0;
+ virtual ~Search_Func() {}
+ };
+
+ enum Cert_Usage {
+ ANY = 0x00,
+ TLS_SERVER = 0x01,
+ TLS_CLIENT = 0x02,
+ CODE_SIGNING = 0x04,
+ EMAIL_PROTECTION = 0x08,
+ TIME_STAMPING = 0x10,
+ CRL_SIGNING = 0x20
+ };
+
+ X509_Code validate_cert(const X509_Certificate&, Cert_Usage = ANY);
+
+ std::vector<X509_Certificate> get_certs(const Search_Func&) const;
+ std::vector<X509_Certificate> get_cert_chain(const X509_Certificate&);
+ std::string PEM_encode() const;
+
+ X509_Code add_crl(const X509_CRL&);
+ void add_cert(const X509_Certificate&, bool = false);
+ void add_certs(DataSource&);
+ void add_trusted_certs(DataSource&);
+
+ void add_new_certstore(Certificate_Store*);
+
+ static X509_Code check_sig(const X509_Object&, Public_Key*);
+
+ X509_Store(u32bit time_slack = 24*60*60,
+ u32bit cache_results = 30*60);
+
+ X509_Store(const X509_Store&);
+ ~X509_Store();
+ private:
+ X509_Store& operator=(const X509_Store&) { return (*this); }
+
+ class BOTAN_DLL Cert_Info
+ {
+ public:
+ bool is_verified(u32bit timeout) const;
+ bool is_trusted() const;
+ X509_Code verify_result() const;
+ void set_result(X509_Code) const;
+ Cert_Info(const X509_Certificate&, bool = false);
+
+ X509_Certificate cert;
+ bool trusted;
+ private:
+ mutable bool checked;
+ mutable X509_Code result;
+ mutable u64bit last_checked;
+ };
+
+ class BOTAN_DLL CRL_Data
+ {
+ public:
+ X509_DN issuer;
+ MemoryVector<byte> serial, auth_key_id;
+ bool operator==(const CRL_Data&) const;
+ bool operator!=(const CRL_Data&) const;
+ bool operator<(const CRL_Data&) const;
+ };
+
+ u32bit find_cert(const X509_DN&, const MemoryRegion<byte>&) const;
+ X509_Code check_sig(const Cert_Info&, const Cert_Info&) const;
+ void recompute_revoked_info() const;
+
+ void do_add_certs(DataSource&, bool);
+ X509_Code construct_cert_chain(const X509_Certificate&,
+ std::vector<u32bit>&, bool = false);
+
+ u32bit find_parent_of(const X509_Certificate&);
+ bool is_revoked(const X509_Certificate&) const;
+
+ static const u32bit NO_CERT_FOUND = 0xFFFFFFFF;
+ std::vector<Cert_Info> certs;
+ std::vector<CRL_Data> revoked;
+ std::vector<Certificate_Store*> stores;
+ u32bit time_slack, validation_cache_timeout;
+ mutable bool revoked_info_valid;
+ };
+
+}
+
+#endif