in src/governance/ccf-app/cpp/crypto/openssl/key_pair.cpp [304:448]
Pem KeyPair_OpenSSL::sign_csr_impl(
const std::optional<Pem>& issuer_cert,
const Pem& signing_request,
const std::string& valid_from,
const std::string& valid_to,
bool ca,
std::optional<int> ca_path_len_constraint,
Signer signer) const
{
X509* icrt = NULL;
Unique_BIO mem(signing_request);
Unique_X509_REQ csr(mem);
Unique_X509 crt;
EVP_PKEY* req_pubkey = NULL;
// First, verify self-signed CSR
if (signer == Signer::SUBJECT)
{
req_pubkey = X509_REQ_get0_pubkey(csr);
OpenSSL::CHECK1(X509_REQ_verify(csr, req_pubkey));
}
// Add version
OpenSSL::CHECK1(X509_set_version(crt, 2));
// Add serial number
unsigned char rndbytes[16];
OpenSSL::CHECK1(RAND_bytes(rndbytes, sizeof(rndbytes)));
BIGNUM* bn = NULL;
OpenSSL::CHECKNULL(bn = BN_new());
OpenSSL::CHECKNULL(BN_bin2bn(rndbytes, sizeof(rndbytes), bn));
ASN1_INTEGER* serial = ASN1_INTEGER_new();
BN_to_ASN1_INTEGER(bn, serial);
OpenSSL::CHECK1(X509_set_serialNumber(crt, serial));
ASN1_INTEGER_free(serial);
BN_free(bn);
// Add issuer name
if (issuer_cert.has_value())
{
Unique_BIO imem(*issuer_cert);
OpenSSL::CHECKNULL(icrt = PEM_read_bio_X509(imem, NULL, NULL, NULL));
OpenSSL::CHECK1(X509_set_issuer_name(crt, X509_get_subject_name(icrt)));
if (signer == Signer::ISSUER)
{
// Verify issuer-signed CSR
req_pubkey = X509_REQ_get0_pubkey(csr);
auto issuer_pubkey = X509_get0_pubkey(icrt);
OpenSSL::CHECK1(X509_REQ_verify(csr, issuer_pubkey));
}
}
else
{
OpenSSL::CHECK1(
X509_set_issuer_name(crt, X509_REQ_get_subject_name(csr)));
}
Unique_X509_TIME not_before(valid_from);
Unique_X509_TIME not_after(valid_to);
if (!validate_chronological_times(not_before, not_after))
{
throw std::logic_error(fmt::format(
"Certificate cannot be created with not_before date {} > not_after "
"date {}",
to_x509_time_string(not_before),
to_x509_time_string(not_after)));
}
OpenSSL::CHECK1(X509_set1_notBefore(crt, not_before));
OpenSSL::CHECK1(X509_set1_notAfter(crt, not_after));
X509_set_subject_name(crt, X509_REQ_get_subject_name(csr));
X509_set_pubkey(crt, req_pubkey);
// Extensions
X509V3_CTX v3ctx;
X509V3_set_ctx_nodb(&v3ctx);
X509V3_set_ctx(&v3ctx, icrt ? icrt : crt, NULL, csr, NULL, 0);
std::string constraints = "critical,CA:FALSE";
if (ca)
{
int path_len = ca_path_len_constraint.value_or(0);
constraints = fmt::format("critical,CA:TRUE,pathlen:{}", path_len);
}
// Add basic constraints
X509_EXTENSION* ext = NULL;
OpenSSL::CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_basic_constraints, constraints.c_str()));
OpenSSL::CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Add subject key identifier
OpenSSL::CHECKNULL(
ext =
X509V3_EXT_conf_nid(NULL, &v3ctx, NID_subject_key_identifier, "hash"));
OpenSSL::CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Add authority key identifier
OpenSSL::CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_authority_key_identifier, "keyid:always"));
OpenSSL::CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Add subject alternative names (read from csr)
Unique_STACK_OF_X509_EXTENSIONS exts = X509_REQ_get_extensions(csr);
int extension_count = sk_X509_EXTENSION_num(exts);
if (extension_count > 0)
{
for (int i = 0; i < extension_count; i++)
{
X509_EXTENSION* ext1 = sk_X509_EXTENSION_value(exts, i);
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ext1);
auto nid = OBJ_obj2nid(obj);
if (nid == NID_subject_alt_name)
{
OpenSSL::CHECK1(X509_add_ext(crt, ext1, -1));
}
}
}
// Sign
auto md = get_md_type(get_md_for_ec(get_curve_id()));
int size = X509_sign(crt, key, md);
if (size <= 0)
throw std::runtime_error("could not sign CRT");
Unique_BIO omem;
OpenSSL::CHECK1(PEM_write_bio_X509(omem, crt));
// Export
BUF_MEM* bptr;
BIO_get_mem_ptr(omem, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
if (icrt)
X509_free(icrt);
return result;
}