Pem KeyPair_OpenSSL::sign_csr_impl()

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;
  }