diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/x509/pkcs10.cpp | 56 | ||||
-rw-r--r-- | src/lib/x509/pkcs10.h | 22 | ||||
-rw-r--r-- | src/lib/x509/x509_ca.cpp | 63 | ||||
-rw-r--r-- | src/lib/x509/x509_ext.cpp | 15 | ||||
-rw-r--r-- | src/lib/x509/x509_ext.h | 10 | ||||
-rw-r--r-- | src/lib/x509/x509_obj.cpp | 67 | ||||
-rw-r--r-- | src/lib/x509/x509_obj.h | 9 | ||||
-rw-r--r-- | src/lib/x509/x509self.cpp | 80 |
8 files changed, 201 insertions, 121 deletions
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp index 78fea8dc6..2da002cd1 100644 --- a/src/lib/x509/pkcs10.cpp +++ b/src/lib/x509/pkcs10.cpp @@ -9,6 +9,8 @@ #include <botan/x509_ext.h> #include <botan/x509cert.h> #include <botan/ber_dec.h> +#include <botan/der_enc.h> +#include <botan/pubkey.h> #include <botan/oids.h> #include <botan/pem.h> @@ -52,6 +54,60 @@ PKCS10_Request::PKCS10_Request(const std::string& fsname) } #endif +//static +PKCS10_Request PKCS10_Request::create(const Private_Key& key, + const X509_DN& subject_dn, + const Extensions& extensions, + const std::string& hash_fn, + RandomNumberGenerator& rng, + const std::string& padding_scheme, + const std::string& challenge) + { + const std::map<std::string,std::string> sig_opts = { {"padding", padding_scheme} }; + + AlgorithmIdentifier sig_algo; + std::unique_ptr<PK_Signer> signer = choose_sig_format(sig_algo, key, rng, hash_fn, padding_scheme); + + const size_t PKCS10_VERSION = 0; + + DER_Encoder tbs_req; + + tbs_req.start_cons(SEQUENCE) + .encode(PKCS10_VERSION) + .encode(subject_dn) + .raw_bytes(key.subject_public_key()) + .start_explicit(0); + + if(challenge.empty() == false) + { + ASN1_String challenge_str(challenge, DIRECTORY_STRING); + + tbs_req.encode( + Attribute("PKCS9.ChallengePassword", + DER_Encoder().encode(challenge_str).get_contents_unlocked() + ) + ); + } + + tbs_req.encode( + Attribute("PKCS9.ExtensionRequest", + DER_Encoder() + .start_cons(SEQUENCE) + .encode(extensions) + .end_cons() + .get_contents_unlocked() + ) + ) + .end_explicit() + .end_cons(); + + const std::vector<uint8_t> req = + X509_Object::make_signed(signer.get(), rng, sig_algo, + tbs_req.get_contents()); + + return PKCS10_Request(req); + } + /* * Decode the CertificateRequestInfo */ diff --git a/src/lib/x509/pkcs10.h b/src/lib/x509/pkcs10.h index b3a75fd5e..bd8cdb2e1 100644 --- a/src/lib/x509/pkcs10.h +++ b/src/lib/x509/pkcs10.h @@ -18,6 +18,7 @@ namespace Botan { +class Private_Key; class Extensions; struct PKCS10_Data; @@ -109,6 +110,27 @@ class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object * @param vec a std::vector containing the DER value */ explicit PKCS10_Request(const std::vector<uint8_t>& vec); + + /** + * Create a new PKCS10 certificate request + * @param key the key that will be included in the certificate request + * @param subject_dn the DN to be placed in the request + * @param extensions extensions to include in the request + * @param hash_fn the hash function to use to create the signature + * @param rng a random number generator + * @param padding_scheme if set specifies the padding scheme, otherwise an + * algorithm-specific default is used. + * @param challenge a challenge string to be included in the PKCS10 request, + * sometimes used for revocation purposes. + */ + static PKCS10_Request create(const Private_Key& key, + const X509_DN& subject_dn, + const Extensions& extensions, + const std::string& hash_fn, + RandomNumberGenerator& rng, + const std::string& padding_scheme = "", + const std::string& challenge = ""); + private: std::string PEM_label() const override; diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp index 1560140c7..294a564d5 100644 --- a/src/lib/x509/x509_ca.cpp +++ b/src/lib/x509/x509_ca.cpp @@ -277,76 +277,19 @@ PK_Signer* choose_sig_format(const Private_Key& key, const std::string& hash_fn, AlgorithmIdentifier& sig_algo) { - return choose_sig_format(key, std::map<std::string,std::string>(), - rng, hash_fn, sig_algo); + return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, "").release(); } -/* -* Choose a signing format for the key -*/ PK_Signer* choose_sig_format(const Private_Key& key, const std::map<std::string,std::string>& opts, RandomNumberGenerator& rng, const std::string& hash_fn, AlgorithmIdentifier& sig_algo) { - const std::string algo_name = key.algo_name(); - - std::unique_ptr<HashFunction> hash(HashFunction::create_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()) - { + if(opts.count("padding")) padding = opts.at("padding"); - } - - // try to construct an EMSA object from the padding options or default - std::unique_ptr<EMSA> emsa = nullptr; - try - { - emsa.reset(get_emsa(padding)); - } - /* - * get_emsa will throw if opts contains {"padding",<valid_padding>} but - * <valid_padding> 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); + return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, padding).release(); } } diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp index 44c469c48..b969ad7cf 100644 --- a/src/lib/x509/x509_ext.cpp +++ b/src/lib/x509/x509_ext.cpp @@ -131,6 +131,21 @@ void Extensions::add(Certificate_Extension* extn, bool critical) m_extension_info.emplace(oid, info); } +bool Extensions::add_new(Certificate_Extension* extn, bool critical) + { + if(m_extension_info.count(extn->oid_of()) > 0) + { + delete extn; + return false; // already exists + } + + const OID oid = extn->oid_of(); + Extensions_Info info(critical, extn); + m_extension_oids.push_back(oid); + m_extension_info.emplace(oid, info); + return true; + } + void Extensions::replace(Certificate_Extension* extn, bool critical) { // Remove it if it existed diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h index 8e702daf1..7c8a8569c 100644 --- a/src/lib/x509/x509_ext.h +++ b/src/lib/x509/x509_ext.h @@ -155,6 +155,16 @@ class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object void add(Certificate_Extension* extn, bool critical = false); /** + * Adds a new extension to the list unless it already exists. If the extension + * already exists within the Extensions object, the extn pointer will be deleted. + * + * @param extn pointer to the certificate extension (Extensions takes ownership) + * @param critical whether this extension should be marked as critical + * @return true if the object was added false if the extension was already used + */ + bool add_new(Certificate_Extension* extn, bool critical = false); + + /** * Adds an extension to the list or replaces it. * @param extn the certificate extension * @param critical whether this extension should be marked as critical diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp index 01a92a001..78413c121 100644 --- a/src/lib/x509/x509_obj.cpp +++ b/src/lib/x509/x509_obj.cpp @@ -12,6 +12,7 @@ #include <botan/ber_dec.h> #include <botan/parsing.h> #include <botan/pem.h> +#include <botan/emsa.h> #include <algorithm> namespace Botan { @@ -284,4 +285,70 @@ std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer, .get_contents_unlocked(); } +/* +* Choose a signing format for the key +*/ +std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(AlgorithmIdentifier& sig_algo, + const Private_Key& key, + RandomNumberGenerator& rng, + const std::string& hash_fn, + const std::string& padding_algo) + { + const std::string algo_name = key.algo_name(); + + std::string padding; + + // check algo_name and set default + if(algo_name == "RSA") + { + // set to EMSA3 for compatibility reasons, originally it was the only option + padding = "EMSA3(" + hash_fn + ")"; + } + else if(algo_name == "DSA" || + algo_name == "ECDSA" || + algo_name == "ECGDSA" || + algo_name == "ECKCDSA" || + algo_name == "GOST-34.10") + { + padding = "EMSA1(" + hash_fn + ")"; + } + else + { + throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name); + } + + if(padding_algo.empty() == false) + { + padding = padding_algo; + } + + // try to construct an EMSA object from the padding options or default + std::unique_ptr<EMSA> emsa = nullptr; + try + { + emsa.reset(get_emsa(padding)); + } + /* + * get_emsa will throw if opts contains {"padding",<valid_padding>} but + * <valid_padding> 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_fn); + + return std::unique_ptr<PK_Signer>(new PK_Signer(key, rng, emsa->name(), format)); + } + } diff --git a/src/lib/x509/x509_obj.h b/src/lib/x509/x509_obj.h index 72ba0f534..1e4abe00b 100644 --- a/src/lib/x509/x509_obj.h +++ b/src/lib/x509/x509_obj.h @@ -16,6 +16,7 @@ namespace Botan { class Public_Key; +class Private_Key; class RandomNumberGenerator; /** @@ -119,6 +120,14 @@ class BOTAN_PUBLIC_API(2,0) X509_Object : public ASN1_Object { return std::vector<std::string>(); } virtual ~X509_Object() = default; + + static std::unique_ptr<PK_Signer> + choose_sig_format(AlgorithmIdentifier& sig_algo, + const Private_Key& key, + RandomNumberGenerator& rng, + const std::string& hash_fn, + const std::string& padding_algo); + protected: X509_Object() = default; diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp index 108e0496b..78cdfe741 100644 --- a/src/lib/x509/x509self.cpp +++ b/src/lib/x509/x509self.cpp @@ -56,6 +56,8 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo)); load_info(opts, subject_dn, subject_alt); + Extensions extensions = opts.extensions; + Key_Constraints constraints; if(opts.is_CA) { @@ -67,23 +69,21 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, constraints = opts.constraints; } - Extensions extensions = opts.extensions; - - extensions.add( + extensions.add_new( new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit), true); if(constraints != NO_CONSTRAINTS) { - extensions.add(new Cert_Extension::Key_Usage(constraints), true); + extensions.add_new(new Cert_Extension::Key_Usage(constraints), true); } - extensions.add(new Cert_Extension::Subject_Key_ID(pub_key, hash_fn)); + extensions.add_new(new Cert_Extension::Subject_Key_ID(pub_key, hash_fn)); - extensions.add( + extensions.add_new( new Cert_Extension::Subject_Alternative_Name(subject_alt)); - extensions.add( + extensions.add_new( new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key, @@ -100,19 +100,10 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts, const std::string& hash_fn, RandomNumberGenerator& rng) { - AlgorithmIdentifier sig_algo; X509_DN subject_dn; AlternativeName subject_alt; - - // for now, only the padding option is used - std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} }; - - std::vector<uint8_t> pub_key = X509::BER_encode(key); - std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo)); load_info(opts, subject_dn, subject_alt); - const size_t PKCS10_VERSION = 0; - Key_Constraints constraints; if(opts.is_CA) { @@ -126,55 +117,22 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts, Extensions extensions = opts.extensions; - extensions.add( - new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit)); + extensions.add_new(new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit)); if(constraints != NO_CONSTRAINTS) { - extensions.add( - new Cert_Extension::Key_Usage(constraints)); - } - extensions.add( - new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); - extensions.add( - new Cert_Extension::Subject_Alternative_Name(subject_alt)); - - DER_Encoder tbs_req; - - tbs_req.start_cons(SEQUENCE) - .encode(PKCS10_VERSION) - .encode(subject_dn) - .raw_bytes(pub_key) - .start_explicit(0); - - if(!opts.challenge.empty()) - { - ASN1_String challenge(opts.challenge, DIRECTORY_STRING); - - tbs_req.encode( - Attribute("PKCS9.ChallengePassword", - DER_Encoder().encode(challenge).get_contents_unlocked() - ) - ); + extensions.add_new(new Cert_Extension::Key_Usage(constraints)); } - - tbs_req.encode( - Attribute("PKCS9.ExtensionRequest", - DER_Encoder() - .start_cons(SEQUENCE) - .encode(extensions) - .end_cons() - .get_contents_unlocked() - ) - ) - .end_explicit() - .end_cons(); - - const std::vector<uint8_t> req = - X509_Object::make_signed(signer.get(), rng, sig_algo, - tbs_req.get_contents()); - - return PKCS10_Request(req); + extensions.add_new(new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); + extensions.add_new(new Cert_Extension::Subject_Alternative_Name(subject_alt)); + + return PKCS10_Request::create(key, + subject_dn, + extensions, + hash_fn, + rng, + opts.padding_scheme, + opts.challenge); } } |