/* * X.509 Certificate Authority * (C) 1999-2010 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Botan { /* * Load the certificate and private key */ X509_CA::X509_CA(const X509_Certificate& c, const Private_Key& key, const std::string& hash_fn, RandomNumberGenerator& rng) : m_ca_cert(c), m_hash_fn(hash_fn) { if(!m_ca_cert.is_CA_cert()) throw Invalid_Argument("X509_CA: This certificate is not for a CA"); std::map opts; // constructor without additional options: use the padding used in the CA certificate // sig_oid_str = /, so padding with all its options will look // like a cipher mode to the scanner std::string sig_oid_str = OIDS::lookup(c.signature_algorithm().oid); SCAN_Name scanner(sig_oid_str); std::string pad = scanner.cipher_mode(); if(!pad.empty()) opts.insert({"padding",pad}); m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo)); } /* * Load the certificate and private key, and additional options */ X509_CA::X509_CA(const X509_Certificate& ca_certificate, const Private_Key& key, const std::map& opts, const std::string& hash_fn, RandomNumberGenerator& rng) : m_ca_cert(ca_certificate), m_hash_fn(hash_fn) { if(!m_ca_cert.is_CA_cert()) throw Invalid_Argument("X509_CA: This certificate is not for a CA"); m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo)); } /* * X509_CA Destructor */ X509_CA::~X509_CA() { /* for unique_ptr */ } /* * 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) const { Key_Constraints constraints; if(req.is_CA()) { constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN); } else { std::unique_ptr key(req.subject_public_key()); verify_cert_constraints_valid_for_key_type(*key, req.constraints()); constraints = req.constraints(); } Extensions extensions = req.extensions(); extensions.replace( new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()), true); if(constraints != NO_CONSTRAINTS) { extensions.replace(new Cert_Extension::Key_Usage(constraints), true); } extensions.replace(new Cert_Extension::Authority_Key_ID(m_ca_cert.subject_key_id())); extensions.replace(new Cert_Extension::Subject_Key_ID(req.raw_public_key(), m_hash_fn)); extensions.replace( new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name())); extensions.replace( new Cert_Extension::Extended_Key_Usage(req.ex_constraints())); return make_cert(m_signer.get(), rng, m_ca_sig_algo, req.raw_public_key(), not_before, not_after, m_ca_cert.subject_dn(), req.subject_dn(), extensions); } /* * Create a new certificate */ X509_Certificate X509_CA::make_cert(PK_Signer* signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector& pub_key, const X509_Time& not_before, const X509_Time& not_after, const X509_DN& issuer_dn, const X509_DN& subject_dn, const Extensions& extensions) { const size_t X509_CERT_VERSION = 3; const size_t SERIAL_BITS = 128; BigInt serial_no(rng, SERIAL_BITS); // clang-format off return X509_Certificate(X509_Object::make_signed( signer, rng, sig_algo, DER_Encoder().start_cons(SEQUENCE) .start_explicit(0) .encode(X509_CERT_VERSION-1) .end_explicit() .encode(serial_no) .encode(sig_algo) .encode(issuer_dn) .start_cons(SEQUENCE) .encode(not_before) .encode(not_after) .end_cons() .encode(subject_dn) .raw_bytes(pub_key) .start_explicit(3) .start_cons(SEQUENCE) .encode(extensions) .end_cons() .end_explicit() .end_cons() .get_contents() ));; // clang-format on } /* * Create a new, empty CRL */ X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng, uint32_t next_update) const { return new_crl(rng, std::chrono::system_clock::now(), std::chrono::seconds(next_update)); } /* * Update a CRL with new entries */ X509_CRL X509_CA::update_crl(const X509_CRL& crl, const std::vector& new_revoked, RandomNumberGenerator& rng, uint32_t next_update) const { return update_crl(crl, new_revoked, rng, std::chrono::system_clock::now(), std::chrono::seconds(next_update)); } X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng, std::chrono::system_clock::time_point issue_time, std::chrono::seconds next_update) const { std::vector empty; return make_crl(empty, 1, rng, issue_time, next_update); } X509_CRL X509_CA::update_crl(const X509_CRL& last_crl, const std::vector& new_revoked, RandomNumberGenerator& rng, std::chrono::system_clock::time_point issue_time, std::chrono::seconds next_update) const { std::vector revoked = last_crl.get_revoked(); std::copy(new_revoked.begin(), new_revoked.end(), std::back_inserter(revoked)); return make_crl(revoked, last_crl.crl_number() + 1, rng, issue_time, next_update); } /* * Create a CRL */ X509_CRL X509_CA::make_crl(const std::vector& revoked, uint32_t crl_number, RandomNumberGenerator& rng, std::chrono::system_clock::time_point issue_time, std::chrono::seconds next_update) const { const size_t X509_CRL_VERSION = 2; auto expire_time = issue_time + next_update; Extensions extensions; extensions.add(new Cert_Extension::Authority_Key_ID(m_ca_cert.subject_key_id())); extensions.add(new Cert_Extension::CRL_Number(crl_number)); // clang-format off const std::vector crl = X509_Object::make_signed( m_signer.get(), rng, m_ca_sig_algo, DER_Encoder().start_cons(SEQUENCE) .encode(X509_CRL_VERSION-1) .encode(m_ca_sig_algo) .encode(m_ca_cert.subject_dn()) .encode(X509_Time(issue_time)) .encode(X509_Time(expire_time)) .encode_if(revoked.size() > 0, DER_Encoder() .start_cons(SEQUENCE) .encode_list(revoked) .end_cons() ) .start_explicit(0) .start_cons(SEQUENCE) .encode(extensions) .end_cons() .end_explicit() .end_cons() .get_contents()); // clang-format on return X509_CRL(crl); } /* * Return the CA's certificate */ X509_Certificate X509_CA::ca_certificate() const { return m_ca_cert; } /* * Choose a signing format for the key */ PK_Signer* choose_sig_format(const Private_Key& key, RandomNumberGenerator& rng, const std::string& hash_fn, AlgorithmIdentifier& sig_algo) { return choose_sig_format(key, std::map(), rng, hash_fn, sig_algo); } /* * Choose a signing format for the key */ PK_Signer* choose_sig_format(const Private_Key& key, const std::map& opts, RandomNumberGenerator& rng, const std::string& hash_fn, AlgorithmIdentifier& sig_algo) { const std::string algo_name = key.algo_name(); std::unique_ptr hash(HashFunction::create_or_throw(hash_fn)); std::string hash_name = hash->name(); // check algo_name and set default std::string padding; if(algo_name == "RSA") { // set to EMSA3 for compatibility reasons, originally it was the only option padding = "EMSA3(" + hash_name + ")"; } else if(algo_name == "DSA" || algo_name == "ECDSA" || algo_name == "ECGDSA" || algo_name == "ECKCDSA" || algo_name == "GOST-34.10") { padding = "EMSA1(" + hash_name + ")"; } else { throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); } if(opts.count("padding") > 0 && !opts.at("padding").empty()) { padding = opts.at("padding"); } // try to construct an EMSA object from the padding options or default std::unique_ptr emsa = nullptr; try { emsa.reset(get_emsa(padding)); } /* * get_emsa will throw if opts contains {"padding",} but * does not specify a hash function. * Omitting it is valid since it needs to be identical to hash_fn. * If it still throws, something happened that we cannot repair here, * e.g. the algorithm/padding combination is not supported. */ catch(...) { emsa.reset(get_emsa(padding + "(" + hash_fn + ")")); } if(emsa == nullptr) { throw Invalid_Argument("Could not parse padding scheme " + padding); } const Signature_Format format = (key.message_parts() > 1) ? DER_SEQUENCE : IEEE_1363; sig_algo = emsa->config_for_x509(key, hash_name); return new PK_Signer(key, rng, emsa->name(), format); } }