aboutsummaryrefslogtreecommitdiffstats
path: root/src/cert/x509/x509cert.cpp
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-02-06 19:30:38 +0000
committerlloyd <[email protected]>2012-02-06 19:30:38 +0000
commitf1a2b5a7b5f35322927446d1b9a381f05cc677df (patch)
tree905b125d9173a32c4a3b758ae124ded0d045d635 /src/cert/x509/x509cert.cpp
parentcd58927000ef86eacc9de5b80f361d4d05e71731 (diff)
All of the X509 modules were actually mutually dependent. Ideally this
would be fixed but it's quite hard to do, makes more sense for now to merge then back into one big x509 blog.
Diffstat (limited to 'src/cert/x509/x509cert.cpp')
-rw-r--r--src/cert/x509/x509cert.cpp481
1 files changed, 481 insertions, 0 deletions
diff --git a/src/cert/x509/x509cert.cpp b/src/cert/x509/x509cert.cpp
new file mode 100644
index 000000000..52115a1a8
--- /dev/null
+++ b/src/cert/x509/x509cert.cpp
@@ -0,0 +1,481 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/internal/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<std::string>& in)
+ {
+ std::vector<std::string> out;
+
+ 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()
+ {
+ size_t 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 " + Botan::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"))
+ {
+ const size_t limit = (x509_version() < 3) ?
+ Cert_Extension::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;
+
+ return allowed_usage(KEY_CERT_SIGN);
+ }
+
+bool X509_Certificate::allowed_usage(Key_Constraints restriction) const
+ {
+ if(constraints() == NO_CONSTRAINTS)
+ return true;
+ return (constraints() & restriction);
+ }
+
+/*
+* 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);
+ }
+
+std::string X509_Certificate::to_string() const
+ {
+ const char* dn_fields[] = { "Name",
+ "Email",
+ "Organization",
+ "Organizational Unit",
+ "Locality",
+ "State",
+ "Country",
+ "IP",
+ "DNS",
+ "URI",
+ "PKIX.XMPPAddr",
+ 0 };
+
+ std::ostringstream out;
+
+ for(size_t i = 0; dn_fields[i]; ++i)
+ {
+ const std::vector<std::string> vals = this->subject_info(dn_fields[i]);
+
+ if(vals.empty())
+ continue;
+
+ out << "Subject " << dn_fields[i] << ":";
+ for(size_t j = 0; j != vals.size(); ++j)
+ out << " " << vals[j];
+ out << "\n";
+ }
+
+ for(size_t i = 0; dn_fields[i]; ++i)
+ {
+ const std::vector<std::string> vals = this->issuer_info(dn_fields[i]);
+
+ if(vals.empty())
+ continue;
+
+ out << "Issuer " << dn_fields[i] << ":";
+ for(size_t j = 0; j != vals.size(); ++j)
+ out << " " << vals[j];
+ out << "\n";
+ }
+
+ out << "Version: " << this->x509_version() << "\n";
+
+ out << "Not valid before: " << this->start_time() << "\n";
+ out << "Not valid after: " << this->end_time() << "\n";
+
+ out << "Constraints:\n";
+ Key_Constraints constraints = this->constraints();
+ if(constraints == NO_CONSTRAINTS)
+ out << " None\n";
+ else
+ {
+ if(constraints & DIGITAL_SIGNATURE)
+ out << " Digital Signature\n";
+ if(constraints & NON_REPUDIATION)
+ out << " Non-Repuidation\n";
+ if(constraints & KEY_ENCIPHERMENT)
+ out << " Key Encipherment\n";
+ if(constraints & DATA_ENCIPHERMENT)
+ out << " Data Encipherment\n";
+ if(constraints & KEY_AGREEMENT)
+ out << " Key Agreement\n";
+ if(constraints & KEY_CERT_SIGN)
+ out << " Cert Sign\n";
+ if(constraints & CRL_SIGN)
+ out << " CRL Sign\n";
+ }
+
+ std::vector<std::string> policies = this->policies();
+ if(policies.size())
+ {
+ out << "Policies: " << "\n";
+ for(size_t i = 0; i != policies.size(); i++)
+ out << " " << policies[i] << "\n";
+ }
+
+ std::vector<std::string> ex_constraints = this->ex_constraints();
+ if(ex_constraints.size())
+ {
+ out << "Extended Constraints:\n";
+ for(size_t i = 0; i != ex_constraints.size(); i++)
+ out << " " << ex_constraints[i] << "\n";
+ }
+
+ out << "Signature algorithm: " <<
+ OIDS::lookup(this->signature_algorithm().oid) << "\n";
+
+ out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+ if(this->authority_key_id().size())
+ out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+ if(this->subject_key_id().size())
+ out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+ X509_PublicKey* pubkey = this->subject_public_key();
+ out << "Public Key:\n" << X509::PEM_encode(*pubkey);
+ delete pubkey;
+
+ return out.str();
+ }
+
+/*
+* 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 i;
+ for(i = names.begin(); i != names.end(); ++i)
+ dn.add_attribute(i->first, i->second);
+
+ return dn;
+ }
+
+/*
+* Create and populate an AlternativeName
+*/
+AlternativeName create_alt_name(const Data_Store& info)
+ {
+ class AltName_Matcher : public Data_Store::Matcher
+ {
+ public:
+ bool operator()(const std::string& key, const std::string&) const
+ {
+ for(size_t i = 0; i != matches.size(); ++i)
+ if(key.compare(matches[i]) == 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 i;
+ for(i = names.begin(); i != names.end(); ++i)
+ alt_name.add_attribute(i->first, i->second);
+
+ return alt_name;
+ }
+
+}