Skip to content

Commit

Permalink
CMP: add support for central key generation
Browse files Browse the repository at this point in the history
  • Loading branch information
rajeev-0 committed Aug 7, 2024
1 parent 07e4d7f commit cd97785
Show file tree
Hide file tree
Showing 19 changed files with 463 additions and 86 deletions.
57 changes: 51 additions & 6 deletions apps/cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ static char *opt_profile = NULL;
/* certificate enrollment */
static char *opt_newkey = NULL;
static char *opt_newkeypass = NULL;
static int opt_centralkeygen = 0;
static char *opt_newkeyout = NULL;
static char *opt_subject = NULL;
static int opt_days = 0;
static char *opt_reqexts = NULL;
Expand Down Expand Up @@ -229,7 +231,8 @@ typedef enum OPTION_choice {
OPT_CMD, OPT_INFOTYPE, OPT_PROFILE, OPT_GENINFO,
OPT_TEMPLATE, OPT_KEYSPEC,

OPT_NEWKEY, OPT_NEWKEYPASS, OPT_SUBJECT,
OPT_NEWKEY, OPT_NEWKEYPASS, OPT_CENTRALKEYGEN,
OPT_NEWKEYOUT, OPT_SUBJECT,
OPT_DAYS, OPT_REQEXTS,
OPT_SANS, OPT_SAN_NODEFAULT,
OPT_POLICIES, OPT_POLICY_OIDS, OPT_POLICY_OIDS_CRITICAL,
Expand Down Expand Up @@ -325,6 +328,10 @@ const OPTIONS cmp_options[] = {
{"newkey", OPT_NEWKEY, 's',
"Private or public key for the requested cert. Default: CSR key or client key"},
{"newkeypass", OPT_NEWKEYPASS, 's', "New private key pass phrase source"},
{"centralkeygen", OPT_CENTRALKEYGEN, '-',
"Request central (server-side) key generation. Default is local generation"},
{"newkeyout", OPT_NEWKEYOUT, 's',
"File to save new key generated in central key generation"},
{"subject", OPT_SUBJECT, 's',
"Distinguished Name (DN) of subject to use in the requested cert template"},
{OPT_MORE_STR, 0, 0,
Expand Down Expand Up @@ -629,8 +636,8 @@ static varref cmp_vars[] = { /* must be in same order as enumerated above! */
{&opt_cmd_s}, {&opt_infotype_s}, {&opt_profile}, {&opt_geninfo},
{&opt_template}, {&opt_keyspec},

{&opt_newkey}, {&opt_newkeypass}, {&opt_subject},
{(char **)&opt_days}, {&opt_reqexts},
{&opt_newkey}, {&opt_newkeypass}, {(char **)&opt_centralkeygen},
{&opt_newkeyout}, {&opt_subject}, {(char **)&opt_days}, {&opt_reqexts},
{&opt_sans}, {(char **)&opt_san_nodefault},
{&opt_policies}, {&opt_policy_oids}, {(char **)&opt_policy_oids_critical},
{(char **)&opt_popo}, {&opt_csr},
Expand Down Expand Up @@ -1671,11 +1678,27 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
if (!set_name(opt_issuer, OSSL_CMP_CTX_set1_issuer, ctx, "issuer"))
return 0;
if (opt_cmd == CMP_IR || opt_cmd == CMP_CR || opt_cmd == CMP_KUR) {
if (opt_reqin == NULL && opt_newkey == NULL
if (opt_reqin == NULL && opt_newkey == NULL && !opt_centralkeygen
&& opt_key == NULL && opt_csr == NULL && opt_oldcert == NULL) {
CMP_err("missing -newkey (or -key) to be certified and no -csr, -oldcert, -cert, or -reqin option given, which could provide fallback public key");
CMP_err("missing -newkey (or -key) to be certified and no -csr, -oldcert, -cert, or -reqin option given, which could provide fallback public key."
"Neither central key generation is requested.");
return 0;
}
if (opt_popo == OSSL_CRMF_POPO_NONE && !opt_centralkeygen) {
CMP_info("POPO is disabled, using -centralkeygen");
opt_centralkeygen = 1;
}
if (opt_centralkeygen) {
if (opt_popo > OSSL_CRMF_POPO_NONE) {
CMP_err1("-popo value %d is inconsistent with -centralkeygen", opt_popo);
return 0;
}
if (opt_newkeyout == NULL) {
CMP_err("-newkeyout not given, nowhere to save newly generated key");
return 0;
}
opt_popo = OSSL_CRMF_POPO_NONE;
}
if (opt_newkey == NULL
&& opt_popo != OSSL_CRMF_POPO_NONE
&& opt_popo != OSSL_CRMF_POPO_RAVERIFIED) {
Expand Down Expand Up @@ -1723,6 +1746,10 @@ static int setup_request_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
CMP_warn1("-policies %s", msg);
if (opt_policy_oids != NULL)
CMP_warn1("-policy_oids %s", msg);
if (opt_centralkeygen)
CMP_warn1("-centralkeygen %s", msg);
if (opt_newkeyout != NULL)
CMP_warn1("-newkeyout %s", msg);
if (opt_cmd != CMP_P10CR) {
if (opt_implicit_confirm)
CMP_warn1("-implicit_confirm %s, and 'p10cr'", msg);
Expand Down Expand Up @@ -2921,13 +2948,18 @@ static int get_opts(int argc, char **argv)
case OPT_KEYSPEC:
opt_keyspec = opt_str();
break;

case OPT_NEWKEY:
opt_newkey = opt_str();
break;
case OPT_NEWKEYPASS:
opt_newkeypass = opt_str();
break;
case OPT_CENTRALKEYGEN:
opt_centralkeygen = 1;
break;
case OPT_NEWKEYOUT:
opt_newkeyout = opt_str();
break;
case OPT_SUBJECT:
opt_subject = opt_str();
break;
Expand Down Expand Up @@ -3792,6 +3824,19 @@ int cmp_main(int argc, char **argv)
if (save_free_certs(OSSL_CMP_CTX_get1_caPubs(cmp_ctx),
opt_cacertsout, "CA") < 0)
goto err;
if (opt_centralkeygen) {
EVP_PKEY *new_key = OSSL_CMP_CTX_get0_newPkey(cmp_ctx, 1 /* priv */);
BIO *out = bio_open_owner(opt_newkeyout, FORMAT_PEM, 1);

if (out == NULL)
goto err;
CMP_info1("received central (server) generated key, saving to file '%s'",
opt_newkeyout);
if (PEM_write_bio_PrivateKey(out, new_key, NULL, NULL, 0, NULL,
NULL) <= 0)
goto err;
BIO_free(out);
}
}
if (!OSSL_CMP_CTX_reinit(cmp_ctx))
goto err;
Expand Down
4 changes: 2 additions & 2 deletions crypto/cmp/cmp_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,15 +889,15 @@ ASN1_CHOICE(OSSL_CMP_CERTORENCCERT) = {
/* OSSL_CMP_CMPCERTIFICATE is effectively X509 so it is used directly */
ASN1_EXP(OSSL_CMP_CERTORENCCERT, value.certificate, X509, 0),
ASN1_EXP(OSSL_CMP_CERTORENCCERT, value.encryptedCert,
OSSL_CRMF_ENCRYPTEDVALUE, 1),
OSSL_CRMF_ENCRYPTEDKEY, 1),
} ASN1_CHOICE_END(OSSL_CMP_CERTORENCCERT)
IMPLEMENT_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT)

ASN1_SEQUENCE(OSSL_CMP_CERTIFIEDKEYPAIR) = {
ASN1_SIMPLE(OSSL_CMP_CERTIFIEDKEYPAIR, certOrEncCert,
OSSL_CMP_CERTORENCCERT),
ASN1_EXP_OPT(OSSL_CMP_CERTIFIEDKEYPAIR, privateKey,
OSSL_CRMF_ENCRYPTEDVALUE, 0),
OSSL_CRMF_ENCRYPTEDKEY, 0),
ASN1_EXP_OPT(OSSL_CMP_CERTIFIEDKEYPAIR, publicationInfo,
OSSL_CRMF_PKIPUBLICATIONINFO, 1)
} ASN1_SEQUENCE_END(OSSL_CMP_CERTIFIEDKEYPAIR)
Expand Down
6 changes: 4 additions & 2 deletions crypto/cmp/cmp_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ static X509 *get1_cert_status(OSSL_CMP_CTX *ctx, int bodytype,
{
char buf[OSSL_CMP_PKISI_BUFLEN];
X509 *crt = NULL;
EVP_PKEY *privkey = NULL;

if (!ossl_assert(ctx != NULL && crep != NULL))
return NULL;
Expand Down Expand Up @@ -546,7 +547,7 @@ static X509 *get1_cert_status(OSSL_CMP_CTX *ctx, int bodytype,
ERR_raise(ERR_LIB_CMP, CMP_R_UNKNOWN_PKISTATUS);
goto err;
}
crt = ossl_cmp_certresponse_get1_cert(ctx, crep);
crt = ossl_cmp_certresponse_get1_cert_key(crep, ctx, privkey);
if (crt == NULL) /* according to PKIStatus, we can expect a cert */
ERR_raise(ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND);

Expand Down Expand Up @@ -653,7 +654,7 @@ static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid,
ossl_unused int req_type,
ossl_unused int expected_type)
{
EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx);
EVP_PKEY *rkey = NULL;
int fail_info = 0; /* no failure */
const char *txt = NULL;
OSSL_CMP_CERTREPMESSAGE *crepmsg = NULL;
Expand Down Expand Up @@ -745,6 +746,7 @@ static int cert_response(OSSL_CMP_CTX *ctx, int sleep, int rid,
return 0;

subj = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
rkey = ossl_cmp_ctx_get0_newPubkey(ctx);
if (rkey != NULL
/* X509_check_private_key() also works if rkey is just public key */
&& !(X509_check_private_key(ctx->newCert, rkey))) {
Expand Down
6 changes: 6 additions & 0 deletions crypto/cmp/cmp_err.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_EXPECTED_POLLREQ), "expected pollreq"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_BUILDING_OWN_CHAIN),
"failed building own chain"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY),
"failed extracting central gen key"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILED_EXTRACTING_PUBKEY),
"failed extracting pubkey"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_FAILURE_OBTAINING_RANDOM),
Expand All @@ -97,6 +99,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_OPTION), "invalid option"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_INVALID_ROOTCAKEYUPDATE),
"invalid rootcakeyupdate"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_CENTRAL_GEN_KEY),
"missing central gen key"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_CERTID), "missing certid"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION),
"missing key input for creating protection"},
Expand Down Expand Up @@ -151,6 +155,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = {
"transactionid unmatched"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_TRANSFER_ERROR), "transfer error"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNCLEAN_CTX), "unclean ctx"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY),
"unexpected central gen key"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CERTPROFILE),
"unexpected certprofile"},
{ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_CRLSTATUSLIST),
Expand Down
8 changes: 4 additions & 4 deletions crypto/cmp/cmp_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ typedef struct ossl_cmp_certorenccert_st {
int type;
union {
X509 *certificate;
OSSL_CRMF_ENCRYPTEDVALUE *encryptedCert;
OSSL_CRMF_ENCRYPTEDKEY *encryptedCert;
} value;
} OSSL_CMP_CERTORENCCERT;
DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT)
Expand All @@ -326,7 +326,7 @@ DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTORENCCERT)
*/
typedef struct ossl_cmp_certifiedkeypair_st {
OSSL_CMP_CERTORENCCERT *certOrEncCert;
OSSL_CRMF_ENCRYPTEDVALUE *privateKey;
OSSL_CRMF_ENCRYPTEDKEY *privateKey;
OSSL_CRMF_PKIPUBLICATIONINFO *publicationInfo;
} OSSL_CMP_CERTIFIEDKEYPAIR;
DECLARE_ASN1_FUNCTIONS(OSSL_CMP_CERTIFIEDKEYPAIR)
Expand Down Expand Up @@ -988,8 +988,8 @@ ossl_cmp_pollrepcontent_get0_pollrep(const OSSL_CMP_POLLREPCONTENT *prc,
OSSL_CMP_CERTRESPONSE *
ossl_cmp_certrepmessage_get0_certresponse(const OSSL_CMP_CERTREPMESSAGE *crm,
int rid);
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_CERTRESPONSE *crep);
X509 *ossl_cmp_certresponse_get1_cert_key(const OSSL_CMP_CERTRESPONSE *crep,
const OSSL_CMP_CTX *ctx, EVP_PKEY *pkey);
OSSL_CMP_MSG *ossl_cmp_msg_load(const char *file);
int ossl_cmp_is_error_with_waiting(const OSSL_CMP_MSG *msg);

