hx509: private key exclusion options
authorNicolas Williams <nico@twosigma.com>
Fri, 6 Dec 2019 23:11:01 +0000 (17:11 -0600)
committerNicolas Williams <nico@twosigma.com>
Tue, 10 Dec 2019 00:10:10 +0000 (18:10 -0600)
Add two ways to exclude private keys when dealing with an hx509
certificate store.  One as a load option (load no private keys, never
add private keys), one as a store option (store no private keys).

This is useful for CA code so it can have a single store with the
issuer's credentials _and_ the chain for it, and copy those to a store
with the issued certificate and _not_ accidentally include the issuer's
private key.

It would be much safer still to flip the default for this flag, but that
could break out-of-tree libhx509 dependents.

lib/hx509/cert.c
lib/hx509/hx509.h
lib/hx509/hxtool-commands.in
lib/hx509/hxtool.c
lib/hx509/keyset.c
lib/hx509/ks_file.c
lib/hx509/ks_keychain.c
lib/hx509/ks_p11.c
lib/hx509/ks_p12.c

index dc5444d0a3f2556bbd1426261c2373590120992b..598549cea9c97a60d462d26765c3ab3fa2ce1657 100644 (file)
@@ -300,6 +300,26 @@ hx509_cert_init(hx509_context context, const Certificate *c, heim_error_t *error
     return cert;
 }
 
+/**
+ * Copy a certificate object, but drop any private key assignment.
+ *
+ * @param context A hx509 context.
+ * @param src Certificate object
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+HX509_LIB_FUNCTION hx509_cert HX509_LIB_CALL
+hx509_cert_copy_no_private_key(hx509_context context,
+                               hx509_cert src,
+                               heim_error_t *error)
+{
+    return hx509_cert_init(context, src->data, error);
+}
+
 /**
  * Allocate and init an hx509 certificate object containing only a private key
  * (but no Certificate).
index e8158e4d71c54be1e95958739897416892606b7c..778a02ba4b0c557cfaf8255b95d749d8daeb044c 100644 (file)
@@ -154,6 +154,11 @@ typedef enum {
 /* flags to hx509_certs_init */
 #define HX509_CERTS_CREATE                             0x01
 #define HX509_CERTS_UNPROTECT_ALL                      0x02
+#define HX509_CERTS_NO_PRIVATE_KEYS                    0x04
+
+/* flags to hx509_certs_store */
+#define HX509_CERTS_STORE_NO_PRIVATE_KEYS              0x04
+
 
 /* flags to hx509_set_error_string */
 #define HX509_ERROR_APPEND                             0x01
index a75d539c325bd5058d16b55d96c4a99df94ddb2e..556b1888eb8cb2f55aad73785bdb58dae3aaaefc 100644 (file)
@@ -996,6 +996,16 @@ command = {
                 argument = "datetime"
                 help = "check that the certificate's notBefore is after the given time"
         }
+        option = {
+                long = "has-private-key"
+                type = "flag"
+                help = "check that the certificate has a private key"
+        }
+        option = {
+                long = "lacks-private-key"
+                type = "flag"
+                help = "check that the certificate does not have a private key"
+        }
         name = "acert"
         min_args = "1"
         max_args = "1"
index d9c315c345656da27015ba4d6ce917bb08973bb5..ac7ba09606512b06f137cb9a00f9db2209c400dc 100644 (file)
@@ -2677,6 +2677,7 @@ acert1_validity(struct acert_options *opt, hx509_cert cert)
     time_t not_after_eq = 0;
     time_t not_after_lt = 0;
     time_t not_after_gt = 0;
+    int ret = 0;
 
     if (opt->valid_now_flag) {
         time_t now = time(NULL);
@@ -2684,12 +2685,12 @@ acert1_validity(struct acert_options *opt, hx509_cert cert)
         if (hx509_cert_get_notBefore(cert) > now) {
             if (opt->verbose_flag)
                 fprintf(stderr, "Certificate not valid yet\n");
-            return -1;
+            ret = -1;
         }
         if (hx509_cert_get_notAfter(cert) < now) {
             if (opt->verbose_flag)
                 fprintf(stderr, "Certificate currently expired\n");
-            return -1;
+            ret = -1;
         }
     }
     if (opt->valid_at_string) {
@@ -2699,13 +2700,13 @@ acert1_validity(struct acert_options *opt, hx509_cert cert)
             if (opt->verbose_flag)
                 fprintf(stderr, "Certificate not valid yet at %s\n",
                         opt->valid_at_string);
-            return -1;
+            ret = -1;
         }
         if (hx509_cert_get_notAfter(cert) < at) {
             if (opt->verbose_flag)
                 fprintf(stderr, "Certificate expired before %s\n",
                         opt->valid_at_string);
-            return -1;
+            ret = -1;
         }
     }
 
