aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/x509/pkcs10.cpp56
-rw-r--r--src/lib/x509/pkcs10.h22
-rw-r--r--src/lib/x509/x509_ca.cpp63
-rw-r--r--src/lib/x509/x509_ext.cpp15
-rw-r--r--src/lib/x509/x509_ext.h10
-rw-r--r--src/lib/x509/x509_obj.cpp67
-rw-r--r--src/lib/x509/x509_obj.h9
-rw-r--r--src/lib/x509/x509self.cpp80
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);
}
}