[rs-commit] r22 - in /redwax-tool/trunk: redwax-tool.c redwax-tool.h redwax_openssl.c redwax_p11kit.c

rs-commit at redwax.eu rs-commit at redwax.eu
Tue Nov 16 14:20:11 CET 2021


Author: minfrin at redwax.eu
Date: Tue Nov 16 14:20:10 2021
New Revision: 22

Log:
Add support for reading certificates from pkcs11 URLs.

Modified:
    redwax-tool/trunk/redwax-tool.c
    redwax-tool/trunk/redwax-tool.h
    redwax-tool/trunk/redwax_openssl.c
    redwax-tool/trunk/redwax_p11kit.c

Modified: redwax-tool/trunk/redwax-tool.c
==============================================================================
--- redwax-tool/trunk/redwax-tool.c	(original)
+++ redwax-tool/trunk/redwax-tool.c	Tue Nov 16 14:20:10 2021
@@ -47,6 +47,10 @@
         APR_HOOK_LINK(set_verify_param);
         APR_HOOK_LINK(complete_verify_param);
         APR_HOOK_LINK(process_pem_in);
+        APR_HOOK_LINK(complete_pkcs11_in);
+        APR_HOOK_LINK(process_pkcs11_in);
+        APR_HOOK_LINK(complete_pkcs11_module_in);
+        APR_HOOK_LINK(process_pkcs11_module_in);
         APR_HOOK_LINK(complete_filter);
         APR_HOOK_LINK(process_filter);
         APR_HOOK_LINK(complete_nss_out);
@@ -75,6 +79,14 @@
         (redwax_tool_t * r, apr_hash_t *params), (r, params), OK, DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pem_in,
         (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_in,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_in,
+        (redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_module_in,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_module_in,
+        (redwax_tool_t * r, const char *mod, redwax_token_quoted_e quoted), (r, mod, quoted), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, complete_filter,
         (redwax_tool_t * r, apr_hash_t *filters), (r, filters), OK, DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_filter,
@@ -103,9 +115,9 @@
         (redwax_tool_t * r, const redwax_certificate_t *c1,
                 const redwax_certificate_t *c2), (r, c1, c2), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, normalise_key,
-        (redwax_tool_t * r, redwax_key_t *key), (r, key), DECLINED);
+        (redwax_tool_t * r, redwax_key_t *key, int index), (r, key, index), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, normalise_certificate,
-        (redwax_tool_t * r, redwax_certificate_t *cert), (r, cert), DECLINED);
+        (redwax_tool_t * r, redwax_certificate_t *cert, int index), (r, cert, index), DECLINED);
 
 #define REDWAX_TOOL_COMPLINE "COMP_LINE"
 #define REDWAX_TOOL_COMMANDLINE "COMMAND_LINE"
@@ -114,35 +126,37 @@
 #define REDWAX_TOOL_COMP_WORDBREAKS_DEFAULT "\"'><=;|&(:"
 
 #define REDWAX_TOOL_PEM_IN 256
-#define REDWAX_TOOL_FILTER 257
-#define REDWAX_TOOL_EMAIL 258
-#define REDWAX_TOOL_HOSTNAME 259
-#define REDWAX_TOOL_IP 260
-#define REDWAX_TOOL_TOP 261
-#define REDWAX_TOOL_CERT_OUT 262
-#define REDWAX_TOOL_NO_CERT_OUT 263
-#define REDWAX_TOOL_CHAIN_OUT 264
-#define REDWAX_TOOL_NO_CHAIN_OUT 265
-#define REDWAX_TOOL_ROOT_OUT 266
-#define REDWAX_TOOL_NO_ROOT_OUT 267
-#define REDWAX_TOOL_TRUST_OUT 268
-#define REDWAX_TOOL_NO_TRUST_OUT 269
-#define REDWAX_TOOL_CRL_OUT 270
-#define REDWAX_TOOL_NO_CRL_OUT 271
-#define REDWAX_TOOL_KEY_OUT 272
-#define REDWAX_TOOL_NO_KEY_OUT 273
-#define REDWAX_TOOL_AUTO_OUT 274
-#define REDWAX_TOOL_NO_AUTO_OUT 275
-#define REDWAX_TOOL_VERIFY_PARAM 276
-#define REDWAX_TOOL_SECRET_SUFFIX_IN 277
-#define REDWAX_TOOL_SECRET_SUFFIX_OUT 278
-#define REDWAX_TOOL_LABEL_OUT 279
-#define REDWAX_TOOL_NSS_OUT 280
-#define REDWAX_TOOL_NSS_SLOT_OUT 281
-#define REDWAX_TOOL_PEM_OUT 282
-#define REDWAX_TOOL_PKCS12_OUT 283
-#define REDWAX_TOOL_PKCS11_OUT 284
-#define REDWAX_TOOL_PKCS11_MODULE_OUT 285
+#define REDWAX_TOOL_PKCS11_IN 257
+#define REDWAX_TOOL_PKCS11_MODULE_IN 258
+#define REDWAX_TOOL_FILTER 259
+#define REDWAX_TOOL_EMAIL 260
+#define REDWAX_TOOL_HOSTNAME 261
+#define REDWAX_TOOL_IP 262
+#define REDWAX_TOOL_TOP 263
+#define REDWAX_TOOL_CERT_OUT 264
+#define REDWAX_TOOL_NO_CERT_OUT 265
+#define REDWAX_TOOL_CHAIN_OUT 266
+#define REDWAX_TOOL_NO_CHAIN_OUT 267
+#define REDWAX_TOOL_ROOT_OUT 268
+#define REDWAX_TOOL_NO_ROOT_OUT 269
+#define REDWAX_TOOL_TRUST_OUT 270
+#define REDWAX_TOOL_NO_TRUST_OUT 271
+#define REDWAX_TOOL_CRL_OUT 272
+#define REDWAX_TOOL_NO_CRL_OUT 273
+#define REDWAX_TOOL_KEY_OUT 274
+#define REDWAX_TOOL_NO_KEY_OUT 275
+#define REDWAX_TOOL_AUTO_OUT 276
+#define REDWAX_TOOL_NO_AUTO_OUT 277
+#define REDWAX_TOOL_VERIFY_PARAM 278
+#define REDWAX_TOOL_SECRET_SUFFIX_IN 279
+#define REDWAX_TOOL_SECRET_SUFFIX_OUT 280
+#define REDWAX_TOOL_LABEL_OUT 281
+#define REDWAX_TOOL_NSS_OUT 282
+#define REDWAX_TOOL_NSS_SLOT_OUT 283
+#define REDWAX_TOOL_PEM_OUT 284
+#define REDWAX_TOOL_PKCS12_OUT 285
+#define REDWAX_TOOL_PKCS11_OUT 286
+#define REDWAX_TOOL_PKCS11_MODULE_OUT 287
 
 #define REDWAX_EXIT_OK 0
 #define REDWAX_EXIT_INIT 1
@@ -166,6 +180,8 @@
     { "secret-suffix-out", REDWAX_TOOL_SECRET_SUFFIX_OUT, 1, "  --secret-suffix-out=suffix\tIf specified, secrets will be read from a file\n\t\t\t\twith the same name as the target file, and\n\t\t\t\tthe suffix specified. With value 'secret',\n\t\t\t\ta file 'key.pem' will have the secret loaded\n\t\t\t\tfrom 'key.secret' in the same directory." },
     { "label-out", REDWAX_TOOL_LABEL_OUT, 1, "  --label-out=label\t\tSet the name of the label to be applied to\n\t\t\t\tthe leaf certificates. If unspecified, the\n\t\t\t\tlabel is set to the subject of the certificate." },
     { "pem-in", REDWAX_TOOL_PEM_IN, 1, "  --pem-in=wildcard\t\tRead pem files from here. Use '-' for stdin." },
+    { "pkcs11-in", REDWAX_TOOL_PKCS11_IN, 1, "  --pkcs11-in=url\t\tRead certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys from a PKCS11\n\t\t\t\ttoken identified by the given url." },
+    { "pkcs11-module-in", REDWAX_TOOL_PKCS11_MODULE_IN, 1, "  --pkcs11-module-in=mod\tSpecify the name of the PKCS11 module to be used,\n\t\t\t\toverriding system defaults. If relative, use the\n\t\t\t\tdefault PKCS11 module path, otherwise specify the\n\t\t\t\tabsolute path. Include the extension of the module." },
     { "filter", REDWAX_TOOL_FILTER, 1, "  --filter=type\t\t\tApply the given filter to pass inputs to the\n\t\t\t\toutputs. \"search\" will pass through all\n\t\t\t\tcertificates matching the given hostname,\n\t\t\t\temail or ip address. \"verify\" will pass all\n\t\t\t\tleaf certificates that can be successfully\n\t\t\t\tverified through the certificate chain to a\n\t\t\t\ttrusted root certificate. With the default\n\t\t\t\t\"passthrough\", all certificates, csrs, and\n\t\t\t\tkeys are passed through." },
     { "email", REDWAX_TOOL_EMAIL, 1, "  --email=address\t\tSearch/verify by the given email address. Leaf\n\t\t\t\tcertificates matching the email address will\n\t\t\t\tbe included. Can be specified more than once." },
     { "hostname", REDWAX_TOOL_HOSTNAME, 1, "  --hostname=domain\t\tSearch/verify by the given hostname. Leaf\n\t\t\t\tcertificates matching the hostname will be\n\t\t\t\tkept, taking into account wildcards where\n\t\t\t\tpresent." },
@@ -1330,6 +1346,30 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t redwax_complete_pkcs11_in(redwax_tool_t *r, const char *arg,
+        redwax_token_quoted_e quoted)
+{
+    apr_hash_t *urls = apr_hash_make(r->pool);
+
+    apr_hash_index_t *hi;
+    void *val;
+
+    apr_size_t skiplen = quoted == REDWAX_TOKEN_NOQUOTE ?
+            redwax_strrcspn(arg, r->breaks) : 0;
+
+    rt_run_complete_pkcs11_in(r, arg, urls);
+
+    for (hi = apr_hash_first(r->pool, urls); hi; hi = apr_hash_next(hi)) {
+        apr_hash_this(hi, NULL, NULL, &val);
+
+        apr_file_printf(r->out, "%s\n",
+                redwax_pescape_echo(r->pool,
+                        redwax_stroff((const char *)val, skiplen), quoted, 0));
+    }
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t redwax_complete_pkcs11_out(redwax_tool_t *r, const char *arg,
         redwax_token_quoted_e quoted)
 {
@@ -1724,12 +1764,30 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t redwax_pkcs11_in(redwax_tool_t *r, const char *arg)
+{
+
+    apr_status_t status = rt_run_process_pkcs11_in(r, arg);
+
+    apr_array_clear(r->pkcs11_in.pkcs11_modules);
+
+    return status;
+}
+
+static apr_status_t redwax_pkcs11_module_in(redwax_tool_t *r, const char *arg)
+{
+
+    APR_ARRAY_PUSH(r->pkcs11_in.pkcs11_modules, const char *) = arg;
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t redwax_pkcs11_out(redwax_tool_t *r, const char *arg)
 {
 
     apr_status_t status = rt_run_process_pkcs11_out(r, arg);
 
-    apr_array_clear(r->pkcs11_out.pkcs11_modules_out);
+    apr_array_clear(r->pkcs11_out.pkcs11_modules);
 
     return status;
 }
@@ -1737,7 +1795,7 @@
 static apr_status_t redwax_pkcs11_module_out(redwax_tool_t *r, const char *arg)
 {
 
-    APR_ARRAY_PUSH(r->pkcs11_out.pkcs11_modules_out, const char *) = arg;
+    APR_ARRAY_PUSH(r->pkcs11_out.pkcs11_modules, const char *) = arg;
 
     return APR_SUCCESS;
 }
@@ -1808,6 +1866,14 @@
             redwax_dir_walk(r, optarg, &rt_run_process_pem_in);
             break;
         }
+        case REDWAX_TOOL_PKCS11_IN: {
+            redwax_pkcs11_in(r, optarg);
+            break;
+        }
+        case REDWAX_TOOL_PKCS11_MODULE_IN: {
+            redwax_pkcs11_module_in(r, optarg);
+            break;
+        }
         }
 
     }
@@ -2024,6 +2090,14 @@
                 redwax_complete_file(r, optarg, state.isquoted);
                 break;
             }
+            case REDWAX_TOOL_PKCS11_IN: {
+                redwax_complete_pkcs11_in(r, optarg, state.isquoted);
+                break;
+            }
+            case REDWAX_TOOL_PKCS11_MODULE_IN: {
+                rt_run_complete_pkcs11_module_in(r, optarg, state.isquoted);
+                break;
+            }
             case REDWAX_TOOL_FILTER: {
                 redwax_complete_filter(r, optarg, state.isquoted);
                 break;
@@ -2092,6 +2166,10 @@
             }
             case REDWAX_TOOL_PEM_IN: {
                 redwax_dir_walk(r, optarg, &rt_run_process_pem_in);
+                break;
+            }
+            case REDWAX_TOOL_PKCS11_MODULE_IN: {
+                redwax_pkcs11_module_in(r, optarg);
                 break;
             }
             case REDWAX_TOOL_NSS_OUT: {
@@ -2184,6 +2262,14 @@
             redwax_complete_file(r, "", state.isquoted);
             break;
         }
+        case REDWAX_TOOL_PKCS11_IN: {
+            redwax_complete_pkcs11_in(r, "", state.isquoted);
+            break;
+        }
+        case REDWAX_TOOL_PKCS11_MODULE_IN: {
+            rt_run_complete_pkcs11_module_in(r, "", state.isquoted);
+            break;
+        }
         case REDWAX_TOOL_FILTER: {
             redwax_complete_filter(r, "", state.isquoted);
             break;
@@ -2336,7 +2422,9 @@
     r.hostnames = apr_hash_make(r.pool);
     r.ips = apr_hash_make(r.pool);
 
-    r.pkcs11_out.pkcs11_modules_out = apr_array_make(r.pool, 10,
+    r.pkcs11_in.pkcs11_modules = apr_array_make(r.pool, 10,
+            sizeof(const char*));
+    r.pkcs11_out.pkcs11_modules = apr_array_make(r.pool, 10,
             sizeof(const char*));
 
     if (apr_gethostname(str, sizeof(str) - 1, r.pool) != APR_SUCCESS) {

Modified: redwax-tool/trunk/redwax-tool.h
==============================================================================
--- redwax-tool/trunk/redwax-tool.h	(original)
+++ redwax-tool/trunk/redwax-tool.h	Tue Nov 16 14:20:10 2021
@@ -36,7 +36,7 @@
 } redwax_nss_t;
 
 typedef struct redwax_pkcs11_t {
-    apr_array_header_t *pkcs11_modules_out;
+    apr_array_header_t *pkcs11_modules;
     const char *url;
     const char *token;
     int needs_write;
@@ -74,6 +74,7 @@
     const char *secret_suffix_out;
     const char *label_out;
     redwax_nss_t nss_out;
+    redwax_pkcs11_t pkcs11_in;
     redwax_pkcs11_t pkcs11_out;
     apr_time_t *now;
     int top;
@@ -133,8 +134,6 @@
     const char *header;
     const unsigned char *der;
     apr_size_t len;
-    const unsigned char *pub;
-    apr_size_t plen;
     const char *name;
     void *ctx;
     redwax_certificate_common_t common;
@@ -196,8 +195,6 @@
     const char *header;
     const unsigned char *der;
     apr_size_t len;
-    const unsigned char *pub;
-    apr_size_t plen;
     const char *name;
     void *ctx;
     redwax_key_common_t common;
@@ -328,6 +325,38 @@
         (redwax_tool_t *r, const char *arg, const char *secret));
 
 /**
+ * Hook to complete PKCS11 URL in.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, complete_pkcs11_in,
+        (redwax_tool_t *r, const char *url, apr_hash_t *urls));
+
+/**
+ * Hook to write incoming PKCS11 URL.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_pkcs11_in,
+        (redwax_tool_t *r, const char *arg));
+
+/**
+ * Hook to complete the module for the incoming PKCS11 URL.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, complete_pkcs11_module_in,
+        (redwax_tool_t *r, const char *mod, redwax_token_quoted_e quoted));
+
+/**
+ * Hook to specify the module for the incoming PKCS11 URL.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_pkcs11_module_in,
+        (redwax_tool_t *r, const char *arg));
+
+/**
  * Hook to complete incoming certificates / intermediates / keys
  * and pass filtered results to the outgoing certificates /
  * intermediates / keys.
@@ -468,7 +497,7 @@
  * @param r The redwax-tool context.
  */
 APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, normalise_key,
-        (redwax_tool_t *r, redwax_key_t *key));
+        (redwax_tool_t *r, redwax_key_t *key, int index));
 
 /**
  * Hook to normalise a certificate.
@@ -476,7 +505,7 @@
  * @param r The redwax-tool context.
  */
 APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, normalise_certificate,