@@ -2727,17 +2728,29 @@ acert1_validity(struct acert_options *opt, hx509_cert cert)
         (not_before_gt && hx509_cert_get_notBefore(cert) <= not_before_gt)) {
         if (opt->verbose_flag)
             fprintf(stderr, "Certificate notBefore not as requested\n");
-        return -1;
+        ret = -1;
     }
     if ((not_after_eq && hx509_cert_get_notAfter(cert) != not_after_eq) ||
         (not_after_lt && hx509_cert_get_notAfter(cert) >= not_after_lt) ||
         (not_after_gt && hx509_cert_get_notAfter(cert) <= not_after_gt)) {
         if (opt->verbose_flag)
             fprintf(stderr, "Certificate notAfter not as requested\n");
-        return -1;
+        ret = -1;
     }
 
-    return 0;
+    if (opt->has_private_key_flag && !hx509_cert_have_private_key(cert)) {
+        if (opt->verbose_flag)
+            fprintf(stderr, "Certificate does not have a private key\n");
+        ret = -1;
+    }
+
+    if (opt->lacks_private_key_flag && hx509_cert_have_private_key(cert)) {
+        if (opt->verbose_flag)
+            fprintf(stderr, "Certificate does not have a private key\n");
+        ret = -1;
+    }
+
+    return ret;
 }
 
 static int