Expand Down
56 changes: 46 additions & 10 deletions crypto/cmp/cmp_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ static const X509_NAME *determine_subj(OSSL_CMP_CTX *ctx, int for_KUR,
OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
{
OSSL_CRMF_MSG *crm = NULL;
int central_keygen = OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_POPO_METHOD)
== OSSL_CRMF_POPO_NONE;
X509 *refcert = ctx->oldCert != NULL ? ctx->oldCert : ctx->cert;
/* refcert defaults to current client cert */
EVP_PKEY *rkey = ossl_cmp_ctx_get0_newPubkey(ctx);
Expand All @@ -283,6 +285,7 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
: X509_get_issuer_name(refcert);
int crit = ctx->setSubjectAltNameCritical || subject == NULL;
/* RFC5280: subjectAltName MUST be critical if subject is null */
OSSL_CRMF_CERTTEMPLATE *tmpl;
X509_EXTENSIONS *exts = NULL;

if (rkey == NULL) {
Expand All @@ -297,6 +300,7 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
}
if ((crm = OSSL_CRMF_MSG_new()) == NULL)
return NULL;
tmpl = OSSL_CRMF_MSG_get0_tmpl(crm);
if (!OSSL_CRMF_MSG_set_certReqId(crm, rid)
/*
* fill certTemplate, corresponding to CertificationRequestInfo
Expand All @@ -306,6 +310,10 @@ OSSL_CRMF_MSG *OSSL_CMP_CTX_setup_CRM(OSSL_CMP_CTX *ctx, int for_KUR, int rid)
|| !OSSL_CRMF_CERTTEMPLATE_fill(OSSL_CRMF_MSG_get0_tmpl(crm), rkey,
subject, issuer, NULL /* serial */))
goto err;
if (rkey != NULL && central_keygen)
X509_PUBKEY_set0_public_key(OSSL_CRMF_CERTTEMPLATE_get0_publicKey(tmpl),
NULL, 0);