-        (redwax_tool_t *r, redwax_certificate_t *cert));
+        (redwax_tool_t *r, redwax_certificate_t *cert, int index));
 
 
 #endif

Modified: redwax-tool/trunk/redwax_openssl.c
==============================================================================
--- redwax-tool/trunk/redwax_openssl.c	(original)
+++ redwax-tool/trunk/redwax_openssl.c	Tue Nov 16 14:20:10 2021
@@ -283,7 +283,6 @@
     for (;;) {
 
         const unsigned char *der;
-        unsigned char *pub;
 
         PKCS8_PRIV_KEY_INFO *p8inf = NULL;
         EVP_PKEY *pkey = NULL;
@@ -322,8 +321,6 @@
 
             redwax_certificate_t *cert;
 
-            EVP_PKEY *xk;
-
             X509 *x = d2i_X509(NULL, &der, len);
 
             if (!x) {
@@ -342,29 +339,1629 @@
                 cert->common.type = REDWAX_CERTIFICATE_X509;
                 cert->common.category = REDWAX_CERTIFICATE_INTERMEDIATE;
 
-                X509_set_ex_data(x, redwax_get_x509_index(), (void *)cert);
-                sk_X509_push(chain_index, x);
-
                 redwax_print_error(r, "pem-in: intermediate: %s\n",
                         redwax_openssl_name(cert->pool,
                                 X509_get_subject_name(x)));
             }
             else {
 
+                cert = apr_array_push(r->certs_in);
+
+                apr_pool_create(&cert->pool, r->pool);
+
+                cert->common.type = REDWAX_CERTIFICATE_X509;
+                cert->common.category = REDWAX_CERTIFICATE_END_ENTITY;
+
+                redwax_print_error(r, "pem-in: certificate: %s\n",
+                        redwax_openssl_name(cert->pool,
+                                X509_get_subject_name(x)));
+
+            }
+
+            cert->header = header;
+            cert->der = data;
+            cert->len = len;
+
+            rt_run_normalise_certificate(r, cert, 1);
+
+        }
+
+        else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
+
+            redwax_certificate_t *cert;
+
+            X509 *x = d2i_X509_AUX(NULL, &der, len);
+
+            if (!x) {
+                redwax_print_error(r, "Could not read certificate from '%s', skipping.\n",
+                        file);
+                redwax_openssl_print_errors(r);
+                continue;
+            }
+
+            else {
+
+                unsigned char *der = NULL;
+
+                cert = apr_array_push(r->trusted_in);
+
+                apr_pool_create(&cert->pool, r->pool);
+
+                cert->common.type = REDWAX_CERTIFICATE_X509;
+                cert->common.category = REDWAX_CERTIFICATE_TRUSTED;
+
+                cert->header = header;
+                cert->name = (const char *)X509_alias_get0(x, NULL);
+
+                cert->len = i2d_X509(x, &der);
+                cert->der = der;
+
+                rt_run_normalise_certificate(r, cert, 1);
+
+                redwax_print_error(r, "pem-in: trusted: %s\n",
+                        redwax_openssl_name(cert->pool,
+                                X509_get_subject_name(x)));
+
+            }
+        }
+
+        else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
+
+            redwax_crl_t *crl;
+
+            X509_CRL *c = d2i_X509_CRL(NULL, &der, len);
+
+            if (c) {
+
+                redwax_print_error(r, "pem-in: crl: %s\n",
+                        redwax_openssl_name(r->pool, X509_CRL_get_issuer(c)));
+
+                sk_X509_CRL_push(crl_index, c);
+
+                crl = apr_array_push(r->crls_in);
+
+                apr_pool_create(&crl->pool, r->pool);
+
+                crl->header = header;
+                crl->der = data;
+                crl->len = len;
+
+//                 X509_CRL_free(c);
+             }
+
+        }
+
+        else if (strcmp(name, PEM_STRING_RSA) == 0) {
+
+            if (!(pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &der, len)) ||
+                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
+
+                redwax_print_error(r, "Could not read RSA private key from '%s', skipping.\n",
+                        file);
+                redwax_openssl_print_errors(r);
+            }
+
+        }
+
+        else if (strcmp(name, PEM_STRING_DSA) == 0) {
+
+            if (!(pkey = d2i_PrivateKey(EVP_PKEY_DSA, NULL, &der, len)) ||
+                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
+
+                redwax_print_error(r, "Could not read DSA private key from '%s', skipping.\n",
+                        file);
+                redwax_openssl_print_errors(r);
+            }
+
+        }
+
+        else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
+
+            if (!(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &der, len)) ||
+                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
+
+                redwax_print_error(r, "Could not read EC private key from '%s', skipping.\n",
+                        file);
+                redwax_openssl_print_errors(r);
+            }
+
+        }
+        else if (strcmp(name, PEM_STRING_PKCS8INF) == 0) {
+
+            BIO *kbio;
+
+            if ((kbio = BIO_new_mem_buf(der, len)) == NULL) {
+                return APR_ENOMEM;
+            }
+
+            if (!(p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(kbio, NULL)) ||
+                    !(pkey = EVP_PKCS82PKEY(p8inf))) {
+
+                redwax_print_error(r, "Could not read private key from '%s', skipping.\n",
+                        file);
+                redwax_openssl_print_errors(r);
+            }
+
+            BIO_free(kbio);
+        }
+
+        if (p8inf) {
+            apr_pool_cleanup_register(r->pool, p8inf, cleanup_p8inf,
+                    apr_pool_cleanup_null);
+        }
+
+        if (pkey) {
+            apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
+                    apr_pool_cleanup_null);
+        }
+
+        /* handle keys */
+        if (p8inf && pkey) {
+
+            redwax_key_t *key;
+
+            BIO *kbio;
+
+#if HAVE_EVP_PKEY_GET0_DESCRIPTION
+            redwax_print_error(r, "pem-in: private key: %s\n",
+                    EVP_PKEY_get0_description(pkey));
+#else
+            redwax_print_error(r, "pem-in: private key\n");
+#endif
+
+            if ((kbio = BIO_new(BIO_s_mem())) == NULL) {
+                return APR_ENOMEM;
+            }
+
+            apr_pool_cleanup_register(r->pool, kbio, cleanup_bio,
+                    apr_pool_cleanup_null);
+
+            i2d_PKCS8_PRIV_KEY_INFO_bio(kbio, p8inf);
+
+            key = apr_array_push(r->keys_in);
+
+            apr_pool_create(&key->pool, r->pool);
+
+            switch(EVP_PKEY_base_id(pkey)) {
+            case EVP_PKEY_RSA: {
+                key->common.type = REDWAX_KEY_RSA;
+                break;
+            }
+            }
+
+            key->header = header;
+            key->len = BIO_get_mem_data(kbio, &key->der);
+
+            rt_run_normalise_key(r, key, 1);
+
+        }
+
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_set_verify_param(redwax_tool_t *r, const char *arg)
+{
+    const X509_VERIFY_PARAM *param;
+
+    if (!(param = X509_VERIFY_PARAM_lookup(arg))) {
+        redwax_print_error(r,
+                "Verify parameter not found: %s\n", arg);
+        return APR_ENOENT;
+    }
+
+    r->verify_param = arg;
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_complete_verify_param(redwax_tool_t *r,
+        apr_hash_t *params)
+{
+    int i;
+    int count = X509_VERIFY_PARAM_get_count();
+
+    for (i = 0; i < count; i++) {
+        const char *name =
+                X509_VERIFY_PARAM_get0_name(X509_VERIFY_PARAM_get0(i));
+
+        apr_hash_set(params, name, APR_HASH_KEY_STRING, name);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_complete_filter_search(redwax_tool_t *r,
+        apr_hash_t *filters)
+{
+    apr_hash_set(filters, REDWAX_OPENSSL_SEARCH,
+            strlen(REDWAX_OPENSSL_SEARCH), REDWAX_OPENSSL_SEARCH);
+
+    return APR_SUCCESS;
+}
+
+static int search_cert_match(redwax_tool_t *r, const redwax_certificate_t *cert)
+{
+    const unsigned char *der = cert->der;
+
+    X509 *x = d2i_X509(NULL, &der, cert->len);
+
+    int match = 0;
+
+    if (x) {
+
+        apr_hash_index_t *hi;
+        const void *key;
+        apr_ssize_t klen;
+
+        for (hi = apr_hash_first(r->pool, r->emails); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, &key, &klen, NULL);
+
+            if (X509_check_email(x, key, klen, 0) == 1) {
+                match = 1;
+            }
+
+        }
+
+        for (hi = apr_hash_first(r->pool, r->hostnames); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, &key, &klen, NULL);
+
+             if (X509_check_host(x, key, klen, 0, NULL) == 1) {
+                match = 1;
+            }
+
+        }
+
+        for (hi = apr_hash_first(r->pool, r->ips); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, &key, &klen, NULL);
+
+             if (X509_check_ip_asc(x, key, 0) == 1) {
+                match = 1;
+            }
+
+        }
+
+        X509_free(x);
+    }
+
+    return match;
+}
+
+static apr_status_t redwax_openssl_process_filter_search(redwax_tool_t *r,
+        const char *arg)
+{
+    int i;
+
+    if (strcmp(arg, REDWAX_OPENSSL_SEARCH)) {
+        return DECLINED;
+    }
+
+    for (i = 0; i < r->certs_in->nelts; i++)
+    {
+        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_in, i,
+                const redwax_certificate_t);
+
+        if (search_cert_match(r, cert)) {
+
+            redwax_certificate_t *ncert;
+            redwax_certificate_t tcert = { 0 };
+
+            tcert.header = cert->header;
+            tcert.der = cert->der;
+            tcert.len = cert->len;
+
+            if (r->top && r->certs_out->nelts) {
+
+                const redwax_certificate_t *ocert =
+                        &APR_ARRAY_IDX(r->certs_out, 0,
+                                const redwax_certificate_t);
+
+                int diff = rt_run_compare_certificate(r, ocert, &tcert);
+
+                /* no compare module, give up */
+                if (diff == DECLINED)  {
+                    redwax_print_error(r,
+                            "When searching, there was no implementation "
+                            "to compare certificates. Giving up.\n");
+                    return APR_ENOENT;
+                }
+
+                /* new cert is better, blow away previous results */
+                else if (diff == RIGHT) {
+                    apr_array_clear(r->certs_out);
+                    apr_array_clear(r->intermediates_out);
+                    apr_array_clear(r->trusted_out);
+                    apr_array_clear(r->keys_out);
+                }
+
+                /* original cert is better, ignore the new cert */
+                else {
+                    continue;
+                }
+
+            }
+
+            ncert = apr_array_push(r->certs_out);
+
+            memcpy(ncert, &tcert, sizeof(redwax_certificate_t));
+
+            rt_run_search_chain(r, cert);
+            rt_run_search_key(r, cert);
+        }
+
+    }
+
+    for (i = 0; i < r->intermediates_in->nelts; i++)
+    {
+        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_in, i,
+                const redwax_certificate_t);
+
+        if (search_cert_match(r, cert)) {
+
+            redwax_certificate_t *ncert = apr_array_push(r->intermediates_out);
+
+            ncert->header = cert->header;
+            ncert->der = cert->der;
+            ncert->len = cert->len;
+
+            rt_run_search_chain(r, cert);
+            rt_run_search_key(r, cert);
+         }
+
+    }
+
+    for (i = 0; i < r->trusted_in->nelts; i++)
+    {
+        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_in, i,
+                const redwax_certificate_t);
+
+        if (search_cert_match(r, cert)) {
+
+            redwax_certificate_t *ncert = apr_array_push(r->trusted_out);
+
+            ncert->header = cert->header;
+            ncert->der = cert->der;
+            ncert->len = cert->len;
+
+            rt_run_search_chain(r, cert);
+            rt_run_search_key(r, cert);
+         }
+
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_complete_filter_verify(redwax_tool_t *r,
+        apr_hash_t *filters)
+{
+    apr_hash_set(filters, REDWAX_OPENSSL_VERIFY,
+            strlen(REDWAX_OPENSSL_VERIFY), REDWAX_OPENSSL_VERIFY);
+
+    return APR_SUCCESS;
+}
+
+static int verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+    BIO *bio;
+    X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
+    char *buf = NULL;
+    int len = 0;
+
+    redwax_tool_t *r =
+             X509_STORE_CTX_get_ex_data(ctx,
+                     redwax_get_x509_store_ctx_index());
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+        return 0;
+    }
+
+    if (current_cert != NULL) {
+        X509_NAME_print_ex(bio,
+                        X509_get_subject_name(current_cert),
+                        0, XN_FLAG_RFC2253);
+        len = BIO_get_mem_data(bio, &buf);
+    }
+
+    if (!ok) {
+        redwax_print_error(r,
+                "verify-filter: %d: %.*s: verify failed: %s\n",
+                X509_STORE_CTX_get_error_depth(ctx), len, buf,
+                X509_verify_cert_error_string(
+                        X509_STORE_CTX_get_error(ctx)));
+    }
+    else {
+        redwax_print_error(r,
+                "verify-filter: %d: %.*s: verify ok\n",
+                X509_STORE_CTX_get_error_depth(ctx), len, buf);
+    }
+
+    BIO_free(bio);
+
+    return ok;
+}
+
+static apr_status_t redwax_openssl_process_filter_verify(redwax_tool_t *r,
+        const char *arg)
+{
+    X509_STORE_CTX *ctx;
+    X509_STORE *store;
+    apr_hash_index_t *hi;
+    const void *key;
+    apr_ssize_t klen;
+    int i, j;
+
+    if (strcmp(arg, REDWAX_OPENSSL_VERIFY)) {
+        return DECLINED;
+    }
+
+    store = X509_STORE_new();
+
+    apr_pool_cleanup_register(r->pool, store, cleanup_x509_store,
+            apr_pool_cleanup_null);
+
+    ctx = X509_STORE_CTX_new();
+
+    apr_pool_cleanup_register(r->pool, ctx, cleanup_x509_store_ctx,
+            apr_pool_cleanup_null);
+
+    if (r->verify_param) {
+        const X509_VERIFY_PARAM *param;
+
+        if (!(param = X509_VERIFY_PARAM_lookup(r->verify_param))) {
+            redwax_print_error(r,
+                    "Verify parameter not found: %s", r->verify_param);
+            return APR_ENOENT;
+        }
+
+        X509_VERIFY_PARAM_inherit(X509_STORE_get0_param(store), param);
+    }
+
+    if (apr_hash_count(r->emails) <= 1) {
+        for (hi = apr_hash_first(r->pool, r->emails); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, &key, &klen, NULL);
+
+            X509_VERIFY_PARAM_set1_email(X509_STORE_get0_param(store),
+                    (const char *)key, klen);
+        }
+    }
+    else {
+        redwax_print_error(r,
+                "When verifying, email address can only be specified once\n");
+        return APR_ENOENT;
+    }
+
+    for (hi = apr_hash_first(r->pool, r->hostnames); hi; hi = apr_hash_next(hi)) {
+        apr_hash_this(hi, &key, &klen, NULL);
+
+        X509_VERIFY_PARAM_add1_host(X509_STORE_get0_param(store),
+                (const char *)key, klen);
+    }
+
+    if (apr_hash_count(r->ips) <= 1) {
+        for (hi = apr_hash_first(r->pool, r->ips); hi; hi = apr_hash_next(hi)) {
+            apr_hash_this(hi, &key, &klen, NULL);
+
+            X509_VERIFY_PARAM_set1_ip_asc(X509_STORE_get0_param(store),
+                    (const char *)key);
+        }
+    }
+    else {
+        redwax_print_error(r,
+                "When verifying, ip address can only be specified once\n");
+        return APR_ENOENT;
+    }
+
+
+    for (i = 0; i < r->certs_in->nelts; i++)
+    {
+        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_in, i,
+                const redwax_certificate_t);
+
+        const unsigned char *der = cert->der;
+
+         X509 *x = d2i_X509(NULL, &der, cert->len);
+         if (x) {
+
+             if (!X509_STORE_CTX_init(ctx, store, x, chain_index)) {
+                 return APR_ENOMEM;
+             }
+
+             X509_STORE_CTX_set_ex_data(ctx,
+                     redwax_get_x509_store_ctx_index(), (void *)r);
+
+             X509_STORE_CTX_set0_trusted_stack(ctx, trusted_index);
+             X509_STORE_CTX_set0_crls(ctx, crl_index);
+
+             X509_STORE_CTX_set_verify_cb(ctx, &verify_cb);
+
+             if (X509_verify_cert(ctx) > 0 &&
+                     X509_STORE_CTX_get_error(ctx) == X509_V_OK) {
+
+                 redwax_certificate_t *ncert;
+                 redwax_certificate_t tcert = { 0 };
+
+                 tcert.header = cert->header;
+                 tcert.der = cert->der;
+                 tcert.len = cert->len;
+
+                 if (r->top && r->certs_out->nelts) {
+
+                     const redwax_certificate_t *ocert =
+                             &APR_ARRAY_IDX(r->certs_out, 0,
+                                     const redwax_certificate_t);
+
+                     int diff = rt_run_compare_certificate(r, ocert, &tcert);
+
+                     /* no compare module, give up */
+                     if (diff == DECLINED)  {
+                         redwax_print_error(r,
+                                 "When verifying, there was no implementation "
+                                 "to compare certificates. Giving up.\n");
+                         return APR_ENOENT;
+                     }
+
+                     /* new cert is better, blow away previous results */
+                     else if (diff == RIGHT) {
+                         apr_array_clear(r->certs_out);
+                         apr_array_clear(r->intermediates_out);
+                         apr_array_clear(r->trusted_out);
+                         apr_array_clear(r->keys_out);
+                     }
+
+                     /* original cert is better, ignore the new cert */
+                     else {
+                         continue;
+                     }
+
+                 }
+
+                 ncert = apr_array_push(r->certs_out);
+
+                 memcpy(ncert, &tcert, sizeof(redwax_certificate_t));
+
+                 rt_run_search_key(r, cert);
+
+                 STACK_OF(X509) *xs = X509_STORE_CTX_get1_chain(ctx);
+                 int num_untrusted = X509_STORE_CTX_get_num_untrusted(ctx);
+
+                 for (j = 0; j < sk_X509_num(xs); j++) {
+                     X509 *x = sk_X509_value(xs, j);
+
+                     redwax_certificate_t *chain =
+                             X509_get_ex_data(x, redwax_get_x509_index());
+
+                     if (!chain) {
+
+                         /* leaf cert, we're already handled above */
+
+                     }
+                     else if (j < num_untrusted) {
+
+                         ncert = apr_array_push(r->intermediates_out);
+
+                         ncert->header = chain->header;
+                         ncert->der = chain->der;
+                         ncert->len = chain->len;
+
+                         rt_run_search_key(r, chain);
+                     }
+                     else {
+
+                         ncert = apr_array_push(r->trusted_out);
+
+                         ncert->header = chain->header;
+                         ncert->der = chain->der;
+                         ncert->len = chain->len;
+
+                         rt_run_search_key(r, chain);
+                     }
+
+                 }
+                 sk_X509_pop_free(xs, X509_free);
+
+             }
+             else {
+
+                 /* verification failed */
+
+             }
+
+             X509_STORE_CTX_cleanup(ctx);
+             X509_free(x);
+         }
+
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_process_pem_out(redwax_tool_t *r,
+        const char *file, const char *secret)
+{
+
+    BIO *bio;
+    int i;
+
+    if (!strcmp(file, "-")) {
+        if ((bio = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL) {
+            redwax_openssl_print_errors(r);
+            return APR_ENOMEM;
+        }
+    }
+    else if ((bio = BIO_new(BIO_s_file())) == NULL) {
+        redwax_openssl_print_errors(r);
+        return APR_ENOMEM;
+    }
+    else if (BIO_write_filename(bio, (char *)file) <= 0) {
+        redwax_openssl_print_errors(r);
+        BIO_free(bio);
+        return APR_ENOENT;
+    }
+
+    apr_pool_cleanup_register(r->pool, bio, cleanup_bio,
+            apr_pool_cleanup_null);
+
+    if (r->cert_out) {
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out, i, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+             X509 *x = d2i_X509(NULL, &der, cert->len);
+
+             if (x) {
+
+                 redwax_print_error(r, "pem-out: certificate: %s\n",
+                         redwax_openssl_name(r->pool, X509_get_subject_name(x)));
+
+                 if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
+                         !PEM_write_bio_X509(bio, x)) {
+                     redwax_openssl_print_errors(r);
+                     X509_free(x);
+                     return APR_ENOENT;
+                 }
+                 X509_free(x);
+             }
+
+        }
+    }
+
+    if (r->chain_out) {
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out, i, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+            X509 *x = d2i_X509(NULL, &der, cert->len);
+
+            if (x) {
+
+                redwax_print_error(r, "pem-out: intermediate: %s\n",
+                        redwax_openssl_name(r->pool, X509_get_subject_name(x)));
+
+                if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
+                        !PEM_write_bio_X509(bio, x)) {
+                    redwax_openssl_print_errors(r);
+                    X509_free(x);
+                    return APR_ENOENT;
+                }
+                X509_free(x);
+            }
+
+        }
+    }
+
+    if (r->root_out || r->trust_out) {
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+            X509 *x = d2i_X509_AUX(NULL, &der, cert->len);
+
+            if (x) {
+
+                redwax_print_error(r, "pem-out: trusted: %s\n",
+                        redwax_openssl_name(r->pool, X509_get_subject_name(x)));
+
+                if (r->trust_out) {
+
+                    if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
+                            !PEM_write_bio_X509_AUX(bio, x)) {
+                        redwax_openssl_print_errors(r);
+                        X509_free(x);
+                        return APR_ENOENT;
+                    }
+
+                }
+                else {
+
+                    if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
+                            !PEM_write_bio_X509(bio, x)) {
+                        redwax_openssl_print_errors(r);
+                        X509_free(x);
+                        return APR_ENOENT;
+                    }
+
+                }
+                X509_free(x);
+            }
+
+        }
+    }
+
+    if (r->crl_out) {
+        for (i = 0; i < r->crls_out->nelts; i++)
+        {
+            const redwax_crl_t *crl = &APR_ARRAY_IDX(r->crls_out, i, const redwax_crl_t);
+
+            const unsigned char *der = crl->der;
+
+            X509_CRL *c = d2i_X509_CRL(NULL, &der, crl->len);
+
+            if (c) {
+
+                redwax_print_error(r, "pem-out: crl: %s\n",
+                        redwax_openssl_name(r->pool, X509_CRL_get_issuer(c)));
+
+                if ((r->text && !X509_CRL_print(bio, c)) ||
+                        !PEM_write_bio_X509_CRL(bio, c)) {
+                    redwax_openssl_print_errors(r);
+                    X509_CRL_free(c);
+                    return APR_ENOENT;
+                }
+                   X509_CRL_free(c);
+            }
+
+        }
+    }
+
+    if (r->key_out) {
+        for (i = 0; i < r->keys_out->nelts; i++)
+        {
+            const redwax_key_t *key = &APR_ARRAY_IDX(r->keys_out, i, const redwax_key_t);
+
+            BIO *kbio;
+            PKCS8_PRIV_KEY_INFO *p8inf;
+            EVP_PKEY *pkey;
+
+            if ((kbio = BIO_new_mem_buf(key->der, key->len)) == NULL) {
+                return APR_ENOMEM;
+            }
+
+            apr_pool_cleanup_register(r->pool, kbio, cleanup_bio,
+                    apr_pool_cleanup_null);
+
+            if (!(p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(kbio, NULL)) ||
+                    !(pkey = EVP_PKCS82PKEY(p8inf))) {
+
+                redwax_openssl_print_errors(r);
+                return APR_ENOENT;
+            }
+
+#if HAVE_EVP_PKEY_GET0_DESCRIPTION
+            redwax_print_error(r, "pem-out: private key: %s\n",
+                    EVP_PKEY_get0_description(pkey));
+#else
+            redwax_print_error(r, "pem-out: private key\n");
+#endif
+
+            if ((r->text && !EVP_PKEY_print_private(bio, pkey, 0, NULL)) ||
+                    !PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio, p8inf)) {
+
+                redwax_openssl_print_errors(r);
+                return APR_ENOENT;
+            }
+
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+static const char *encrypt_secret(redwax_tool_t *r, const char *what, const char *file,
+        const char *secret, int min, int max, apr_pool_t *pool)
+{
+
+
+    /*
+     * Obtain a secret to encrypt a key.
+     *
+     * Secret file specified and secret file exists, use that secret.
+     *
+     * Secret file specified and secret file does not exist, generate
+     * a random secret and write the secret to the file.
+     *
+     * No secret file specified, tell openssl to ask for the secret from
+     * the ui twice.
+     */
+
+    if (secret) {
+
+        apr_file_t *sfile;
+        apr_status_t status;
+
+        status = apr_file_open(&sfile, secret, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, pool);
+
+        if (APR_SUCCESS == status) {
+
+            char *buf = apr_palloc(pool, max + 2);
+            char *lf;
+
+#if HAVE_APR_CRYPTO_CLEAR
+            apr_crypto_clear(pool, buf, max + 2);
+#endif
+
+            status = apr_file_gets(buf, max + 2, sfile);
+
+            if (APR_SUCCESS != status) {
+                redwax_print_error(r,
+                        "Could not read '%s': %pm\n", secret, &status);
+                return NULL;
+            }
+
+            lf = strrchr(buf, '\n');
+            if (lf) {
+                *lf = 0;
+            }
+
+            return buf;
+
+        }
+        else if (APR_ENOENT == status) {
+
+        }
+        else {
+            redwax_print_error(r,
+                    "Could not open '%s': %pm\n", secret, &status);
+            return NULL;
+        }
+
+    }
+
+    else {
+
+        UI *ui;
+        char *prompt = NULL;
+
+        char *buf = apr_palloc(pool, max + 2);
+        char *buff = apr_palloc(pool, max + 2);
+
+#if HAVE_APR_CRYPTO_CLEAR
+        apr_crypto_clear(pool, buf, max + 2);
+        apr_crypto_clear(pool, buff, max + 2);
+#endif
+
+        ui = UI_new();
+        if (ui == NULL) {
+            return NULL;
+        }
+
+        apr_pool_cleanup_register(pool, ui, cleanup_ui,
+                apr_pool_cleanup_null);
+
+        if ((prompt = UI_construct_prompt(ui, what,
+                                          file)) == NULL) {
+            return NULL;
+        }
+
+        apr_pool_cleanup_register(pool, prompt, cleanup_alloc,
+                apr_pool_cleanup_null);
+
+        if (UI_add_input_string(ui, prompt, 0, buf, min, max) < 0
+            || (UI_add_verify_string(ui, prompt, 0, buff, min,
+                                        max, buf) < 0)) {
+
+            return NULL;
+        }
+
+        if (!UI_process(ui)) {
+            return buf;
+        }
+        else {
+            redwax_print_error(r,
+                    "Could not read %s. Must be between %d and %d characters.\n",
+                    what, min, max);
+        }
+    }
+
+    return NULL;
+}
+
+apr_status_t decrypt_secret(redwax_tool_t *r, const char *file,
+        const char *secret)
+{
+
+    /*
+     * Obtain a secret to decrypt a key.
+     *
+     * Secret file specified and secret file exists, use that secret.
+     *
+     * Secret file specified and secret file does not exist, bail out
+     * with an error.
+     *
+     * No secret file specified, and we are on the console, ask for
+     * the secret from the console once.
+     *
+     * No secret file specified, and we are not on the console. Bail
+     * out with an error.
+     */
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_process_pkcs12_out(redwax_tool_t *r,
+        const char *file, const char *secret)
+{
+    PKCS12 *p12 = NULL;
+    X509 *x = NULL;
+    STACK_OF(X509) *xis = NULL;
+
+    const char *pass;
+    const char *name = NULL;
+    EVP_PKEY *pkey = NULL;
+    int iter = PKCS12_DEFAULT_ITER, mac_iter = PKCS12_DEFAULT_ITER;
+    int key_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+    int cert_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+    int keytype = 0;
+
+    BIO *bio;
+    int i;
+
+    if (!strcmp(file, "-")) {
+        if ((bio = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL) {
+            redwax_openssl_print_errors(r);
+            return APR_ENOMEM;
+        }
+    }
+    else if ((bio = BIO_new(BIO_s_file())) == NULL) {
+        redwax_openssl_print_errors(r);
+        return APR_ENOMEM;
+    }
+    else if (BIO_write_filename(bio, (char *)file) <= 0) {
+        redwax_openssl_print_errors(r);
+        BIO_free(bio);
+        return APR_ENOENT;
+    }
+
+    apr_pool_cleanup_register(r->pool, bio, cleanup_bio,
+            apr_pool_cleanup_null);
+
+    xis = sk_X509_new_null();
+
+    apr_pool_cleanup_register(r->pool, xis, cleanup_sk_x509,
+            apr_pool_cleanup_null);
+
+    if (r->cert_out) {
+        if (!r->certs_out->nelts) {
+            redwax_print_error(r, "Warning: no certificate present, skipping pkcs12.\n");
+            return APR_ENOENT;
+        }
+        else if (r->certs_out->nelts == 1) {
+
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    0, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+            x = d2i_X509(NULL, &der, cert->len);
+
+            if (!x) {
+                redwax_openssl_print_errors(r);
+                return APR_ENOENT;
+            }
+
+            redwax_print_error(r, "pkcs12-out: certificate: %s\n",
+                    redwax_openssl_name(r->pool, X509_get_subject_name(x)));
+
+            apr_pool_cleanup_register(r->pool, x, cleanup_x509,
+                    apr_pool_cleanup_null);
+
+        }
+        else {
+            redwax_print_error(r, "Warning: more than one certificate present (%d), skipping pkcs12.\n",
+                    r->certs_out->nelts);
+            return APR_ENOENT;
+        }
+    }
+
+    if (r->chain_out) {
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+            X509 *xi = d2i_X509(NULL, &der, cert->len);
+
+            if (!xi) {
+                redwax_openssl_print_errors(r);
+                X509_free(x);
+                return APR_ENOENT;
+            }
+
+            redwax_print_error(r, "pkcs12-out: intermediate: %s\n",
+                    redwax_openssl_name(r->pool, X509_get_subject_name(xi)));
+
+            sk_X509_push(xis, xi);
+
+        }
+    }
+
+    if (r->root_out || r->trust_out) {
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            const unsigned char *der = cert->der;
+
+            X509 *xi = d2i_X509_AUX(NULL, &der, cert->len);
+
+            if (!xi) {
+                redwax_openssl_print_errors(r);
+                X509_free(x);
+                return APR_ENOENT;
+            }
+
+            redwax_print_error(r, "pkcs12-out: trusted: %s\n",
+                    redwax_openssl_name(r->pool, X509_get_subject_name(xi)));
+
+            sk_X509_push(xis, xi);
+
+        }
+    }
+
+    if (r->key_out) {
+        if (!r->keys_out->nelts) {
+            redwax_print_error(r, "Warning: no key present, skipping pkcs12.\n");
+            return APR_ENOENT;
+        }
+        else if (r->keys_out->nelts == 1) {
+
+            const redwax_key_t *key = &APR_ARRAY_IDX(r->keys_out,
+                    0, const redwax_key_t);
+
+            const unsigned char *der = key->der;
+
+            pkey = d2i_AutoPrivateKey(NULL, &der, key->len);
+
+            if (!pkey) {
+                redwax_openssl_print_errors(r);
+                return APR_ENOENT;
+            }
+
+#if HAVE_EVP_PKEY_GET0_DESCRIPTION
+            redwax_print_error(r, "pkcs12-out: private key: %s\n",
+                    EVP_PKEY_get0_description(pkey));
+#else
+            redwax_print_error(r, "pkcs12-out: private key\n");
+#endif
+
+            apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
+                    apr_pool_cleanup_null);
+
+        }
+        else {
+            redwax_print_error(r, "Warning: more than one key present (%d), skipping.\n",
+                    r->keys_out->nelts);
+            return APR_ENOENT;
+        }
+    }
+
+    pass = encrypt_secret(r, "PKCS12 export passphrase", file, secret,
+            REDWAX_PKCS12_MIN, REDWAX_PKCS12_MAX, r->pool);
+    if (!pass) {
+        return APR_ENOENT;
+    }
+
+    if (r->label_out) {
+
+        name = r->label_out;
+    }
+    else if (x) {
+
+        X509_NAME *subject = X509_get_subject_name(x);
+        int lastpos = -1;
+
+        if (subject) {
+            lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
+        }
+
+        if (lastpos > -1) {
+
+            X509_NAME_ENTRY *e;
+            ASN1_STRING *cn;
+            BIO *lbio;
+            char *buf = NULL;
+            int len = 0;
+
+            e = X509_NAME_get_entry(subject, lastpos);
+            cn = X509_NAME_ENTRY_get_data(e);
+
+            if ((lbio = BIO_new(BIO_s_mem())) == NULL) {
+                return 0;
+            }
+
+            ASN1_STRING_print_ex(lbio, cn, ASN1_STRFLGS_ESC_2253 |
+                    ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_UTF8_CONVERT);
+
+            len = BIO_get_mem_data(lbio, &buf);
+
+            name = apr_psprintf(r->pool, "%.*s", len, buf);
+
+            BIO_free(lbio);
+
+        }
+
+    }
+
+    p12 = PKCS12_create((char *)pass, (char *)name, pkey, x, xis,
+            key_pbe, cert_pbe, iter, mac_iter, keytype);
+
+    if (!p12) {
+        redwax_openssl_print_errors(r);
+        return APR_ENOENT;
+    }
+
+    else {
+
+        apr_pool_cleanup_register(r->pool, p12, cleanup_pkcs12,
+                apr_pool_cleanup_null);
+
+        i2d_PKCS12_bio(bio, p12);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_search_chain(redwax_tool_t *r,
+        const redwax_certificate_t *cert)
+{
+    redwax_certificate_t *ncert;
+    int j;
+
+    const unsigned char *der = cert->der;
+
+    X509 *x = d2i_X509(NULL, &der, cert->len);
+    if (x) {
+
+        BIO *bio_err;
+        bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
+
+        BIO_printf(bio_err, "search-filter: ");
+        X509_NAME_print_ex(bio_err,
+                        X509_get_subject_name(x),
+                        0, XN_FLAG_RFC2253);
+        BIO_printf(bio_err, "\n");
+        BIO_free(bio_err);
+
+        if (!X509_NAME_cmp(X509_get_issuer_name(x),
+                X509_get_subject_name(x))) {
+
+            X509_free(x);
+
+            return APR_ENOENT;
+        }
+
+        for (j = 0; j < sk_X509_num(trusted_index); j++) {
+             X509 *xi = sk_X509_value(trusted_index, j);
+
+             if (X509_check_issued(xi, x) == X509_V_OK) {
+
+                  redwax_certificate_t *chain =
+                          X509_get_ex_data(xi, redwax_get_x509_index());
+
+                  /* detect loops */
+
+                  ncert = apr_array_push(r->trusted_out);
+
+                  ncert->header = chain->header;
+                  ncert->der = chain->der;
+                  ncert->len = chain->len;
+
+                  if (X509_NAME_cmp(X509_get_issuer_name(x),
+                          X509_get_subject_name(x))) {
+
+                      rt_run_search_chain(r, chain);
+                  }
+             }
+
+         }
+
+        for (j = 0; j < sk_X509_num(chain_index); j++) {
+             X509 *xi = sk_X509_value(chain_index, j);
+
+             if (X509_check_issued(xi, x) == X509_V_OK) {
+
+                  redwax_certificate_t *chain =
+                          X509_get_ex_data(xi, redwax_get_x509_index());
+
+                  /* detect loops / depth */
+
+                  ncert = apr_array_push(r->intermediates_out);
+
+                  ncert->header = chain->header;
+                  ncert->der = chain->der;
+                  ncert->len = chain->len;
+
+                  if (X509_NAME_cmp(X509_get_issuer_name(x),
+                          X509_get_subject_name(x))) {
+
+                      rt_run_search_chain(r, chain);
+                  }
+             }
+
+         }
+
+        X509_free(x);
+    }
+    else {
+        return APR_EINVAL;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_search_key(redwax_tool_t *r,
+        const redwax_certificate_t *cert)
+{
+
+    redwax_key_t *key = apr_hash_get(r->keys_index,
+            cert->common.subjectpublickeyinfo_der,
+            cert->common.subjectpublickeyinfo_len);
+
+    if (key) {
+
+        redwax_key_t *nkey = apr_array_push(r->keys_out);
+
+        nkey->header = key->header;
+        nkey->der = key->der;
+        nkey->len = key->len;
+
+    }
+    else {
+        return APR_ENOENT;
+    }
+
+    return APR_SUCCESS;
+}
+
+static int redwax_openssl_compare_certificate(redwax_tool_t *r,
+        const redwax_certificate_t *c1, const redwax_certificate_t *c2)
+{
+    X509 *x1, *x2;
+    const ASN1_TIME *a1, *a2, *b1, *b2;
+    const unsigned char *der;
+    int bc1, bc2, ac1, ac2;
+    int pday, psec, diff;
+
+    /* a present certificate always beats an absent certificate */
+
+    if ((!c1 || !c1->len) && (!c2 || !c2->len)) {
+        return SAME;
+    }
+    if ((!c1 || !c1->len)) {
+        return RIGHT;
+    }
+    if ((!c2 || !c2->len)) {
+        return LEFT;
+    }
+
+    if (c1->len == c2->len && !memcmp(c1->der, c2->der, c1->len)) {
+        return SAME;
+    }
+
+    /* a parseable certificate always beats an unreadable certificate */
+
+    der = c1->der;
+    x1 = d2i_X509(NULL, &der, c1->len);
+
+    der = c2->der;
+    x2 = d2i_X509(NULL, &der, c2->len);
+
+    if (!x1 && !x2) {
+        return SAME;
+    }
+    if (!x1) {
+        X509_free(x2);
+        return RIGHT;
+    }
+    if (!x2) {
+        X509_free(x1);
+        return LEFT;
+    }
+
+    /* a valid certificate always beats a not-yet-valid certificate */
+
+    b1 = X509_get0_notBefore(x1);
+    b2 = X509_get0_notBefore(x2);
+
+    bc1 = b1 ? X509_cmp_time(b1, r->now) : -1;
+    bc2 = b2 ? X509_cmp_time(b2, r->now) : -1;
+
+    if (bc1 == bc2) {
+        /* we have a tie */
+    }
+    else if (bc1 > -1) {
+        X509_free(x1);
+        X509_free(x2);
+        return RIGHT;
+    }
+    else if (bc2 > -1) {
+        X509_free(x1);
+        X509_free(x2);
+        return LEFT;
+    }
+
+    /* a valid certificate always beats a expired certificate */
+
+    a1 = X509_get0_notAfter(x1);
+    a2 = X509_get0_notAfter(x2);
+
+    ac1 = a1 ? X509_cmp_time(a1, r->now) : 1;
+    ac2 = a2 ? X509_cmp_time(a2, r->now) : 1;
+
+    if (ac1 == ac2) {
+        /* we have a tie */
+    }
+    else if (ac1 < 1) {
+        X509_free(x1);
+        X509_free(x2);
+        return RIGHT;
+    }
+    else if (ac2 < 1) {
+        X509_free(x1);
+        X509_free(x2);
+        return LEFT;
+    }
+
+    /* the longest validity beats the shortest validity */
+
+    if (ASN1_TIME_diff(&pday, &psec, a1, a2)) {
+        if (!pday && !psec) {
+            /* still a tie */
+        }
+        else if (pday < 0 || psec < 0) {
+            X509_free(x1);
+            X509_free(x2);
+            return LEFT;
+        }
+        else if (pday > 0 || psec > 0) {
+            X509_free(x1);
+            X509_free(x2);
+            return RIGHT;
+        }
+    }
+
+    /* we still have a tie, choose one cert, but deterministically */
+    diff = X509_cmp(x1, x2);
+
+    if (diff < 0) {
+        X509_free(x1);
+        X509_free(x2);
+        return LEFT;
+    }
+    else if (diff > 0) {
+        X509_free(x1);
+        X509_free(x2);
+        return RIGHT;
+    }
+
+    /* after all this, they're the same */
+
+    X509_free(x1);
+    X509_free(x2);
+
+    return SAME;
+}
+
+static apr_status_t redwax_openssl_normalise_key(redwax_tool_t *r,
+        redwax_key_t *key, int index)
+{
+    /*
+     * DER encoded key is present, but the components are not.
+     */
+    if (key->der && !key->rsa) {
+
+        PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+        EVP_PKEY *pkey = NULL;
+        X509_PUBKEY *pub = NULL;
+
+        const unsigned char *der;
+
+        der = key->der;
+
+        p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &der, key->len);
+        if (!p8inf) {
+            /* could not unpack, let someone else try */
+            return DECLINED;
+        }
+
+        apr_pool_cleanup_register(key->pool, p8inf, cleanup_p8inf,
+                apr_pool_cleanup_null);
+
+        pkey = EVP_PKCS82PKEY(p8inf);
+        if (!pkey) {
+            /* could not convert, let someone else try */
+            return DECLINED;
+        }
+
+        apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
+                apr_pool_cleanup_null);
+
+        if (X509_PUBKEY_set(&pub, pkey)) {
+
+            unsigned char *der;
+
+            key->common.subjectpublickeyinfo_len = i2d_X509_PUBKEY(pub, NULL);
+            key->common.subjectpublickeyinfo_der = der = apr_palloc(r->pool,
+                    key->common.subjectpublickeyinfo_len);
+            i2d_X509_PUBKEY(pub, &der);
+
+            /* index the key */
+            if (index) {
+
+                if (!apr_hash_get(r->keys_index,
+                        key->common.subjectpublickeyinfo_der,
+                        key->common.subjectpublickeyinfo_len)) {
+                    apr_hash_set(r->keys_index,
+                            key->common.subjectpublickeyinfo_der,
+                            key->common.subjectpublickeyinfo_len, key);
+                }
+
+            }
+
+        }
+
+        switch(EVP_PKEY_base_id(pkey)) {
+        case EVP_PKEY_RSA: {
+            /* id is the sha1 hash of the modulus */
+            unsigned char digest[EVP_MAX_MD_SIZE];
+
+            RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+
+            unsigned int len;
+
+            key->rsa = apr_pcalloc(key->pool, sizeof(redwax_key_rsa_t));
+
+            /* public */
+            key->rsa->modulus_len = BN_num_bytes(RSA_get0_n(rsa));
+            key->rsa->modulus = apr_palloc(key->pool,
+                    key->rsa->modulus_len);
+            BN_bn2bin(RSA_get0_n(rsa), key->rsa->modulus);
+
+            key->rsa->public_exponent_len = BN_num_bytes(RSA_get0_e(rsa));
+            key->rsa->public_exponent = apr_palloc(key->pool,
+                    key->rsa->public_exponent_len);
+            BN_bn2bin(RSA_get0_e(rsa), key->rsa->public_exponent);
+
+            /* private */
+            key->rsa->private_exponent_len = BN_num_bytes(RSA_get0_d(rsa));
+            key->rsa->private_exponent = apr_palloc(key->pool,
+                    key->rsa->private_exponent_len);
+            BN_bn2bin(RSA_get0_d(rsa), key->rsa->private_exponent);
+
+            key->rsa->prime_1_len = BN_num_bytes(RSA_get0_p(rsa));
+            key->rsa->prime_1 = apr_palloc(key->pool,
+                    key->rsa->prime_1_len);
+            BN_bn2bin(RSA_get0_p(rsa), key->rsa->prime_1);
+
+            key->rsa->prime_2_len = BN_num_bytes(RSA_get0_q(rsa));
+            key->rsa->prime_2 = apr_palloc(key->pool,
+                    key->rsa->prime_2_len);
+            BN_bn2bin(RSA_get0_q(rsa), key->rsa->prime_2);
+
+            key->rsa->exponent_1_len = BN_num_bytes(RSA_get0_dmp1(rsa));
+            key->rsa->exponent_1 = apr_palloc(key->pool,
+                    key->rsa->exponent_1_len);
+            BN_bn2bin(RSA_get0_dmp1(rsa), key->rsa->exponent_1);
+
+            key->rsa->exponent_2_len = BN_num_bytes(RSA_get0_dmq1(rsa));
+            key->rsa->exponent_2 = apr_palloc(key->pool,
+                    key->rsa->exponent_2_len);
+            BN_bn2bin(RSA_get0_dmq1(rsa), key->rsa->exponent_2);
+
+            key->rsa->coefficient_len = BN_num_bytes(RSA_get0_iqmp(rsa));
+            key->rsa->coefficient = apr_palloc(key->pool,
+                    key->rsa->coefficient_len);
+            BN_bn2bin(RSA_get0_iqmp(rsa), key->rsa->coefficient);
+
+
+
+            /* ID is a SHA1 hash of the modulus */
+            EVP_Digest(key->rsa->modulus, key->rsa->modulus_len, digest, &len,
+                    EVP_sha1(), NULL);
+
+            key->common.id_der = apr_pmemdup(key->pool, digest, len);
+            key->common.id_len = len;
+
+            break;
+        }
+#if 0
+        case EVP_PKEY_DSA:
+            break;
+        case EVP_PKEY_DH:
+            break;
+        case EVP_PKEY_EC:
+            break;
+#endif
+        }
+
+    }
+
+    /*
+     * RSA components are present, but the DER encoded key is not.
+     */
+    else if (key->rsa && !key->der) {
+
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_normalise_certificate(redwax_tool_t *r,
+        redwax_certificate_t *cert, int index)
+{
+
+    /*
+     * DER encoded certificate is present, but the components are not.
+     */
+    if (cert->der && !cert->x509) {
+
+        X509 *x;
+        X509_NAME *name;
+        X509_PUBKEY *pub;
+        ASN1_INTEGER *integer;
+        ASN1_OCTET_STRING *skid;
+
+        const unsigned char *der;
+
+        redwax_certificate_x509_t *x509;
+
+        der = cert->der;
+
+        x = d2i_X509(NULL, &der, cert->len);
+        if (!x) {
+            /* could not unpack, let someone else try */
+            return DECLINED;
+        }
+
+        if (REDWAX_CERTIFICATE_UNSPECIFIED == cert->common.category) {
+
+            if (X509_check_ca(x)) {
+
+                if (X509_get_extension_flags(x) & EXFLAG_SI) {
+                    cert->common.category = REDWAX_CERTIFICATE_ROOT;
+                }
+                else {
+                    cert->common.category = REDWAX_CERTIFICATE_INTERMEDIATE;
+                }
+            }
+            else {
+                cert->common.category = REDWAX_CERTIFICATE_END_ENTITY;
+            }
+        }
+
+        /* handle indexing */
+        if (index) {
+
+            switch (cert->common.category) {
+            case REDWAX_CERTIFICATE_END_ENTITY: {
+
                 apr_hash_t *index;
                 GENERAL_NAMES *gens = NULL;
                 int i;
 
-                cert = apr_array_push(r->certs_in);
-
-                apr_pool_create(&cert->pool, r->pool);
-
-                cert->common.type = REDWAX_CERTIFICATE_X509;
-                cert->common.category = REDWAX_CERTIFICATE_END_ENTITY;
-
                 /* index */
                 X509_set_ex_data(x, redwax_get_x509_index(), (void *)cert);
                 sk_X509_push(cert_index, x);
+                X509_up_ref(x);
 
                 gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
                 if (gens) {
@@ -409,1596 +2006,27 @@
                     GENERAL_NAMES_free(gens);
                 }
 
-                redwax_print_error(r, "pem-in: certificate: %s\n",
-                        redwax_openssl_name(cert->pool,
-                                X509_get_subject_name(x)));
-
-            }
-
-            cert->header = header;
-            cert->der = data;
-            cert->len = len;
-
-            xk = X509_get_pubkey(x);
-
-            cert->plen = i2d_PUBKEY(xk, NULL);
-            cert->pub = pub = apr_palloc(r->pool, cert->plen);
-            i2d_PUBKEY(xk, &pub);
-
-            EVP_PKEY_free(xk);
-
-            rt_run_normalise_certificate(r, cert);
-
-        }
-
-        else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
-
-            redwax_certificate_t *cert;
-
-            EVP_PKEY *xk;
-
-            X509 *x = d2i_X509_AUX(NULL, &der, len);
-
-            if (!x) {
-                redwax_print_error(r, "Could not read certificate from '%s', skipping.\n",
-                        file);
-                redwax_openssl_print_errors(r);
-                continue;
-            }
-
-            else {
-
-                unsigned char *der = NULL;
-
-                cert = apr_array_push(r->trusted_in);
-
-                apr_pool_create(&cert->pool, r->pool);
-
-                cert->common.type = REDWAX_CERTIFICATE_X509;
-                cert->common.category = REDWAX_CERTIFICATE_TRUSTED;
-
-                cert->header = header;
-                cert->name = (const char *)X509_alias_get0(x, NULL);
-
-                cert->len = i2d_X509(x, &der);
-                cert->der = der;
-
-                xk = X509_get_pubkey(x);
-
-                cert->plen = i2d_PUBKEY(xk, NULL);
-                cert->pub = pub = apr_palloc(cert->pool, cert->plen);
-                i2d_PUBKEY(xk, &pub);
-
-                EVP_PKEY_free(xk);
+                break;
+            }
+            case REDWAX_CERTIFICATE_INTERMEDIATE:
 
                 X509_set_ex_data(x, redwax_get_x509_index(), (void *)cert);
-
+                sk_X509_push(chain_index, x);
+                X509_up_ref(x);
+
+                break;
+            case REDWAX_CERTIFICATE_ROOT:
+            case REDWAX_CERTIFICATE_TRUSTED:
+
+                X509_set_ex_data(x, redwax_get_x509_index(), (void *)cert);
                 sk_X509_push(trusted_index, x);
-
-                rt_run_normalise_certificate(r, cert);
-
-                redwax_print_error(r, "pem-in: trusted: %s\n",
-                        redwax_openssl_name(cert->pool,
-                                X509_get_subject_name(x)));
-
-            }
-        }
-
-        else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
-
-            redwax_crl_t *crl;
-
-            X509_CRL *c = d2i_X509_CRL(NULL, &der, len);
-
-            if (c) {
-
-                redwax_print_error(r, "pem-in: crl: %s\n",
-                        redwax_openssl_name(r->pool, X509_CRL_get_issuer(c)));
-
-                sk_X509_CRL_push(crl_index, c);
-
-                crl = apr_array_push(r->crls_in);
-
-                apr_pool_create(&crl->pool, r->pool);
-
-                crl->header = header;
-                crl->der = data;
-                crl->len = len;
-
-//                 X509_CRL_free(c);
-             }
-
-        }
-
-        else if (strcmp(name, PEM_STRING_RSA) == 0) {
-
-            if (!(pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &der, len)) ||
-                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
-
-                redwax_print_error(r, "Could not read RSA private key from '%s', skipping.\n",
-                        file);
-                redwax_openssl_print_errors(r);
-            }
-
-        }
-
-        else if (strcmp(name, PEM_STRING_DSA) == 0) {
-
-            if (!(pkey = d2i_PrivateKey(EVP_PKEY_DSA, NULL, &der, len)) ||
-                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
-
-                redwax_print_error(r, "Could not read DSA private key from '%s', skipping.\n",
-                        file);
-                redwax_openssl_print_errors(r);
-            }
-
-        }
-
-        else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
-
-            if (!(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &der, len)) ||
-                    !(p8inf = EVP_PKEY2PKCS8(pkey))) {
-
-                redwax_print_error(r, "Could not read EC private key from '%s', skipping.\n",
-                        file);
-                redwax_openssl_print_errors(r);
-            }
-
-        }
-        else if (strcmp(name, PEM_STRING_PKCS8INF) == 0) {
-
-            BIO *kbio;
-
-            if ((kbio = BIO_new_mem_buf(der, len)) == NULL) {
-                return APR_ENOMEM;
-            }
-
-            if (!(p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(kbio, NULL)) ||
-                    !(pkey = EVP_PKCS82PKEY(p8inf))) {
-
-                redwax_print_error(r, "Could not read private key from '%s', skipping.\n",
-                        file);
-                redwax_openssl_print_errors(r);
-            }
-
-            BIO_free(kbio);
-        }
-
-        if (p8inf) {
-            apr_pool_cleanup_register(r->pool, p8inf, cleanup_p8inf,
-                    apr_pool_cleanup_null);
-        }
-
-        if (pkey) {
-            apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
-                    apr_pool_cleanup_null);
-        }
-
-        /* handle keys */
-        if (p8inf && pkey) {
-
-            redwax_key_t *key;
-
-            BIO *kbio;
-            unsigned char *tmp;
-            apr_size_t plen;
-
-            plen = i2d_PUBKEY(pkey, NULL);
-            pub = tmp = apr_palloc(r->pool, plen);
-            i2d_PUBKEY(pkey, &tmp);
-
-            if (!apr_hash_get(r->keys_index, pub, plen)) {
-
-#if HAVE_EVP_PKEY_GET0_DESCRIPTION
-                redwax_print_error(r, "pem-in: private key: %s\n",
-                        EVP_PKEY_get0_description(pkey));
-#else
-                redwax_print_error(r, "pem-in: private key\n");
-#endif
-
-                if ((kbio = BIO_new(BIO_s_mem())) == NULL) {
-                    return APR_ENOMEM;
-                }
-
-                apr_pool_cleanup_register(r->pool, kbio, cleanup_bio,
-                        apr_pool_cleanup_null);
-
-                i2d_PKCS8_PRIV_KEY_INFO_bio(kbio, p8inf);
-
-                key = apr_array_push(r->keys_in);
-
-                apr_pool_create(&key->pool, r->pool);
-
-                switch(EVP_PKEY_base_id(pkey)) {
-                case EVP_PKEY_RSA: {
-                    key->common.type = REDWAX_KEY_RSA;
-                    break;
-                }
-                }
-
-                key->header = header;
-                key->len = BIO_get_mem_data(kbio, &key->der);
-                key->pub = pub;
-                key->plen = plen;
-
-                apr_hash_set(r->keys_index, key->pub, key->plen, key);
-
-                rt_run_normalise_key(r, key);
-
-            }
-
-        }
-
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_set_verify_param(redwax_tool_t *r, const char *arg)
-{
-    const X509_VERIFY_PARAM *param;
-
-    if (!(param = X509_VERIFY_PARAM_lookup(arg))) {
-        redwax_print_error(r,
-                "Verify parameter not found: %s\n", arg);
-        return APR_ENOENT;
-    }
-
-    r->verify_param = arg;
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_complete_verify_param(redwax_tool_t *r,
-        apr_hash_t *params)
-{
-    int i;
-    int count = X509_VERIFY_PARAM_get_count();
-
-    for (i = 0; i < count; i++) {
-        const char *name =
-                X509_VERIFY_PARAM_get0_name(X509_VERIFY_PARAM_get0(i));
-
-        apr_hash_set(params, name, APR_HASH_KEY_STRING, name);
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_complete_filter_search(redwax_tool_t *r,
-        apr_hash_t *filters)
-{
-    apr_hash_set(filters, REDWAX_OPENSSL_SEARCH,
-            strlen(REDWAX_OPENSSL_SEARCH), REDWAX_OPENSSL_SEARCH);
-
-    return APR_SUCCESS;
-}
-
-static int search_cert_match(redwax_tool_t *r, const redwax_certificate_t *cert)
-{
-    const unsigned char *der = cert->der;
-
-    X509 *x = d2i_X509(NULL, &der, cert->len);
-
-    int match = 0;
-
-    if (x) {
-
-        apr_hash_index_t *hi;
-        const void *key;
-        apr_ssize_t klen;
-
-        for (hi = apr_hash_first(r->pool, r->emails); hi; hi = apr_hash_next(hi)) {
-            apr_hash_this(hi, &key, &klen, NULL);
-
-            if (X509_check_email(x, key, klen, 0) == 1) {
-                match = 1;
-            }
-
-        }
-
-        for (hi = apr_hash_first(r->pool, r->hostnames); hi; hi = apr_hash_next(hi)) {
-            apr_hash_this(hi, &key, &klen, NULL);
-
-             if (X509_check_host(x, key, klen, 0, NULL) == 1) {
-                match = 1;
-            }
-
-        }
-
-        for (hi = apr_hash_first(r->pool, r->ips); hi; hi = apr_hash_next(hi)) {
-            apr_hash_this(hi, &key, &klen, NULL);
-
-             if (X509_check_ip_asc(x, key, 0) == 1) {
-                match = 1;
-            }
-
-        }
-
-        X509_free(x);
-    }
-
-    return match;
-}
-
-static apr_status_t redwax_openssl_process_filter_search(redwax_tool_t *r,
-        const char *arg)
-{
-    int i;
-
-    if (strcmp(arg, REDWAX_OPENSSL_SEARCH)) {
-        return DECLINED;
-    }
-
-    for (i = 0; i < r->certs_in->nelts; i++)
-    {
-        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_in, i,
-                const redwax_certificate_t);
-
-        if (search_cert_match(r, cert)) {
-
-            redwax_certificate_t *ncert;
-            redwax_certificate_t tcert = { 0 };
-
-            tcert.header = cert->header;
-            tcert.der = cert->der;
-            tcert.len = cert->len;
-
-            if (r->top && r->certs_out->nelts) {
-
-                const redwax_certificate_t *ocert =
-                        &APR_ARRAY_IDX(r->certs_out, 0,
-                                const redwax_certificate_t);
-
-                int diff = rt_run_compare_certificate(r, ocert, &tcert);
-
-                /* no compare module, give up */
-                if (diff == DECLINED)  {
-                    redwax_print_error(r,
-                            "When searching, there was no implementation "
-                            "to compare certificates. Giving up.\n");
-                    return APR_ENOENT;
-                }
-
-                /* new cert is better, blow away previous results */
-                else if (diff == RIGHT) {
-                    apr_array_clear(r->certs_out);
-                    apr_array_clear(r->intermediates_out);
-                    apr_array_clear(r->trusted_out);
-                    apr_array_clear(r->keys_out);
-                }
-
-                /* original cert is better, ignore the new cert */
-                else {
-                    continue;
-                }
-
-            }
-
-            ncert = apr_array_push(r->certs_out);
-
-            memcpy(ncert, &tcert, sizeof(redwax_certificate_t));
-
-            rt_run_search_chain(r, cert);
-            rt_run_search_key(r, cert);
-        }
-
-    }
-
-    for (i = 0; i < r->intermediates_in->nelts; i++)
-    {
-        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_in, i,
-                const redwax_certificate_t);
-
-        if (search_cert_match(r, cert)) {
-
-            redwax_certificate_t *ncert = apr_array_push(r->intermediates_out);
-
-            ncert->header = cert->header;
-            ncert->der = cert->der;
-            ncert->len = cert->len;
-
-            rt_run_search_chain(r, cert);
-            rt_run_search_key(r, cert);
-         }
-
-    }
-
-    for (i = 0; i < r->trusted_in->nelts; i++)
-    {
-        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_in, i,
-                const redwax_certificate_t);
-
-        if (search_cert_match(r, cert)) {
-
-            redwax_certificate_t *ncert = apr_array_push(r->trusted_out);
-
-            ncert->header = cert->header;
-            ncert->der = cert->der;
-            ncert->len = cert->len;
-
-            rt_run_search_chain(r, cert);
-            rt_run_search_key(r, cert);
-         }
-
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_complete_filter_verify(redwax_tool_t *r,
-        apr_hash_t *filters)
-{
-    apr_hash_set(filters, REDWAX_OPENSSL_VERIFY,
-            strlen(REDWAX_OPENSSL_VERIFY), REDWAX_OPENSSL_VERIFY);
-
-    return APR_SUCCESS;
-}
-
-static int verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-    BIO *bio;
-    X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx);
-    char *buf = NULL;
-    int len = 0;
-
-    redwax_tool_t *r =
-             X509_STORE_CTX_get_ex_data(ctx,
-                     redwax_get_x509_store_ctx_index());
-
-    if ((bio = BIO_new(BIO_s_mem())) == NULL) {
-        return 0;
-    }
-
-    if (current_cert != NULL) {
-        X509_NAME_print_ex(bio,
-                        X509_get_subject_name(current_cert),
-                        0, XN_FLAG_RFC2253);
-        len = BIO_get_mem_data(bio, &buf);
-    }
-
-    if (!ok) {
-        redwax_print_error(r,
-                "verify-filter: %d: %.*s: verify failed: %s\n",
-                X509_STORE_CTX_get_error_depth(ctx), len, buf,
-                X509_verify_cert_error_string(
-                        X509_STORE_CTX_get_error(ctx)));
-    }
-    else {
-        redwax_print_error(r,
-                "verify-filter: %d: %.*s: verify ok\n",
-                X509_STORE_CTX_get_error_depth(ctx), len, buf);
-    }
-
-    BIO_free(bio);
-
-    return ok;
-}
-
-static apr_status_t redwax_openssl_process_filter_verify(redwax_tool_t *r,
-        const char *arg)
-{
-    X509_STORE_CTX *ctx;
-    X509_STORE *store;
-    apr_hash_index_t *hi;
-    const void *key;
-    apr_ssize_t klen;
-    int i, j;
-
-    if (strcmp(arg, REDWAX_OPENSSL_VERIFY)) {
-        return DECLINED;
-    }
-
-    store = X509_STORE_new();
-
-    apr_pool_cleanup_register(r->pool, store, cleanup_x509_store,
-            apr_pool_cleanup_null);
-
-    ctx = X509_STORE_CTX_new();
-
-    apr_pool_cleanup_register(r->pool, ctx, cleanup_x509_store_ctx,
-            apr_pool_cleanup_null);
-
-    if (r->verify_param) {
-        const X509_VERIFY_PARAM *param;
-
-        if (!(param = X509_VERIFY_PARAM_lookup(r->verify_param))) {
-            redwax_print_error(r,
-                    "Verify parameter not found: %s", r->verify_param);
-            return APR_ENOENT;
-        }
-
-        X509_VERIFY_PARAM_inherit(X509_STORE_get0_param(store), param);
-    }
-
-    if (apr_hash_count(r->emails) <= 1) {
-        for (hi = apr_hash_first(r->pool, r->emails); hi; hi = apr_hash_next(hi)) {
-            apr_hash_this(hi, &key, &klen, NULL);
-
-            X509_VERIFY_PARAM_set1_email(X509_STORE_get0_param(store),
-                    (const char *)key, klen);
-        }
-    }
-    else {
-        redwax_print_error(r,
-                "When verifying, email address can only be specified once\n");
-        return APR_ENOENT;
-    }
-
-    for (hi = apr_hash_first(r->pool, r->hostnames); hi; hi = apr_hash_next(hi)) {
-        apr_hash_this(hi, &key, &klen, NULL);
-
-        X509_VERIFY_PARAM_add1_host(X509_STORE_get0_param(store),
-                (const char *)key, klen);
-    }
-
-    if (apr_hash_count(r->ips) <= 1) {
-        for (hi = apr_hash_first(r->pool, r->ips); hi; hi = apr_hash_next(hi)) {
-            apr_hash_this(hi, &key, &klen, NULL);
-
-            X509_VERIFY_PARAM_set1_ip_asc(X509_STORE_get0_param(store),
-                    (const char *)key);
-        }
-    }
-    else {
-        redwax_print_error(r,
-                "When verifying, ip address can only be specified once\n");
-        return APR_ENOENT;
-    }
-
-    for (i = 0; i < r->certs_in->nelts; i++)
-    {
-        const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_in, i,
-                const redwax_certificate_t);
-
-        const unsigned char *der = cert->der;
-
-         X509 *x = d2i_X509(NULL, &der, cert->len);
-         if (x) {
-
-             if (!X509_STORE_CTX_init(ctx, store, x, chain_index)) {
-                 return APR_ENOMEM;
-             }
-
-             X509_STORE_CTX_set_ex_data(ctx,
-                     redwax_get_x509_store_ctx_index(), (void *)r);
-
-             X509_STORE_CTX_set0_trusted_stack(ctx, trusted_index);
-             X509_STORE_CTX_set0_crls(ctx, crl_index);
-
-             X509_STORE_CTX_set_verify_cb(ctx, &verify_cb);
-
-             if (X509_verify_cert(ctx) > 0 &&
-                     X509_STORE_CTX_get_error(ctx) == X509_V_OK) {
-
-                 redwax_certificate_t *ncert;
-                 redwax_certificate_t tcert = { 0 };
-
-                 tcert.header = cert->header;
-                 tcert.der = cert->der;
-                 tcert.len = cert->len;
-
-                 if (r->top && r->certs_out->nelts) {
-
-                     const redwax_certificate_t *ocert =
-                             &APR_ARRAY_IDX(r->certs_out, 0,
-                                     const redwax_certificate_t);
-
-                     int diff = rt_run_compare_certificate(r, ocert, &tcert);
-
-                     /* no compare module, give up */
-                     if (diff == DECLINED)  {
-                         redwax_print_error(r,
-                                 "When verifying, there was no implementation "
-                                 "to compare certificates. Giving up.\n");
-                         return APR_ENOENT;
-                     }
-
-                     /* new cert is better, blow away previous results */
-                     else if (diff == RIGHT) {
-                         apr_array_clear(r->certs_out);
-                         apr_array_clear(r->intermediates_out);
-                         apr_array_clear(r->trusted_out);
-                         apr_array_clear(r->keys_out);
-                     }
-
-                     /* original cert is better, ignore the new cert */
-                     else {
-                         continue;
-                     }
-
-                 }
-
-                 ncert = apr_array_push(r->certs_out);
-
-                 memcpy(ncert, &tcert, sizeof(redwax_certificate_t));
-
-                 rt_run_search_key(r, cert);
-
-                 STACK_OF(X509) *xs = X509_STORE_CTX_get1_chain(ctx);
-                 int num_untrusted = X509_STORE_CTX_get_num_untrusted(ctx);
-
-                 for (j = 0; j < sk_X509_num(xs); j++) {
-                     X509 *x = sk_X509_value(xs, j);
-
-                     redwax_certificate_t *chain =
-                             X509_get_ex_data(x, redwax_get_x509_index());
-
-                     if (!chain) {
-
-                         /* leaf cert, we're already handled above */
-
-                     }
-                     else if (j < num_untrusted) {
-
-                         ncert = apr_array_push(r->intermediates_out);
-
-                         ncert->header = chain->header;
-                         ncert->der = chain->der;
-                         ncert->len = chain->len;
-
-                         rt_run_search_key(r, chain);
-                     }
-                     else {
-
-                         ncert = apr_array_push(r->trusted_out);
-
-                         ncert->header = chain->header;
-                         ncert->der = chain->der;
-                         ncert->len = chain->len;
-
-                         rt_run_search_key(r, chain);
-                     }
-
-                 }
-                 sk_X509_pop_free(xs, X509_free);
-
-             }
-             else {
-
-                 /* verification failed */
-
-             }
-
-             X509_STORE_CTX_cleanup(ctx);
-             X509_free(x);
-         }
-
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_process_pem_out(redwax_tool_t *r,
-        const char *file, const char *secret)
-{
-
-    BIO *bio;
-    int i;
-
-    if (!strcmp(file, "-")) {
-        if ((bio = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL) {
-            redwax_openssl_print_errors(r);
-            return APR_ENOMEM;
-        }
-    }
-    else if ((bio = BIO_new(BIO_s_file())) == NULL) {
-        redwax_openssl_print_errors(r);
-        return APR_ENOMEM;
-    }
-    else if (BIO_write_filename(bio, (char *)file) <= 0) {
-        redwax_openssl_print_errors(r);
-        BIO_free(bio);
-        return APR_ENOENT;
-    }
-
-    apr_pool_cleanup_register(r->pool, bio, cleanup_bio,
-            apr_pool_cleanup_null);
-
-    if (r->cert_out) {
-        for (i = 0; i < r->certs_out->nelts; i++)
-        {
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out, i, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-             X509 *x = d2i_X509(NULL, &der, cert->len);
-
-             if (x) {
-
-                 redwax_print_error(r, "pem-out: certificate: %s\n",
-                         redwax_openssl_name(r->pool, X509_get_subject_name(x)));
-
-                 if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
-                         !PEM_write_bio_X509(bio, x)) {
-                     redwax_openssl_print_errors(r);
-                     X509_free(x);
-                     return APR_ENOENT;
-                 }
-                 X509_free(x);
-             }
-
-        }
-    }
-
-    if (r->chain_out) {
-        for (i = 0; i < r->intermediates_out->nelts; i++)
-        {
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out, i, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-            X509 *x = d2i_X509(NULL, &der, cert->len);
-
-            if (x) {
-
-                redwax_print_error(r, "pem-out: intermediate: %s\n",
-                        redwax_openssl_name(r->pool, X509_get_subject_name(x)));
-
-                if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
-                        !PEM_write_bio_X509(bio, x)) {
-                    redwax_openssl_print_errors(r);
-                    X509_free(x);
-                    return APR_ENOENT;
-                }
-                X509_free(x);
-            }
-
-        }
-    }
-
-    if (r->root_out || r->trust_out) {
-        for (i = 0; i < r->trusted_out->nelts; i++)
-        {
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-            X509 *x = d2i_X509_AUX(NULL, &der, cert->len);
-
-            if (x) {
-
-                redwax_print_error(r, "pem-out: trusted: %s\n",
-                        redwax_openssl_name(r->pool, X509_get_subject_name(x)));
-
-                if (r->trust_out) {
-
-                    if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
-                            !PEM_write_bio_X509_AUX(bio, x)) {
-                        redwax_openssl_print_errors(r);
-                        X509_free(x);
-                        return APR_ENOENT;
-                    }
-
-                }
-                else {
-
-                    if ((r->text && !X509_print_ex(bio, x, 0, 0)) ||
-                            !PEM_write_bio_X509(bio, x)) {
-                        redwax_openssl_print_errors(r);
-                        X509_free(x);
-                        return APR_ENOENT;
-                    }
-
-                }
-                X509_free(x);
-            }
-
-        }
-    }
-
-    if (r->crl_out) {
-        for (i = 0; i < r->crls_out->nelts; i++)
-        {
-            const redwax_crl_t *crl = &APR_ARRAY_IDX(r->crls_out, i, const redwax_crl_t);
-
-            const unsigned char *der = crl->der;
-
-            X509_CRL *c = d2i_X509_CRL(NULL, &der, crl->len);
-
-            if (c) {
-
-                redwax_print_error(r, "pem-out: crl: %s\n",
-                        redwax_openssl_name(r->pool, X509_CRL_get_issuer(c)));
-
-                if ((r->text && !X509_CRL_print(bio, c)) ||
-                        !PEM_write_bio_X509_CRL(bio, c)) {
-                    redwax_openssl_print_errors(r);
-                    X509_CRL_free(c);
-                    return APR_ENOENT;
-                }
-                   X509_CRL_free(c);
-            }
-
-        }
-    }
-
-    if (r->key_out) {
-        for (i = 0; i < r->keys_out->nelts; i++)
-        {
-            const redwax_key_t *key = &APR_ARRAY_IDX(r->keys_out, i, const redwax_key_t);
-
-            BIO *kbio;
-            PKCS8_PRIV_KEY_INFO *p8inf;
-            EVP_PKEY *pkey;
-
-            if ((kbio = BIO_new_mem_buf(key->der, key->len)) == NULL) {
-                return APR_ENOMEM;
-            }
-
-            apr_pool_cleanup_register(r->pool, kbio, cleanup_bio,
-                    apr_pool_cleanup_null);
-
-            if (!(p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(kbio, NULL)) ||
-                    !(pkey = EVP_PKCS82PKEY(p8inf))) {
-
-                redwax_openssl_print_errors(r);
-                return APR_ENOENT;
-            }
-
-#if HAVE_EVP_PKEY_GET0_DESCRIPTION
-            redwax_print_error(r, "pem-out: private key: %s\n",
-                    EVP_PKEY_get0_description(pkey));
-#else
-            redwax_print_error(r, "pem-out: private key\n");
-#endif
-
-            if ((r->text && !EVP_PKEY_print_private(bio, pkey, 0, NULL)) ||
-                    !PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio, p8inf)) {
-
-                redwax_openssl_print_errors(r);
-                return APR_ENOENT;
-            }
-
-        }
-    }
-
-    return APR_SUCCESS;
-}
-
-static const char *encrypt_secret(redwax_tool_t *r, const char *what, const char *file,
-        const char *secret, int min, int max, apr_pool_t *pool)
-{
-
-
-    /*
-     * Obtain a secret to encrypt a key.
-     *
-     * Secret file specified and secret file exists, use that secret.
-     *
-     * Secret file specified and secret file does not exist, generate
-     * a random secret and write the secret to the file.
-     *
-     * No secret file specified, tell openssl to ask for the secret from
-     * the ui twice.
-     */
-
-    if (secret) {
-
-        apr_file_t *sfile;
-        apr_status_t status;
-
-        status = apr_file_open(&sfile, secret, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, pool);
-
-        if (APR_SUCCESS == status) {
-
-            char *buf = apr_palloc(pool, max + 2);
-            char *lf;
-
-#if HAVE_APR_CRYPTO_CLEAR
-            apr_crypto_clear(pool, buf, max + 2);
-#endif
-
-            status = apr_file_gets(buf, max + 2, sfile);
-
-            if (APR_SUCCESS != status) {
-                redwax_print_error(r,
-                        "Could not read '%s': %pm\n", secret, &status);
-                return NULL;
-            }
-
-            lf = strrchr(buf, '\n');
-            if (lf) {
-                *lf = 0;
-            }
-
-            return buf;
-
-        }
-        else if (APR_ENOENT == status) {
-
-        }
-        else {
-            redwax_print_error(r,
-                    "Could not open '%s': %pm\n", secret, &status);
-            return NULL;
-        }
-
-    }
-
-    else {
-
-        UI *ui;
-        char *prompt = NULL;
-
-        char *buf = apr_palloc(pool, max + 2);
-        char *buff = apr_palloc(pool, max + 2);
-
-#if HAVE_APR_CRYPTO_CLEAR
-        apr_crypto_clear(pool, buf, max + 2);
-        apr_crypto_clear(pool, buff, max + 2);
-#endif
-
-        ui = UI_new();
-        if (ui == NULL) {
-            return NULL;
-        }
-
-        apr_pool_cleanup_register(pool, ui, cleanup_ui,
-                apr_pool_cleanup_null);
-
-        if ((prompt = UI_construct_prompt(ui, what,
-                                          file)) == NULL) {
-            return NULL;
-        }
-
-        apr_pool_cleanup_register(pool, prompt, cleanup_alloc,
-                apr_pool_cleanup_null);
-
-        if (UI_add_input_string(ui, prompt, 0, buf, min, max) < 0
-            || (UI_add_verify_string(ui, prompt, 0, buff, min,
-                                        max, buf) < 0)) {
-
-            return NULL;
-        }
-
-        if (!UI_process(ui)) {
-            return buf;
-        }
-        else {
-			redwax_print_error(r,
-					"Could not read %s. Must be between %d and %d characters.\n",
-					what, min, max);
-        }
-    }
-
-    return NULL;
-}
-
-apr_status_t decrypt_secret(redwax_tool_t *r, const char *file,
-        const char *secret)
-{
-
-    /*
-     * Obtain a secret to decrypt a key.
-     *
-     * Secret file specified and secret file exists, use that secret.
-     *
-     * Secret file specified and secret file does not exist, bail out
-     * with an error.
-     *
-     * No secret file specified, and we are on the console, ask for
-     * the secret from the console once.
-     *
-     * No secret file specified, and we are not on the console. Bail
-     * out with an error.
-     */
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_process_pkcs12_out(redwax_tool_t *r,
-        const char *file, const char *secret)
-{
-    PKCS12 *p12 = NULL;
-    X509 *x = NULL;
-    STACK_OF(X509) *xis = NULL;
-
-    const char *pass;
-    const char *name = NULL;
-    EVP_PKEY *pkey = NULL;
-    int iter = PKCS12_DEFAULT_ITER, mac_iter = PKCS12_DEFAULT_ITER;
-    int key_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
-    int cert_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
-    int keytype = 0;
-
-    BIO *bio;
-    int i;
-
-    if (!strcmp(file, "-")) {
-        if ((bio = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL) {
-            redwax_openssl_print_errors(r);
-            return APR_ENOMEM;
-        }
-    }
-    else if ((bio = BIO_new(BIO_s_file())) == NULL) {
-        redwax_openssl_print_errors(r);
-        return APR_ENOMEM;
-    }
-    else if (BIO_write_filename(bio, (char *)file) <= 0) {
-        redwax_openssl_print_errors(r);
-        BIO_free(bio);
-        return APR_ENOENT;
-    }
-
-    apr_pool_cleanup_register(r->pool, bio, cleanup_bio,
-            apr_pool_cleanup_null);
-
-    xis = sk_X509_new_null();
-
-    apr_pool_cleanup_register(r->pool, xis, cleanup_sk_x509,
-            apr_pool_cleanup_null);
-
-    if (r->cert_out) {
-        if (!r->certs_out->nelts) {
-            redwax_print_error(r, "Warning: no certificate present, skipping pkcs12.\n");
-            return APR_ENOENT;
-        }
-        else if (r->certs_out->nelts == 1) {
-
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
-                    0, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-            x = d2i_X509(NULL, &der, cert->len);
-
-            if (!x) {
-                redwax_openssl_print_errors(r);
-                return APR_ENOENT;
-            }
-
-            redwax_print_error(r, "pkcs12-out: certificate: %s\n",
-                    redwax_openssl_name(r->pool, X509_get_subject_name(x)));
-
-            apr_pool_cleanup_register(r->pool, x, cleanup_x509,
-                    apr_pool_cleanup_null);
-
-        }
-        else {
-            redwax_print_error(r, "Warning: more than one certificate present (%d), skipping pkcs12.\n",
-                    r->certs_out->nelts);
-            return APR_ENOENT;
-        }
-    }
-
-    if (r->chain_out) {
-        for (i = 0; i < r->intermediates_out->nelts; i++)
-        {
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
-                    i, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-            X509 *xi = d2i_X509(NULL, &der, cert->len);
-
-            if (!xi) {
-                redwax_openssl_print_errors(r);
-                X509_free(x);
-                return APR_ENOENT;
-            }
-
-            redwax_print_error(r, "pkcs12-out: intermediate: %s\n",
-                    redwax_openssl_name(r->pool, X509_get_subject_name(xi)));
-
-            sk_X509_push(xis, xi);
-
-        }
-    }
-
-    if (r->root_out || r->trust_out) {
-        for (i = 0; i < r->trusted_out->nelts; i++)
-        {
-            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
-
-            const unsigned char *der = cert->der;
-
-            X509 *xi = d2i_X509_AUX(NULL, &der, cert->len);
-
-            if (!xi) {
-                redwax_openssl_print_errors(r);
-                X509_free(x);
-                return APR_ENOENT;
-            }
-
-            redwax_print_error(r, "pkcs12-out: trusted: %s\n",
-                    redwax_openssl_name(r->pool, X509_get_subject_name(xi)));
-
-            sk_X509_push(xis, xi);
-
-        }
-    }
-
-    if (r->key_out) {
-        if (!r->keys_out->nelts) {
-            redwax_print_error(r, "Warning: no key present, skipping pkcs12.\n");
-            return APR_ENOENT;
-        }
-        else if (r->keys_out->nelts == 1) {
-
-            const redwax_key_t *key = &APR_ARRAY_IDX(r->keys_out,
-                    0, const redwax_key_t);
-
-            const unsigned char *der = key->der;
-
-            pkey = d2i_AutoPrivateKey(NULL, &der, key->len);
-
-            if (!pkey) {
-                redwax_openssl_print_errors(r);
-                return APR_ENOENT;
-            }
-
-#if HAVE_EVP_PKEY_GET0_DESCRIPTION
-            redwax_print_error(r, "pkcs12-out: private key: %s\n",
-                    EVP_PKEY_get0_description(pkey));
-#else
-            redwax_print_error(r, "pkcs12-out: private key\n");
-#endif
-
-            apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
-                    apr_pool_cleanup_null);
-
-        }
-        else {
-            redwax_print_error(r, "Warning: more than one key present (%d), skipping.\n",
-                    r->keys_out->nelts);
-            return APR_ENOENT;
-        }
-    }
-
-    pass = encrypt_secret(r, "PKCS12 export passphrase", file, secret,
-            REDWAX_PKCS12_MIN, REDWAX_PKCS12_MAX, r->pool);
-    if (!pass) {
-        return APR_ENOENT;
-    }
-
-    if (r->label_out) {
-
-        name = r->label_out;
-    }
-    else if (x) {
-
-        X509_NAME *subject = X509_get_subject_name(x);
-        int lastpos = -1;
-
-        if (subject) {
-            lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
-        }
-
-        if (lastpos > -1) {
-
-            X509_NAME_ENTRY *e;
-            ASN1_STRING *cn;
-            BIO *lbio;
-            char *buf = NULL;
-            int len = 0;
-
-            e = X509_NAME_get_entry(subject, lastpos);
-            cn = X509_NAME_ENTRY_get_data(e);
-
-            if ((lbio = BIO_new(BIO_s_mem())) == NULL) {
-                return 0;
-            }
-
-            ASN1_STRING_print_ex(lbio, cn, ASN1_STRFLGS_ESC_2253 |
-                    ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_UTF8_CONVERT);
-
-            len = BIO_get_mem_data(lbio, &buf);
-
-            name = apr_psprintf(r->pool, "%.*s", len, buf);
-
-            BIO_free(lbio);
-
-        }
-
-    }
-
-    p12 = PKCS12_create((char *)pass, (char *)name, pkey, x, xis,
-            key_pbe, cert_pbe, iter, mac_iter, keytype);
-
-    if (!p12) {
-        redwax_openssl_print_errors(r);
-        return APR_ENOENT;
-    }
-
-    else {
-
-        apr_pool_cleanup_register(r->pool, p12, cleanup_pkcs12,
-                apr_pool_cleanup_null);
-
-        i2d_PKCS12_bio(bio, p12);
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_search_chain(redwax_tool_t *r,
-        const redwax_certificate_t *cert)
-{
-    redwax_certificate_t *ncert;
-    int j;
-
-    const unsigned char *der = cert->der;
-
-    X509 *x = d2i_X509(NULL, &der, cert->len);
-    if (x) {
-
-        BIO *bio_err;
-        bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
-
-        BIO_printf(bio_err, "search-filter: ");
-        X509_NAME_print_ex(bio_err,
-                        X509_get_subject_name(x),
-                        0, XN_FLAG_RFC2253);
-        BIO_printf(bio_err, "\n");
-        BIO_free(bio_err);
-
-        if (!X509_NAME_cmp(X509_get_issuer_name(x),
-                X509_get_subject_name(x))) {
-
-            X509_free(x);
-
-            return APR_ENOENT;
-        }
-
-        for (j = 0; j < sk_X509_num(trusted_index); j++) {
-             X509 *xi = sk_X509_value(trusted_index, j);
-
-             if (X509_check_issued(xi, x) == X509_V_OK) {
-
-                  redwax_certificate_t *chain =
-                          X509_get_ex_data(xi, redwax_get_x509_index());
-
-                  /* detect loops */
-
-                  ncert = apr_array_push(r->trusted_out);
-
-                  ncert->header = chain->header;
-                  ncert->der = chain->der;
-                  ncert->len = chain->len;
-
-                  if (X509_NAME_cmp(X509_get_issuer_name(x),
-                          X509_get_subject_name(x))) {
-
-                      rt_run_search_chain(r, chain);
-                  }
-             }
-
-         }
-
-        for (j = 0; j < sk_X509_num(chain_index); j++) {
-             X509 *xi = sk_X509_value(chain_index, j);
-
-             if (X509_check_issued(xi, x) == X509_V_OK) {
-
-                  redwax_certificate_t *chain =
-                          X509_get_ex_data(xi, redwax_get_x509_index());
-
-                  /* detect loops / depth */
-
-                  ncert = apr_array_push(r->intermediates_out);
-
-                  ncert->header = chain->header;
-                  ncert->der = chain->der;
-                  ncert->len = chain->len;
-
-                  if (X509_NAME_cmp(X509_get_issuer_name(x),
-                          X509_get_subject_name(x))) {
-
-                      rt_run_search_chain(r, chain);
-                  }
-             }
-
-         }
-
-        X509_free(x);
-    }
-    else {
-        return APR_EINVAL;
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_search_key(redwax_tool_t *r,
-        const redwax_certificate_t *cert)
-{
-
-    redwax_key_t *key = apr_hash_get(r->keys_index, cert->pub, cert->plen);
-
-    if (key) {
-
-        redwax_key_t *nkey = apr_array_push(r->keys_out);
-
-        nkey->header = key->header;
-        nkey->der = key->der;
-        nkey->len = key->len;
-
-    }
-    else {
-        return APR_ENOENT;
-    }
-
-    return APR_SUCCESS;
-}
-
-static int redwax_openssl_compare_certificate(redwax_tool_t *r,
-        const redwax_certificate_t *c1, const redwax_certificate_t *c2)
-{
-    X509 *x1, *x2;
-    const ASN1_TIME *a1, *a2, *b1, *b2;
-    const unsigned char *der;
-    int bc1, bc2, ac1, ac2;
-    int pday, psec, diff;
-
-    /* a present certificate always beats an absent certificate */
-
-    if ((!c1 || !c1->len) && (!c2 || !c2->len)) {
-        return SAME;
-    }
-    if ((!c1 || !c1->len)) {
-        return RIGHT;
-    }
-    if ((!c2 || !c2->len)) {
-        return LEFT;
-    }
-
-    if (c1->len == c2->len && !memcmp(c1->der, c2->der, c1->len)) {
-        return SAME;
-    }
-
-    /* a parseable certificate always beats an unreadable certificate */
-
-    der = c1->der;
-    x1 = d2i_X509(NULL, &der, c1->len);
-
-    der = c2->der;
-    x2 = d2i_X509(NULL, &der, c2->len);
-
-    if (!x1 && !x2) {
-        return SAME;
-    }
-    if (!x1) {
-        X509_free(x2);
-        return RIGHT;
-    }
-    if (!x2) {
-        X509_free(x1);
-        return LEFT;
-    }
-
-    /* a valid certificate always beats a not-yet-valid certificate */
-
-    b1 = X509_get0_notBefore(x1);
-    b2 = X509_get0_notBefore(x2);
-
-    bc1 = b1 ? X509_cmp_time(b1, r->now) : -1;
-    bc2 = b2 ? X509_cmp_time(b2, r->now) : -1;
-
-    if (bc1 == bc2) {
-        /* we have a tie */
-    }
-    else if (bc1 > -1) {
-        X509_free(x1);
-        X509_free(x2);
-        return RIGHT;
-    }
-    else if (bc2 > -1) {
-        X509_free(x1);
-        X509_free(x2);
-        return LEFT;
-    }
-
-    /* a valid certificate always beats a expired certificate */
-
-    a1 = X509_get0_notAfter(x1);
-    a2 = X509_get0_notAfter(x2);
-
-    ac1 = a1 ? X509_cmp_time(a1, r->now) : 1;
-    ac2 = a2 ? X509_cmp_time(a2, r->now) : 1;
-
-    if (ac1 == ac2) {
-        /* we have a tie */
-    }
-    else if (ac1 < 1) {
-        X509_free(x1);
-        X509_free(x2);
-        return RIGHT;
-    }
-    else if (ac2 < 1) {
-        X509_free(x1);
-        X509_free(x2);
-        return LEFT;
-    }
-
-    /* the longest validity beats the shortest validity */
-
-    if (ASN1_TIME_diff(&pday, &psec, a1, a2)) {
-        if (!pday && !psec) {
-            /* still a tie */
-        }
-        else if (pday < 0 || psec < 0) {
-            X509_free(x1);
-            X509_free(x2);
-            return LEFT;
-        }
-        else if (pday > 0 || psec > 0) {
-            X509_free(x1);
-            X509_free(x2);
-            return RIGHT;
-        }
-    }
-
-    /* we still have a tie, choose one cert, but deterministically */
-    diff = X509_cmp(x1, x2);
-
-    if (diff < 0) {
-        X509_free(x1);
-        X509_free(x2);
-        return LEFT;
-    }
-    else if (diff > 0) {
-        X509_free(x1);
-        X509_free(x2);
-        return RIGHT;
-    }
-
-    /* after all this, they're the same */
-
-    X509_free(x1);
-    X509_free(x2);
-
-    return SAME;
-}
-
-static apr_status_t redwax_openssl_normalise_key(redwax_tool_t *r,
-        redwax_key_t *key)
-{
-    /*
-     * DER encoded certificate is present, but the components are not.
-     */
-    if (key->der && !key->rsa) {
-
-        PKCS8_PRIV_KEY_INFO *p8inf = NULL;
-        EVP_PKEY *pkey = NULL;
-        X509_PUBKEY *pub = NULL;
-
-        const unsigned char *der;
-
-        der = key->der;
-
-        p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &der, key->len);
-        if (!p8inf) {
-            /* could not unpack, let someone else try */
-            return DECLINED;
-        }
-
-        apr_pool_cleanup_register(key->pool, p8inf, cleanup_p8inf,
-                apr_pool_cleanup_null);
-
-        pkey = EVP_PKCS82PKEY(p8inf);
-        if (!pkey) {
-            /* could not convert, let someone else try */
-            return DECLINED;
-        }
-
-        apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
-                apr_pool_cleanup_null);
-
-        if (X509_PUBKEY_set(&pub, pkey)) {
-
-            unsigned char *der;
-
-            key->common.subjectpublickeyinfo_len = i2d_X509_PUBKEY(pub, NULL);
-            key->common.subjectpublickeyinfo_der = der = apr_palloc(r->pool,
-                    key->common.subjectpublickeyinfo_len);
-            i2d_X509_PUBKEY(pub, &der);
-        }
-
-        switch(EVP_PKEY_base_id(pkey)) {
-        case EVP_PKEY_RSA: {
-            /* id is the sha1 hash of the modulus */
-            unsigned char digest[EVP_MAX_MD_SIZE];
-
-            RSA *rsa = EVP_PKEY_get0_RSA(pkey);
-
-            unsigned int len;
-
-            key->rsa = apr_pcalloc(key->pool, sizeof(redwax_key_rsa_t));
-
-            /* public */
-            key->rsa->modulus_len = BN_num_bytes(RSA_get0_n(rsa));
-            key->rsa->modulus = apr_palloc(key->pool,
-                    key->rsa->modulus_len);
-            BN_bn2bin(RSA_get0_n(rsa), key->rsa->modulus);
-
-            key->rsa->public_exponent_len = BN_num_bytes(RSA_get0_e(rsa));
-            key->rsa->public_exponent = apr_palloc(key->pool,
-                    key->rsa->public_exponent_len);
-            BN_bn2bin(RSA_get0_e(rsa), key->rsa->public_exponent);
-
-            /* private */
-            key->rsa->private_exponent_len = BN_num_bytes(RSA_get0_d(rsa));
-            key->rsa->private_exponent = apr_palloc(key->pool,
-                    key->rsa->private_exponent_len);
-            BN_bn2bin(RSA_get0_d(rsa), key->rsa->private_exponent);
-
-            key->rsa->prime_1_len = BN_num_bytes(RSA_get0_p(rsa));
-            key->rsa->prime_1 = apr_palloc(key->pool,
-                    key->rsa->prime_1_len);
-            BN_bn2bin(RSA_get0_p(rsa), key->rsa->prime_1);
-
-            key->rsa->prime_2_len = BN_num_bytes(RSA_get0_q(rsa));
-            key->rsa->prime_2 = apr_palloc(key->pool,
-                    key->rsa->prime_2_len);
-            BN_bn2bin(RSA_get0_q(rsa), key->rsa->prime_2);
-
-            key->rsa->exponent_1_len = BN_num_bytes(RSA_get0_dmp1(rsa));
-            key->rsa->exponent_1 = apr_palloc(key->pool,
-                    key->rsa->exponent_1_len);
-            BN_bn2bin(RSA_get0_dmp1(rsa), key->rsa->exponent_1);
-
-            key->rsa->exponent_2_len = BN_num_bytes(RSA_get0_dmq1(rsa));
-            key->rsa->exponent_2 = apr_palloc(key->pool,
-                    key->rsa->exponent_2_len);
-            BN_bn2bin(RSA_get0_dmq1(rsa), key->rsa->exponent_2);
-
-            key->rsa->coefficient_len = BN_num_bytes(RSA_get0_iqmp(rsa));
-            key->rsa->coefficient = apr_palloc(key->pool,
-                    key->rsa->coefficient_len);
-            BN_bn2bin(RSA_get0_iqmp(rsa), key->rsa->coefficient);
-
-
-
-            /* ID is a SHA1 hash of the modulus */
-            EVP_Digest(key->rsa->modulus, key->rsa->modulus_len, digest, &len,
-                    EVP_sha1(), NULL);
-
-            key->common.id_der = apr_pmemdup(key->pool, digest, len);
-            key->common.id_len = len;
-
-            break;
-        }
-#if 0
-        case EVP_PKEY_DSA:
-            break;
-        case EVP_PKEY_DH:
-            break;
-        case EVP_PKEY_EC:
-            break;
-#endif
-        }
-
-    }
-
-    return APR_SUCCESS;
-}
-
-static apr_status_t redwax_openssl_normalise_certificate(redwax_tool_t *r,
-        redwax_certificate_t *cert)
-{
-
-    /*
-     * DER encoded certificate is present, but the components are not.
-     */
-    if (cert->der && !cert->x509) {
-
-        X509 *x;
-        X509_NAME *name;
-        X509_PUBKEY *pub;
-        ASN1_INTEGER *integer;
-        ASN1_OCTET_STRING *skid;
-
-        const unsigned char *der;
-
-        redwax_certificate_x509_t *x509;
-
-        der = cert->der;
-
-        x = d2i_X509(NULL, &der, cert->len);
-        if (!x) {
-            /* could not unpack, let someone else try */
-            return DECLINED;
+                X509_up_ref(x);
+
+                break;
+            default:
+                break;
+            }
+
         }
 
         pub = X509_get_X509_PUBKEY(x);

Modified: redwax-tool/trunk/redwax_p11kit.c
==============================================================================
--- redwax-tool/trunk/redwax_p11kit.c	(original)
+++ redwax-tool/trunk/redwax_p11kit.c	Tue Nov 16 14:20:10 2021
@@ -1012,6 +1012,461 @@
     return APR_SUCCESS;
 }
 
+static CK_RV redwax_p11kit_read_attributes(apr_pool_t *pool,
+        CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
+        CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR template,
+        CK_ULONG template_len)
+{
+    CK_ULONG i;
+    int ret;
+
+    ret = module->C_GetAttributeValue(session, object,
+            template, template_len);
+    if (ret == CKR_OK || ret == CKR_ATTRIBUTE_SENSITIVE
+            || ret == CKR_ATTRIBUTE_TYPE_INVALID) {
+
+        for (i = 0; i < template_len; i++) {
+            if (CK_UNAVAILABLE_INFORMATION != template[i].ulValueLen) {
+                template[i].pValue = apr_palloc(pool, template[i].ulValueLen);
+            }
+        }
+
+        ret = module->C_GetAttributeValue(session,
+                object, &template[0], template_len);
+
+    }
+
+    return ret;
+}
+
+static apr_status_t redwax_p11kit_read_slot(redwax_tool_t *r,
+        P11KitUri *parsed, CK_FUNCTION_LIST *module, CK_TOKEN_INFO *tokenInfo,
+        CK_SLOT_ID_PTR slot_id)
+{
+    apr_pool_t *pool;
+    CK_SESSION_HANDLE session;
+    CK_ATTRIBUTE_PTR attrs;
+    CK_ULONG n_attrs;
+    CK_OBJECT_HANDLE object;
+    CK_ULONG object_count;
+
+    redwax_pkcs11_session_t *s;
+
+    int ret;
+
+    apr_pool_create(&pool, r->pool);
+
+    ret = module->C_OpenSession(*slot_id,
+            CKF_SERIAL_SESSION, NULL, NULL, &session);
+    if (ret != CKR_OK) {
+
+        redwax_print_error(r,
+                "pkcs11-in: could not open session to '%s', skipping: %s\n",
+                redwax_pstrntrim(pool, (const char*) tokenInfo->label,
+                        sizeof(tokenInfo->label)), pkcs11_errstr(ret));
+
+        apr_pool_destroy(pool);
+
+        return APR_EGENERAL;
+    }
+
+    s = apr_pcalloc(pool, sizeof(redwax_pkcs11_session_t));
+    s->module = module;
+    s->session = session;
+
+    apr_pool_cleanup_register(pool, s, cleanup_session,
+            apr_pool_cleanup_null);
+
+    // fixme: warnings say pkcs11-out
+
+//    status = redwax_p11kit_handle_token_login(r, pool, parsed,
+//            module, tokenInfo, slot_id, session);
+//    if (status != APR_SUCCESS) {
+
+//        apr_pool_destroy(pool);
+
+//        return status;
+//    }
+
+    attrs = p11_kit_uri_get_attributes (parsed, &n_attrs);
+
+    ret = module->C_FindObjectsInit(session, attrs, n_attrs);
+    if (ret != CKR_OK) {
+
+        redwax_print_error(r,
+                "pkcs11-in: could not find objects on '%s', skipping: %s\n",
+                redwax_pstrntrim(pool, (const char*) tokenInfo->label,
+                        sizeof(tokenInfo->label)), pkcs11_errstr(ret));
+
+        apr_pool_destroy(pool);
+
+        return APR_EGENERAL;
+    }
+
+    while (1) {
+
+        CK_ATTRIBUTE class_template[] = {
+          {CKA_CLASS, NULL_PTR, 0},
+          {CKA_CERTIFICATE_TYPE, NULL_PTR, 0}
+        };
+
+        int class_template_len = 2;
+
+        ret = module->C_FindObjects(session, &object, 1,
+                &object_count);
+        if (ret != CKR_OK || object_count == 0) {
+            break;
+        }
+
+        ret = redwax_p11kit_read_attributes(pool, module, session, object,
+                class_template, class_template_len);
+        if (ret == CKR_OK) {
+
+            CK_OBJECT_CLASS clazz = *(CK_OBJECT_CLASS_PTR)class_template[0].pValue;
+            CK_CERTIFICATE_TYPE type = *(CK_CERTIFICATE_TYPE *)class_template[1].pValue;
+
+            /* 4.6 Certificate objects */
+            if (CKO_CERTIFICATE == clazz) {
+
+                /* 4.6.3 X.509 public key certificate objects */
+                if (CKC_X_509 == type) {
+
+                    CK_ATTRIBUTE cert_template[] =
+                            { { CKA_VALUE, NULL_PTR, 0 }, { CKA_TRUSTED, NULL_PTR, 0 } };
+                    int cert_template_len = 2;
+
+                    ret = redwax_p11kit_read_attributes(pool, module, session, object,
+                            cert_template, cert_template_len);
+                    if (ret == CKR_OK || ret == CKR_ATTRIBUTE_SENSITIVE
+                            || ret == CKR_ATTRIBUTE_TYPE_INVALID) {
+
+                        CK_BBOOL trusted = *(CK_BBOOL *)cert_template[1].pValue;
+
+                        redwax_certificate_t *cert = apr_pcalloc(pool,
+                                sizeof(redwax_certificate_t));
+
+                        apr_pool_create(&cert->pool, r->pool);
+
+                        cert->common.type = REDWAX_CERTIFICATE_X509;
+
+                        cert->der = apr_pmemdup(cert->pool,
+                                cert_template[0].pValue,
+                                cert_template[0].ulValueLen);
+                        cert->len = cert_template[0].ulValueLen;
+
+                        rt_run_normalise_certificate(r, cert, 1);
+
+                        if (REDWAX_CERTIFICATE_INTERMEDIATE
+                                == cert->common.category && trusted) {
+                            cert->common.category = REDWAX_CERTIFICATE_TRUSTED;
+                        }
+
+                        switch (cert->common.category) {
+                        case REDWAX_CERTIFICATE_END_ENTITY: {
+
+                            redwax_certificate_t *c = apr_array_push(r->certs_in);
+                            memcpy(c, cert, sizeof(*cert));
+
+                            redwax_print_error(r, "pkcs11-in: certificate: %s\n",
+                                    cert->common.subject);
+
+                            break;
+                        }
+                        case REDWAX_CERTIFICATE_INTERMEDIATE: {
+
+                            redwax_certificate_t *c = apr_array_push(r->intermediates_in);
+                            memcpy(c, cert, sizeof(*cert));
+
+                            redwax_print_error(r, "pkcs11-in: intermediate: %s\n",
+                                    cert->common.subject);
+
+                            break;
+                        }
+                        case REDWAX_CERTIFICATE_ROOT: {
+
+                            // fixme: root, but nowhere to put it
+
+                            redwax_certificate_t *c = apr_array_push(r->trusted_in);
+                            memcpy(c, cert, sizeof(*cert));
+
+                            redwax_print_error(r, "pkcs11-in: root: %s\n",
+                                    cert->common.subject);
+
+                            break;
+                        }
+                        case REDWAX_CERTIFICATE_TRUSTED: {
+
+                            redwax_certificate_t *c = apr_array_push(r->trusted_in);
+                            memcpy(c, cert, sizeof(*cert));
+
+                            redwax_print_error(r, "pkcs11-in: trusted: %s\n",
+                                    cert->common.subject);
+
+                            break;
+                        }
+                        default: {
+
+                            redwax_print_error(r, "pkcs11-in: unrecognised "
+                                    "certificate, skipped: %s\n",
+                                    cert->common.subject);
+
+                            break;
+                        }
+                        }
+
+                    }
+
+                }
+
+                /* 4.6.4 WTLS public key certificate objects */
+                else if (CKC_WTLS == type) {
+
+                    redwax_print_error(r,
+                            "pkcs11-in: WTLS certificate found on '%s', skipping\n",
+                            redwax_pstrntrim(pool, (const char*) tokenInfo->label,
+                                    sizeof(tokenInfo->label)));
+
+                }
+
+                /* 4.6.5 X.509 attribute certificate objects */
+                else if (CKC_X_509_ATTR_CERT == type) {
+
+                    redwax_print_error(r,
+                            "pkcs11-in: Attribute certificate cert found on '%s', skipping\n",
+                            redwax_pstrntrim(pool, (const char*) tokenInfo->label,
+                                    sizeof(tokenInfo->label)));
+
+                }
+
+            }
+
+            else if (CKO_PRIVATE_KEY == clazz) {
+
+                redwax_print_error(r,
+                        "pkcs11-in: Private key found on '%s', skipping\n",
+                        redwax_pstrntrim(pool, (const char*) tokenInfo->label,
+                                sizeof(tokenInfo->label)));
+
+            }
+
+            /* all other object types are ignored for now */
+
+        }
+
+    }
+
+    module->C_FindObjectsFinal (session);
+
+    apr_pool_destroy(pool);
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_p11kit_process_pkcs11_in(redwax_tool_t *r,
+        const char *url)
+{
+    apr_pool_t *pool;
+    P11KitUri *parsed;
+    CK_FUNCTION_LIST **modules;
+    const char *module_name;
+    const char *module_path;
+
+    int i, j;
+
+    int ret;
+
+    apr_status_t status;
+
+    apr_pool_create(&pool, r->pool);
+
+    /* start with some url parsing */
+
+    parsed = p11_kit_uri_new();
+
+    apr_pool_cleanup_register(r->pool, parsed, cleanup_p11kituri,
+            apr_pool_cleanup_null);
+
+    ret = p11_kit_uri_parse(url, P11_KIT_URI_FOR_ANY, parsed);
+
+    switch (ret) {
+    case P11_KIT_URI_OK: {
+        break;
+    }
+    case P11_KIT_URI_BAD_SCHEME:
+    case P11_KIT_URI_BAD_SYNTAX:
+    case P11_KIT_URI_BAD_VERSION:
+    case P11_KIT_URI_BAD_ENCODING:
+    default:
+
+        /* url prefix bad, giving up */
+        redwax_print_error(r,
+                "pkcs11-in: url '%s' could not be parsed, giving up: %s\n",
+                url, p11_kit_uri_message(ret));
+
+        r->rc = APR_BADARG;
+        return APR_BADARG;
+    }
+
+    /* now for some module loading */
+
+    module_name = p11_kit_uri_get_module_name(parsed);
+    module_path = p11_kit_uri_get_module_path(parsed);
+
+    if (module_name || module_path) {
+
+        const char *mod;
+
+        modules = apr_pcalloc(r->pool, 2 * sizeof(CK_FUNCTION_LIST *));
+
+        if (module_path) {
+            mod = apr_pstrcat(r->pool, module_path, "/", module_name, MODULE_EXT, NULL);
+        }
+        else {
+            mod = apr_pstrcat(r->pool, module_name, MODULE_EXT, NULL);
+        }
+
+        CK_FUNCTION_LIST *module = p11_kit_module_load(mod, 0);
+        if (module) {
+
+            p11_kit_module_initialize(module);
+
+            apr_pool_cleanup_register(r->pool, module, cleanup_module,
+                    apr_pool_cleanup_null);
+
+            modules[0] = module;
+        }
+        else {
+
+            redwax_print_error(r,
+                    "pkcs11-in: module '%s' could not be loaded, giving up\n",
+                    mod);
+            return APR_EDSOOPEN;
+
+        }
+
+    }
+    else if (r->pkcs11_in.pkcs11_modules->nelts) {
+
+        modules = apr_pcalloc(r->pool,
+                (r->pkcs11_in.pkcs11_modules->nelts + 1)
+                        * sizeof(CK_FUNCTION_LIST *));
+
+        for (i = 0, j = 0; i < r->pkcs11_in.pkcs11_modules->nelts;
+                i++)
+        {
+            const char *mod =
+                    APR_ARRAY_IDX(r->pkcs11_in.pkcs11_modules, i,
+                    const char *);
+
+            CK_FUNCTION_LIST *module = p11_kit_module_load(mod, 0);
+            if (module) {
+
+                p11_kit_module_initialize(module);
+
+                apr_pool_cleanup_register(r->pool, module, cleanup_module,
+                        apr_pool_cleanup_null);
+
+                modules[j++] = module;
+            }
+            else {
+
+                redwax_print_error(r,
+                        "pkcs11-in: module '%s' could not be loaded, giving up\n",
+                        mod);
+                return APR_EDSOOPEN;
+
+            }
+
+        }
+
+    }
+    else {
+        modules = global_modules;
+    }
+
+    /* match the modules to the url */
+
+    for (i = 0; modules[i]; i++) {
+
+        CK_INFO info;
+        CK_ULONG ulCount;
+        CK_SLOT_ID_PTR pSlotList;
+        CK_SLOT_INFO slotInfo;
+        CK_TOKEN_INFO tokenInfo;
+
+        ret = modules[i]->C_GetInfo(&info);
+        if (ret != CKR_OK) {
+            return APR_EGENERAL;
+        }
+
+        if (!p11_kit_uri_match_module_info(parsed, &info)) {
+            continue;
+        }
+
+
+
+
+        /* match slots to the url */
+
+        ret = modules[i]->C_GetSlotList(CK_FALSE, NULL_PTR, &ulCount);
+        if (ret != CKR_OK) {
+
+            return APR_EGENERAL;
+        }
+
+        if (!ulCount) {
+            continue;
+        }
+
+        pSlotList = apr_palloc(pool, ulCount * sizeof(CK_SLOT_ID));
+        ret = modules[i]->C_GetSlotList(CK_FALSE, pSlotList, &ulCount);
+
+        for (j = 0; j < ulCount; j++) {
+
+            ret = modules[i]->C_GetSlotInfo(pSlotList[j], &slotInfo);
+            if (ret != CKR_OK) {
+                return APR_EGENERAL;
+            }
+
+            if (!p11_kit_uri_match_slot_info(parsed, &slotInfo)) {
+                continue;
+            }
+
+
+
+
+            /* match tokens to the url */
+
+            ret = modules[i]->C_GetTokenInfo(pSlotList[j], &tokenInfo);
+            if (ret == CKR_TOKEN_NOT_PRESENT) {
+                continue;
+            }
+            else if (ret != CKR_OK) {
+                return APR_EGENERAL;
+            }
+
+            if (!p11_kit_uri_match_token_info(parsed, &tokenInfo)) {
+                continue;
+            }
+
+            status = redwax_p11kit_read_slot(r, parsed,
+                    modules[i], &tokenInfo, &pSlotList[j]);
+            if (status != APR_SUCCESS) {
+                return status;
+            }
+
+
+
+
+        }
+
+
+    }
+
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t redwax_p11kit_process_pkcs11_out(redwax_tool_t *r,
         const char *url)
 {
@@ -1095,17 +1550,17 @@
         }
 
     }
-    else if (r->pkcs11_out.pkcs11_modules_out->nelts) {
+    else if (r->pkcs11_out.pkcs11_modules->nelts) {
 
         modules = apr_pcalloc(r->pool,
-                (r->pkcs11_out.pkcs11_modules_out->nelts + 1)
+                (r->pkcs11_out.pkcs11_modules->nelts + 1)
                         * sizeof(CK_FUNCTION_LIST *));
 
-        for (i = 0, j = 0; i < r->pkcs11_out.pkcs11_modules_out->nelts;
+        for (i = 0, j = 0; i < r->pkcs11_out.pkcs11_modules->nelts;
                 i++)
         {
             const char *mod =
-                    APR_ARRAY_IDX(r->pkcs11_out.pkcs11_modules_out, i,
+                    APR_ARRAY_IDX(r->pkcs11_out.pkcs11_modules, i,
                     const char *);
 
             CK_FUNCTION_LIST *module = p11_kit_module_load(mod, 0);
@@ -1326,7 +1781,7 @@
 
 }
 
-static apr_status_t redwax_p11kit_complete_pkcs11_out(redwax_tool_t *r,
+static apr_status_t redwax_p11kit_complete_pkcs11(redwax_tool_t *r,
         const char *url, apr_hash_t *urls)
 {
     apr_pool_t *pool;
@@ -1483,17 +1938,17 @@
         int i, j;
 
         /* grab the modules */
-        if (r->pkcs11_out.pkcs11_modules_out->nelts) {
+        if (r->pkcs11_out.pkcs11_modules->nelts) {
 
             modules = apr_pcalloc(r->pool,
-                    (r->pkcs11_out.pkcs11_modules_out->nelts + 1)
+                    (r->pkcs11_out.pkcs11_modules->nelts + 1)
                             * sizeof(CK_FUNCTION_LIST *));
 
-            for (i = 0, j = 0; i < r->pkcs11_out.pkcs11_modules_out->nelts;
+            for (i = 0, j = 0; i < r->pkcs11_out.pkcs11_modules->nelts;
                     i++)
             {
                 const char *mod =
-                        APR_ARRAY_IDX(r->pkcs11_out.pkcs11_modules_out, i,
+                        APR_ARRAY_IDX(r->pkcs11_out.pkcs11_modules, i,
                         const char *);
 
                 CK_FUNCTION_LIST *module = p11_kit_module_load(mod, 0);
@@ -1760,7 +2215,7 @@
     return APR_SUCCESS;
 }
 
-static apr_status_t redwax_p11kit_complete_pkcs11_module_out(redwax_tool_t *r,
+static apr_status_t redwax_p11kit_complete_pkcs11_module(redwax_tool_t *r,
         const char *mod, redwax_token_quoted_e quoted)
 {
     apr_dir_t *thedir;
@@ -1887,11 +2342,17 @@
 void redwax_add_default_p11kit_hooks()
 {
     rt_hook_initialise(redwax_p11kit_initialise, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_process_pkcs11_in(redwax_p11kit_process_pkcs11_in, NULL, NULL,
+            APR_HOOK_MIDDLE);
     rt_hook_process_pkcs11_out(redwax_p11kit_process_pkcs11_out, NULL, NULL,
             APR_HOOK_MIDDLE);
-    rt_hook_complete_pkcs11_out(redwax_p11kit_complete_pkcs11_out, NULL, NULL,
+    rt_hook_complete_pkcs11_in(redwax_p11kit_complete_pkcs11, NULL, NULL,
             APR_HOOK_MIDDLE);
-    rt_hook_complete_pkcs11_module_out(redwax_p11kit_complete_pkcs11_module_out,
+    rt_hook_complete_pkcs11_out(redwax_p11kit_complete_pkcs11, NULL, NULL,
+            APR_HOOK_MIDDLE);
+    rt_hook_complete_pkcs11_module_in(redwax_p11kit_complete_pkcs11_module,
+            NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_complete_pkcs11_module_out(redwax_p11kit_complete_pkcs11_module,
             NULL, NULL, APR_HOOK_MIDDLE);
 }
 



More information about the rs-commit mailing list