@@ -2810,7 +2823,7 @@ acert1(struct acert_options *opt, size_t cert_num, hx509_cert cert, int *matched
     if (e == NULL) {
         if (wanted)
             return -1;
-        return acert1_validity(opt, cert);;
+        return acert1_validity(opt, cert);
     }
 
     for (i = 0; i < e->len; i++) {
index f89dfbef3ab3a1198486a89244f033db4f10b4f6..0b852dc518a5ab06bcc68df896b1c651ad2cda38 100644 (file)
@@ -63,6 +63,7 @@ struct hx509_certs_data {
     unsigned int ref;
     struct hx509_keyset_ops *ops;
     void *ops_data;
+    int flags;
 };
 
 static struct hx509_keyset_ops *
@@ -103,6 +104,7 @@ _hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)
  * @param flags list of flags:
  * - HX509_CERTS_CREATE create a new keystore of the specific TYPE.
  * - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.
+ * - HX509_CERTS_NO_PRIVATE_KEYS does not load or permit adding private keys
  * @param lock a lock that unlocks the certificates store, use NULL to
  * select no password/certifictes/prompt lock (see @ref page_lock).
  * @param certs return pointer, free with hx509_certs_free().
@@ -158,6 +160,7 @@ hx509_certs_init(hx509_context context,
        hx509_clear_error_string(context);
        return ENOMEM;
     }
+    c->flags = flags;
     c->ops = ops;
     c->ref = 1;
 
@@ -201,9 +204,12 @@ hx509_certs_destroy(hx509_context context,
 /**
  * Write the certificate store to stable storage.
  *
+ * Use the HX509_CERTS_STORE_NO_PRIVATE_KEYS flag to ensure that no private
+ * keys are stored, even if added.
+ *
  * @param context A hx509 context.
  * @param certs a certificate store to store.
- * @param flags currently unused, use 0.
+ * @param flags currently one flag is defined: HX509_CERTS_STORE_NO_PRIVATE_KEYS
  * @param lock a lock that unlocks the certificates store, use NULL to
  * select no password/certifictes/prompt lock (see @ref page_lock).
  *
@@ -485,6 +491,9 @@ hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c)
 HX509_LIB_FUNCTION int HX509_LIB_CALL
 hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
 {
+    hx509_cert copy = NULL;
+    int ret;
+
     if (certs->ops->add == NULL) {
        hx509_set_error_string(context, 0, ENOENT,
                               "Keyset type %s doesn't support add operation",
@@ -492,7 +501,20 @@ hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
        return ENOENT;
     }
 
-    return (*certs->ops->add)(context, certs, certs->ops_data, cert);
+    if ((certs->flags & HX509_CERTS_NO_PRIVATE_KEYS) &&
+        hx509_cert_have_private_key(cert)) {
+        if ((copy = hx509_cert_copy_no_private_key(context, cert,
+                                                   NULL)) == NULL) {
+            hx509_set_error_string(context, 0, ENOMEM,
+                                   "Could not add certificate to store");
+            return ENOMEM;
+        }
+        cert = copy;
+    }
+
+    ret = (*certs->ops->add)(context, certs, certs->ops_data, cert);
+    hx509_cert_free(copy);
+    return ret;
 }
 
 /**
@@ -637,8 +659,7 @@ certs_merge_func(hx509_context context, void *ctx, hx509_cert c)
 }
 
 /**
- * Merge a certificate store into another. The from store is keep
- * intact.
+ * Merge one certificate store into another. The from store is kept intact.
  *
  * @param context a hx509 context.
  * @param to the store to merge into.
index 886038eb97972b6c54cb066399684b4791d7721b..d406946d89c827861f89befaf289f6f8229e1d20 100644 (file)
@@ -49,7 +49,7 @@ struct ks_file {
  */
 
 static int
-parse_certificate(hx509_context context, const char *fn,
+parse_certificate(hx509_context context, const char *fn, int flags,
                  struct hx509_collector *c,
                  const hx509_pem_header *headers,
                  const void *data, size_t len,
@@ -74,6 +74,7 @@ parse_certificate(hx509_context context, const char *fn,
 static int
 try_decrypt(hx509_context context,
            struct hx509_collector *collector,
+            int flags,
            const AlgorithmIdentifier *alg,
            const EVP_CIPHER *c,
            const void *ivdata,
@@ -122,12 +123,9 @@ try_decrypt(hx509_context context,
        EVP_CIPHER_CTX_cleanup(&ctx);
     }
 
-    ret = _hx509_collector_private_key_add(context,
-                                          collector,
-                                          alg,
-                                          NULL,
-                                          &clear,
-                                          NULL);
+    if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS))
+        ret = _hx509_collector_private_key_add(context, collector, alg, NULL,
+                                               &clear, NULL);
 
     memset_s(clear.data, clear.length, 0, clear.length);
     free(clear.data);
@@ -138,7 +136,7 @@ out:
 }
 
 static int
-parse_pkcs8_private_key(hx509_context context, const char *fn,
+parse_pkcs8_private_key(hx509_context context, const char *fn, int flags,
                        struct hx509_collector *c,
                        const hx509_pem_header *headers,
                        const void *data, size_t length,
@@ -146,28 +144,28 @@ parse_pkcs8_private_key(hx509_context context, const char *fn,
 {
     PKCS8PrivateKeyInfo ki;
     heim_octet_string keydata;
-
     int ret;
 
     ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
     if (ret)
        return ret;
 
-    keydata.data = rk_UNCONST(data);
-    keydata.length = length;
-
-    ret = _hx509_collector_private_key_add(context,
-                                          c,
-                                          &ki.privateKeyAlgorithm,
-                                          NULL,
-                                          &ki.privateKey,
-                                          &keydata);
+    if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
+        keydata.data = rk_UNCONST(data);
+        keydata.length = length;
+        ret = _hx509_collector_private_key_add(context,
+                                               c,
+                                               &ki.privateKeyAlgorithm,
+                                               NULL,
+                                               &ki.privateKey,
+                                               &keydata);
+    }
     free_PKCS8PrivateKeyInfo(&ki);
     return ret;
 }
 
 static int
-parse_pem_private_key(hx509_context context, const char *fn,
+parse_pem_private_key(hx509_context context, const char *fn, int flags,
                      struct hx509_collector *c,
                      const hx509_pem_header *headers,
                      const void *data, size_t len,
@@ -271,7 +269,7 @@ parse_pem_private_key(hx509_context context, const char *fn,
                password = pw->val[i];
                passwordlen = strlen(password);
 
-               ret = try_decrypt(context, c, ai, cipher, ivdata,
+               ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
                                  password, passwordlen, data, len);
                if (ret == 0) {
                    decrypted = 1;
@@ -292,21 +290,21 @@ parse_pem_private_key(hx509_context context, const char *fn,
 
            ret = hx509_lock_prompt(lock, &prompt);
            if (ret == 0)
-               ret = try_decrypt(context, c, ai, cipher, ivdata, password,
-                                 strlen(password), data, len);
+                ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
+                                  password, strlen(password), data, len);
            /* XXX add password to lock password collection ? */
            memset_s(password, sizeof(password), 0, sizeof(password));
        }
        free(ivdata);
 
-    } else {
+    } else if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
        heim_octet_string keydata;
 
        keydata.data = rk_UNCONST(data);
        keydata.length = len;
 
-       ret = _hx509_collector_private_key_add(context, c, ai, NULL,
-                                              &keydata, NULL);
+        ret = _hx509_collector_private_key_add(context, c, ai, NULL,
+                                               &keydata, NULL);
     }
 
     return ret;
@@ -315,7 +313,7 @@ parse_pem_private_key(hx509_context context, const char *fn,
 
 struct pem_formats {
     const char *name;
-    int (*func)(hx509_context, const char *, struct hx509_collector *,
+    int (*func)(hx509_context, const char *, int, struct hx509_collector *,
                const hx509_pem_header *, const void *, size_t,
                const AlgorithmIdentifier *);
     const AlgorithmIdentifier *(*ai)(void);
@@ -347,11 +345,12 @@ pem_func(hx509_context context, const char *type,
        const char *q = formats[j].name;
        if (strcasecmp(type, q) == 0) {
            const AlgorithmIdentifier *ai = NULL;
+
            if (formats[j].ai != NULL)
                ai = (*formats[j].ai)();
 
-           ret = (*formats[j].func)(context, NULL, pem_ctx->c,
-                                    header, data, len, ai);
+            ret = (*formats[j].func)(context, NULL, pem_ctx->flags, pem_ctx->c,
+                                     header, data, len, ai);
            if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
                hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
                                       "Failed parseing PEM format %s", type);
@@ -468,10 +467,12 @@ file_init_common(hx509_context context,
 
            for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
                const AlgorithmIdentifier *ai = NULL;
+
                if (formats[i].ai != NULL)
                    ai = (*formats[i].ai)();
 
-               ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai);
+                ret = (*formats[i].func)(context, p, pem_ctx.flags, pem_ctx.c,
+                                         NULL, ptr, length, ai);
                if (ret == 0)
                    break;
            }
@@ -539,6 +540,7 @@ file_free(hx509_certs certs, void *data)
 struct store_ctx {
     FILE *f;
     outformat format;
+    int store_flags;
 };
 
 static int HX509_LIB_CALL
@@ -563,7 +565,8 @@ store_func(hx509_context context, void *ctx, hx509_cert c)
         if (data.data) {
             fwrite(data.data, data.length, 1, sc->f);
             free(data.data);
-        } else if (_hx509_cert_private_key_exportable(c)) {
+        } else if (_hx509_cert_private_key_exportable(c) &&
+                   !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
             hx509_private_key key = _hx509_cert_private_key(c);
 
             ret = _hx509_private_key_export(context, key,
@@ -573,7 +576,8 @@ store_func(hx509_context context, void *ctx, hx509_cert c)
         }
        break;
     case USE_PEM:
-       if (_hx509_cert_private_key_exportable(c)) {
+       if (_hx509_cert_private_key_exportable(c) &&
+            !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
             heim_octet_string priv_key;
            hx509_private_key key = _hx509_cert_private_key(c);
 
@@ -658,6 +662,7 @@ file_store(hx509_context context,
        return ret;
     }
     rk_cloexec_file(sc.f);
+    sc.store_flags = flags;
     sc.format = ksf->format;
 
     ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
index 44dca03a3673a6b20717a5b71e591a0bbdefcb5b..3243ee8b26c3ddb9d685d430602280b40f587efa 100644 (file)
@@ -328,6 +328,13 @@ keychain_init(hx509_context context,
 {
     struct ks_keychain *ctx;
 
+    if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+        hx509_set_error_string(context, 0, ENOTSUP,
+                               "KEYCHAIN store does not support not reading "
+                               "private keys");
+        return ENOTSUP;
+    }
+
     ctx = calloc(1, sizeof(*ctx));
     if (ctx == NULL) {
        hx509_clear_error_string(context);
index 1826282b2cbae500922a82534a17ff505487d243..289fb4ecc8a757d4eac338dc4d31afac835fe88f 100644 (file)
@@ -820,6 +820,13 @@ p11_init(hx509_context context,
 
     *data = NULL;
 
+    if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
+       hx509_set_error_string(context, 0, ENOTSUP,
+                              "PKCS#11 store does not support "
+                               "HX509_CERTS_NO_PRIVATE_KEYS flag");
+        return ENOTSUP;
+    }
+
     if (residue == NULL || residue[0] == '\0') {
        hx509_set_error_string(context, 0, EINVAL,
                               "PKCS#11 store not specified");
index 2838e812906632ca4973a2a3b2c445fb66926845..6fd7cd1052695c98110c83acc6dc439f6b46127a 100644 (file)
 struct ks_pkcs12 {
     hx509_certs certs;
     char *fn;
+    unsigned int store_no_priv_keys;
 };
 
 typedef int (*collector_func)(hx509_context,
                              struct hx509_collector *,
+                              int,
                              const void *, size_t,
                              const PKCS12_Attributes *);
 
@@ -49,8 +51,9 @@ struct type {
 };
 
 static void
-parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *,
-                 const void *, size_t, const PKCS12_Attributes *);
+parse_pkcs12_type(hx509_context, struct hx509_collector *, int,
+                  const heim_oid *, const void *, size_t,
+                  const PKCS12_Attributes *);
 
 
 static const PKCS12_Attribute *
@@ -68,6 +71,7 @@ find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid)
 static int
 keyBag_parser(hx509_context context,
              struct hx509_collector *c,
+              int flags,
              const void *data, size_t length,
              const PKCS12_Attributes *attrs)
 {
@@ -76,6 +80,9 @@ keyBag_parser(hx509_context context,
     const heim_octet_string *os = NULL;
     int ret;
 
+    if (flags & HX509_CERTS_NO_PRIVATE_KEYS)
+        return 0;
+
     attr = find_attribute(attrs, &asn1_oid_id_pkcs_9_at_localKeyId);
     if (attr)
        os = &attr->attrValues;
@@ -97,6 +104,7 @@ keyBag_parser(hx509_context context,
 static int
 ShroudedKeyBag_parser(hx509_context context,
                      struct hx509_collector *c,
+                      int flags,
                      const void *data, size_t length,
                      const PKCS12_Attributes *attrs)
 {
@@ -119,7 +127,8 @@ ShroudedKeyBag_parser(hx509_context context,
     if (ret)
        return ret;
 
-    ret = keyBag_parser(context, c, content.data, content.length, attrs);
+    ret = keyBag_parser(context, c, flags, content.data, content.length,
+                        attrs);
     der_free_octet_string(&content);
     return ret;
 }
@@ -127,6 +136,7 @@ ShroudedKeyBag_parser(hx509_context context,
 static int
 certBag_parser(hx509_context context,
               struct hx509_collector *c,
+               int flags,
               const void *data, size_t length,
               const PKCS12_Attributes *attrs)
 {
@@ -191,6 +201,7 @@ certBag_parser(hx509_context context,
 static int
 parse_safe_content(hx509_context context,
                   struct hx509_collector *c,
+                   int flags,
                   const unsigned char *p, size_t len)
 {
     PKCS12_SafeContents sc;
@@ -206,6 +217,7 @@ parse_safe_content(hx509_context context,
     for (i = 0; i < sc.len ; i++)
        parse_pkcs12_type(context,
                          c,
+                          flags,
                          &sc.val[i].bagId,
                          sc.val[i].bagValue.data,
                          sc.val[i].bagValue.length,
@@ -218,6 +230,7 @@ parse_safe_content(hx509_context context,
 static int
 safeContent_parser(hx509_context context,
                   struct hx509_collector *c,
+                   int flags,
                   const void *data, size_t length,
                   const PKCS12_Attributes *attrs)
 {
@@ -227,7 +240,7 @@ safeContent_parser(hx509_context context,
     ret = decode_PKCS12_OctetString(data, length, &os, NULL);
     if (ret)
        return ret;
-    ret = parse_safe_content(context, c, os.data, os.length);
+    ret = parse_safe_content(context, c, flags, os.data, os.length);
     der_free_octet_string(&os);
     return ret;
 }
@@ -235,6 +248,7 @@ safeContent_parser(hx509_context context,
 static int
 encryptedData_parser(hx509_context context,
                     struct hx509_collector *c,
+                     int flags,
                     const void *data, size_t length,
                     const PKCS12_Attributes *attrs)
 {
@@ -253,7 +267,8 @@ encryptedData_parser(hx509_context context,
        return ret;
 
     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
-       ret = parse_safe_content(context, c, content.data, content.length);
+       ret = parse_safe_content(context, c, flags,
+                                 content.data, content.length);
 
     der_free_octet_string(&content);
     der_free_oid(&contentType);
@@ -263,6 +278,7 @@ encryptedData_parser(hx509_context context,
 static int
 envelopedData_parser(hx509_context context,
                     struct hx509_collector *c,
+                     int flags,
                     const void *data, size_t length,
                     const PKCS12_Attributes *attrs)
 {
@@ -290,7 +306,8 @@ envelopedData_parser(hx509_context context,
     }
 
     if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) == 0)
-       ret = parse_safe_content(context, c, content.data, content.length);
+       ret = parse_safe_content(context, c, flags,
+                                 content.data, content.length);
 
     der_free_octet_string(&content);
     der_free_oid(&contentType);
@@ -311,6 +328,7 @@ struct type bagtypes[] = {
 static void
 parse_pkcs12_type(hx509_context context,
                  struct hx509_collector *c,
+                  int flags,
                  const heim_oid *oid,
                  const void *data, size_t length,
                  const PKCS12_Attributes *attrs)
@@ -319,7 +337,7 @@ parse_pkcs12_type(hx509_context context,
 
     for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
        if (der_heim_oid_cmp(bagtypes[i].oid, oid) == 0)
-           (*bagtypes[i].func)(context, c, data, length, attrs);
+           (*bagtypes[i].func)(context, c, flags, data, length, attrs);
 }
 
 static int
@@ -429,6 +447,7 @@ p12_init(hx509_context context,
     for (i = 0; i < as.len; i++)
        parse_pkcs12_type(context,
                          c,
+                          flags,
                          &as.val[i].contentType,
                          as.val[i].content->data,
                          as.val[i].content->length,
@@ -492,10 +511,15 @@ addBag(hx509_context context,
     return 0;
 }
 
+struct store_func_ctx {
+    PKCS12_AuthenticatedSafe as;
+    int store_flags;
+};
+
 static int HX509_LIB_CALL
-store_func(hx509_context context, void *ctx, hx509_cert c)
+store_func(hx509_context context, void *d, hx509_cert c)
 {
-    PKCS12_AuthenticatedSafe *as = ctx;
+    struct store_func_ctx *ctx = d;
     PKCS12_OctetString os;
     PKCS12_CertBag cb;
     size_t size;
@@ -528,9 +552,11 @@ store_func(hx509_context context, void *ctx, hx509_cert c)
     if (ret)
        goto out;
 
-    ret = addBag(context, as, &asn1_oid_id_pkcs12_certBag, os.data, os.length);
+    ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_certBag, os.data,
+                 os.length);
 
-    if (_hx509_cert_private_key_exportable(c)) {
+    if (_hx509_cert_private_key_exportable(c) &&
+        !(ctx->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
        hx509_private_key key = _hx509_cert_private_key(c);
        PKCS8PrivateKeyInfo pki;
 
@@ -561,7 +587,8 @@ store_func(hx509_context context, void *ctx, hx509_cert c)
        if (ret)
            return ret;
 
-       ret = addBag(context, as, &asn1_oid_id_pkcs12_keyBag, os.data, os.length);
+        ret = addBag(context, &ctx->as, &asn1_oid_id_pkcs12_keyBag, os.data,
+                     os.length);
        if (ret)
            return ret;
     }
@@ -576,21 +603,22 @@ p12_store(hx509_context context,
 {
     struct ks_pkcs12 *p12 = data;
     PKCS12_PFX pfx;
-    PKCS12_AuthenticatedSafe as;
+    struct store_func_ctx ctx;
     PKCS12_OctetString asdata;
     size_t size;
     int ret;
 
-    memset(&as, 0, sizeof(as));
+    memset(&ctx, 0, sizeof(ctx));
     memset(&pfx, 0, sizeof(pfx));
+    ctx.store_flags = flags;
 
-    ret = hx509_certs_iter_f(context, p12->certs, store_func, &as);
+    ret = hx509_certs_iter_f(context, p12->certs, store_func, &ctx);
     if (ret)
        goto out;
 
     ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
-                      &as, &size, ret);
-    free_PKCS12_AuthenticatedSafe(&as);
+                      &ctx.as, &size, ret);
+    free_PKCS12_AuthenticatedSafe(&ctx.as);
     if (ret)
        return ret;
 
@@ -642,7 +670,7 @@ p12_store(hx509_context context,
     free(asdata.data);
 
 out:
-    free_PKCS12_AuthenticatedSafe(&as);
+    free_PKCS12_AuthenticatedSafe(&ctx.as);
     free_PKCS12_PFX(&pfx);
 
     return ret;