if (ctx->days != 0) {
time_t now = time(NULL);
ASN1_TIME *notBefore = ASN1_TIME_adj(NULL, now, 0, 0);
Expand Down Expand Up @@ -1043,22 +1051,51 @@ ossl_cmp_certrepmessage_get0_certresponse(const OSSL_CMP_CERTREPMESSAGE *crm,
}

/*-
* Retrieve the newly enrolled certificate from the given certResponse crep.
* Uses libctx and propq from ctx, in case of indirect POPO also private key.
* Retrieve newly enrolled certificate and key from the given certResponse crep.
* In case of indirect POPO uses the libctx and propq from ctx and private key.
* In case of central key generation, updates ctx->newPkey.
* Returns a pointer to a copy of the found certificate, or NULL if not found.
*/
X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
const OSSL_CMP_CERTRESPONSE *crep)
X509 *ossl_cmp_certresponse_get1_cert_key(const OSSL_CMP_CERTRESPONSE *crep,
const OSSL_CMP_CTX *ctx, EVP_PKEY *pkey)
{
OSSL_CMP_CERTORENCCERT *coec;
X509 *crt = NULL;
EVP_PKEY *pkey;
OSSL_CRMF_ENCRYPTEDKEY *encr_key;
int central_keygen = OSSL_CMP_CTX_get_option(ctx, OSSL_CMP_OPT_POPO_METHOD)
== OSSL_CRMF_POPO_NONE;

if (crep->certifiedKeyPair == NULL) {
ERR_raise(ERR_LIB_CMP, CMP_R_CERTIFICATE_NOT_FOUND);
return NULL;
}
encr_key = crep->certifiedKeyPair->privateKey;
if (encr_key == NULL && central_keygen) {
ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_CENTRAL_GEN_KEY);
return NULL;
}
if (encr_key != NULL) {
if (!central_keygen) {
ERR_raise(ERR_LIB_CMP, CMP_R_UNEXPECTED_CENTRAL_GEN_KEY);
return NULL;
}
/* found encrypted private key, try to extract */
pkey = OSSL_CRMF_ENCRYPTEDKEY_get1_pkey(encr_key, ctx->trusted,
ctx->untrusted,
ctx->pkey, ctx->cert,
ctx->secretValue,
ctx->libctx, ctx->propq);
if (pkey == NULL) {
ERR_raise(ERR_LIB_CMP, CMP_R_FAILED_EXTRACTING_CENTRAL_GEN_KEY);
return NULL;
}
OSSL_CMP_CTX_set0_newPkey((OSSL_CMP_CTX *)ctx, 1, pkey);
}

