From 252487dfe45cc7af6ed6074d024259dd2e9d93e0 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Mon, 1 Jul 2019 21:38:27 -0500 Subject: [PATCH] hx509: support reading private keys from PEM files This commit adds: - hx509_cert_init_private_key() for creating an hx509_cert object that has just a private key - hx509_cert_have_private_key_only() for checking whether an hx509_cert object has just a private key This also generalizes the get_key() internal function in hxtool, which is tasked with reding or generating a private key for use in signing CSRs. Now hxtool request-create can read/write private keys to/from PEM files, not just DER files. This is needed to support key types other than just RSA for CSRs and certificates. --- lib/hx509/cert.c | 91 ++++++++++++++++++++++++++++------ lib/hx509/ks_file.c | 25 +++++++--- lib/hx509/libhx509-exports.def | 3 ++ lib/hx509/version-script.map | 3 ++ 4 files changed, 99 insertions(+), 23 deletions(-) diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c index c75f5e90c..107d405b6 100644 --- a/lib/hx509/cert.c +++ b/lib/hx509/cert.c @@ -212,6 +212,29 @@ _hx509_cert_get_version(const Certificate *t) return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1; } +static hx509_cert +cert_init(hx509_context context, heim_error_t *error) +{ + hx509_cert cert; + + cert = malloc(sizeof(*cert)); + if (cert == NULL) { + if (error) + *error = heim_error_create_enomem(); + return NULL; + } + cert->ref = 1; + cert->friendlyname = NULL; + cert->attrs.len = 0; + cert->attrs.val = NULL; + cert->private_key = NULL; + cert->basename = NULL; + cert->release = NULL; + cert->ctx = NULL; + cert->data= NULL; + return cert; +} + /** * Allocate and init an hx509 certificate object from the decoded * certificate `c´. @@ -231,20 +254,8 @@ hx509_cert_init(hx509_context context, const Certificate *c, heim_error_t *error hx509_cert cert; int ret; - cert = malloc(sizeof(*cert)); - if (cert == NULL) { - if (error) - *error = heim_error_create_enomem(); - return NULL; - } - cert->ref = 1; - cert->friendlyname = NULL; - cert->attrs.len = 0; - cert->attrs.val = NULL; - cert->private_key = NULL; - cert->basename = NULL; - cert->release = NULL; - cert->ctx = NULL; + if ((cert = cert_init(context, error)) == NULL) + return NULL; cert->data = calloc(1, sizeof(*(cert->data))); if (cert->data == NULL) { @@ -262,6 +273,31 @@ hx509_cert_init(hx509_context context, const Certificate *c, heim_error_t *error return cert; } +/** + * Allocate and init an hx509 certificate object containing only a private key + * (but no Certificate). + * + * @param context A hx509 context. + * @param key + * @param error + * + * @return Returns an hx509 certificate + * + * @ingroup hx509_cert + */ + +HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL +hx509_cert_init_private_key(hx509_context context, + hx509_private_key key, + heim_error_t *error) +{ + hx509_cert cert; + + if ((cert = cert_init(context, error))) + (void) _hx509_cert_assign_key(cert, key); + return cert; +} + /** * Just like hx509_cert_init(), but instead of a decode certificate * takes an pointer and length to a memory region that contains a @@ -360,7 +396,8 @@ hx509_cert_free(hx509_cert cert) if (cert->private_key) hx509_private_key_free(&cert->private_key); - free_Certificate(cert->data); + if (cert->data) + free_Certificate(cert->data); free(cert->data); for (i = 0; i < cert->attrs.len; i++) { @@ -1598,12 +1635,36 @@ _hx509_cert_private_key(hx509_cert p) return p->private_key; } +/** + * Indicate whether a hx509_cert has a private key. + * + * @param p a hx509 certificate + * + * @return 1 if p has a private key, 0 otherwise. + * + * @ingroup hx509_cert + */ HX509_LIB_FUNCTION int HX509_LIB_CALL hx509_cert_have_private_key(hx509_cert p) { return p->private_key ? 1 : 0; } +/** + * Indicate whether a hx509_cert has a private key only (no certificate). + * + * @param p a hx509 certificate + * + * @return 1 if p has a private key only (no certificate), 0 otherwise. + * + * @ingroup hx509_cert + */ +HX509_LIB_FUNCTION int HX509_LIB_CALL +hx509_cert_have_private_key_only(hx509_cert p) +{ + return p->private_key && !p->data ? 1 : 0; +} + HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_cert_private_key_exportable(hx509_cert p) diff --git a/lib/hx509/ks_file.c b/lib/hx509/ks_file.c index 9add061d7..ee93537f0 100644 --- a/lib/hx509/ks_file.c +++ b/lib/hx509/ks_file.c @@ -535,19 +535,28 @@ store_func(hx509_context context, void *ctx, hx509_cert c) heim_octet_string data; int ret; - ret = hx509_cert_binary(context, c, &data); - if (ret) - return ret; + if (hx509_cert_have_private_key_only(c)) { + data.length = 0; + data.data = NULL; + } else { + ret = hx509_cert_binary(context, c, &data); + if (ret) + return ret; + } switch (sc->format) { case USE_DER: - fwrite(data.data, data.length, 1, sc->f); - free(data.data); + if (data.data) { + fwrite(data.data, data.length, 1, sc->f); + free(data.data); + } /* XXX else write private key instead */ break; case USE_PEM: - hx509_pem_write(context, "CERTIFICATE", NULL, sc->f, - data.data, data.length); - free(data.data); + if (data.data) { + hx509_pem_write(context, "CERTIFICATE", NULL, sc->f, + data.data, data.length); + free(data.data); + } if (_hx509_cert_private_key_exportable(c)) { hx509_private_key key = _hx509_cert_private_key(c); ret = _hx509_private_key_export(context, key, diff --git a/lib/hx509/libhx509-exports.def b/lib/hx509/libhx509-exports.def index 53c28eaaf..acdcdaeac 100644 --- a/lib/hx509/libhx509-exports.def +++ b/lib/hx509/libhx509-exports.def @@ -80,8 +80,11 @@ EXPORTS hx509_cert_get_notBefore hx509_cert_get_serialnumber hx509_cert_get_subject + hx509_cert_have_private_key + hx509_cert_have_private_key_only hx509_cert_init hx509_cert_init_data + hx509_cert_init_private_key hx509_cert_keyusage_print hx509_cert_ref hx509_cert_set_friendly_name diff --git a/lib/hx509/version-script.map b/lib/hx509/version-script.map index 50fb146b4..995281d56 100644 --- a/lib/hx509/version-script.map +++ b/lib/hx509/version-script.map @@ -74,8 +74,11 @@ HEIMDAL_X509_1.2 { hx509_cert_get_subject; hx509_cert_get_issuer_unique_id; hx509_cert_get_subject_unique_id; + hx509_cert_have_private_key; + hx509_cert_have_private_key_only; hx509_cert_init; hx509_cert_init_data; + hx509_cert_init_private_key; hx509_cert_keyusage_print; hx509_cert_public_encrypt; hx509_cert_ref;