if (!ossl_assert(crep != NULL && ctx != NULL))
return NULL;

if (crep->certifiedKeyPair
&& (coec = crep->certifiedKeyPair->certOrEncCert) != NULL) {
if ((coec = crep->certifiedKeyPair->certOrEncCert) != NULL) {
switch (coec->type) {
case OSSL_CMP_CERTORENCCERT_CERTIFICATE:
crt = X509_dup(coec->value.certificate);
Expand All @@ -1071,10 +1108,9 @@ X509 *ossl_cmp_certresponse_get1_cert(const OSSL_CMP_CTX *ctx,
ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PRIVATE_KEY);
return NULL;
}
crt =
OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(coec->value.encryptedCert,
crt = OSSL_CRMF_ENCRYPTEDKEY_get1_encCert(coec->value.encryptedCert,
ctx->libctx, ctx->propq,
pkey);
pkey, 0);
break;
default:
ERR_raise(ERR_LIB_CMP, CMP_R_UNKNOWN_CERT_TYPE);
Expand Down
3 changes: 2 additions & 1 deletion crypto/cmp/cmp_vfy.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,11 @@ static int check_cert_path_3gpp(const OSSL_CMP_CTX *ctx,
* verify that the newly enrolled certificate (which assumed rid ==
* OSSL_CMP_CERTREQID) can also be validated with the same trusted store
*/
EVP_PKEY *pkey = OSSL_CMP_CTX_get0_newPkey(ctx, 1);
OSSL_CMP_CERTRESPONSE *crep =
ossl_cmp_certrepmessage_get0_certresponse(msg->body->value.ip,
OSSL_CMP_CERTREQID);
X509 *newcrt = ossl_cmp_certresponse_get1_cert(ctx, crep);
X509 *newcrt = ossl_cmp_certresponse_get1_cert_key(crep, ctx, pkey);

/*
* maybe better use get_cert_status() from cmp_client.c, which catches
Expand Down
13 changes: 13 additions & 0 deletions crypto/crmf/crmf_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ ASN1_SEQUENCE(OSSL_CRMF_ENCRYPTEDVALUE) = {
} ASN1_SEQUENCE_END(OSSL_CRMF_ENCRYPTEDVALUE)
IMPLEMENT_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDVALUE)

/*
* Note from CMP Updates defining CMPv3:
* The EncryptedKey structure defined in CRMF [RFC4211] is reused
* here, which makes the update backward compatible. Using the new
* syntax with the untagged default choice EncryptedValue is bits-on-
* the-wire compatible with the old syntax.
*/
ASN1_CHOICE(OSSL_CRMF_ENCRYPTEDKEY) = {
ASN1_SIMPLE(OSSL_CRMF_ENCRYPTEDKEY, value.encryptedValue, OSSL_CRMF_ENCRYPTEDVALUE),
ASN1_IMP(OSSL_CRMF_ENCRYPTEDKEY, value.envelopedData, CMS_EnvelopedData, 0),
} ASN1_CHOICE_END(OSSL_CRMF_ENCRYPTEDKEY)
IMPLEMENT_ASN1_FUNCTIONS(OSSL_CRMF_ENCRYPTEDKEY)

ASN1_SEQUENCE(OSSL_CRMF_SINGLEPUBINFO) = {
ASN1_SIMPLE(OSSL_CRMF_SINGLEPUBINFO, pubMethod, ASN1_INTEGER),
ASN1_SIMPLE(OSSL_CRMF_SINGLEPUBINFO, pubLocation, GENERAL_NAME)
Expand Down
Loading

0 comments on commit cd97785

Please sign in to comment.