[rs-commit] r78 - in /redwax-tool/trunk: redwax-tool.c redwax_openssl.c

rs-commit at redwax.eu rs-commit at redwax.eu
Thu Nov 25 12:26:45 CET 2021


Author: minfrin at redwax.eu
Date: Thu Nov 25 12:26:44 2021
New Revision: 78

Log:
Add an implementation of pkcs12 import based on openssl.

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

Modified: redwax-tool/trunk/redwax-tool.c
==============================================================================
--- redwax-tool/trunk/redwax-tool.c	(original)
+++ redwax-tool/trunk/redwax-tool.c	Thu Nov 25 12:26:44 2021
@@ -212,9 +212,7 @@
     { "secret-token-out", REDWAX_TOOL_SECRET_TOKEN_OUT, 1, "  --secret-token-out=file\tIf specified, secrets needed to write\n\t\t\t\tcertificates and keys to tokens (PKCS11 and\n\t\t\t\tNSS) will be read from a file one secret per\n\t\t\t\tline. Each secret is preceded by the name of\n\t\t\t\tthe token and a colon, as per the NSS\n\t\t\t\tpwdfile.txt file." },
     { "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." },
-#if 0
     { "pkcs12-in", REDWAX_TOOL_PKCS12_IN, 1, "  --pkcs12-in=file\t\tRead certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys from a PKCS12\n\t\t\t\tfile. Use '-' for stdin. Provide the secret\n\t\t\t\tusing --secret-suffix-in." },
-#endif
     { "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." },

Modified: redwax-tool/trunk/redwax_openssl.c
==============================================================================
--- redwax-tool/trunk/redwax_openssl.c	(original)
+++ redwax-tool/trunk/redwax_openssl.c	Thu Nov 25 12:26:44 2021
@@ -753,6 +753,24 @@
     return APR_SUCCESS;
 }
 
+static apr_status_t cleanup_sk_pkcs7(void *dummy)
+{
+    if (dummy) {
+        sk_PKCS7_pop_free(dummy, PKCS7_free);
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t cleanup_sk_pkcs2_safebag(void *dummy)
+{
+    if (dummy) {
+        sk_PKCS12_SAFEBAG_pop_free(dummy, PKCS12_SAFEBAG_free);
+    }
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t cleanup_x509_store(void *dummy)
 {
     if (dummy) {
@@ -1080,6 +1098,7 @@
 
             apr_pool_create(&key->pool, r->pool);
 
+            // fixme normalise should do this
             switch(EVP_PKEY_base_id(pkey)) {
             case EVP_PKEY_RSA: {
                 key->common.type = REDWAX_KEY_RSA;
@@ -1845,6 +1864,374 @@
      * No secret file specified, and we are not on the console. Bail
      * out with an error.
      */
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t import_bags(redwax_tool_t *r, const char *file, const char *secret,
+        const STACK_OF(PKCS12_SAFEBAG) *bags, const char **pass, apr_size_t *pass_len);
+
+static apr_status_t import_bag(redwax_tool_t *r, const char *file, const char *secret,
+        const PKCS12_SAFEBAG *bag, const char **pass, apr_size_t *pass_len)
+{
+    EVP_PKEY *pkey;
+    const STACK_OF(X509_ATTRIBUTE) *attrs;
+    const PKCS8_PRIV_KEY_INFO *p8inf;
+
+    attrs = PKCS12_SAFEBAG_get0_attrs(bag);
+
+    switch (PKCS12_SAFEBAG_get_nid(bag)) {
+    case NID_keyBag: {
+
+        redwax_key_t *key;
+
+        BIO *kbio;
+        const ASN1_TYPE *label;
+
+        p8inf = PKCS12_SAFEBAG_get0_p8inf(bag);
+
+        if (!(pkey = EVP_PKCS82PKEY(p8inf))) {
+            redwax_openssl_print_errors(r);
+            return APR_EINVAL;
+        }
+
+        apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
+                apr_pool_cleanup_null);
+
+        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, (PKCS8_PRIV_KEY_INFO *)p8inf);
+
+        key = apr_array_push(r->keys_in);
+
+        apr_pool_create(&key->pool, r->pool);
+
+        // fixme duped by normalise key?
+        switch(EVP_PKEY_base_id(pkey)) {
+        case EVP_PKEY_RSA: {
+            key->common.type = REDWAX_KEY_RSA;
+            break;
+        }
+        }
+
+        key->len = BIO_get_mem_data(kbio, &key->der);
+
+        key->origin = file;
+
+        if ((label = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) {
+
+            if (label->type == V_ASN1_BMPSTRING) {
+
+                key->label = OPENSSL_uni2utf8(label->value.bmpstring->data,
+                        label->value.bmpstring->length);
+                key->label_len = strlen(key->label);
+
+                apr_pool_cleanup_register(r->pool, key->label, cleanup_alloc,
+                        apr_pool_cleanup_null);
+
+                break;
+            }
+        }
+
+        rt_run_normalise_key(r, key, 1);
+
+        redwax_print_error(r, "pkcs12-in: private key: %s\n",
+                redwax_pencode_base16_binary(r->pool, key->common.id_der,
+                        key->common.id_len, REDWAX_ENCODE_LOWER, NULL));
+
+        break;
+    }
+    case NID_pkcs8ShroudedKeyBag: {
+
+        redwax_key_t *key;
+
+        BIO *kbio;
+        const ASN1_TYPE *label;
+
+        if (!(*pass)) {
+            *pass = encrypt_secret(r, "PKCS12 import passphrase", file,
+                    secret,
+                    REDWAX_PKCS12_MIN, REDWAX_PKCS12_MAX, r->pool);
+            if (!(*pass)) {
+                return APR_ENOENT;
+            }
+            *pass_len = strlen(*pass);
+        }
+
+        if ((p8inf = PKCS12_decrypt_skey(bag, *pass, *pass_len)) == NULL) {
+            redwax_openssl_print_errors(r);
+            return APR_EINVAL;
+        }
+
+        apr_pool_cleanup_register(r->pool, p8inf, cleanup_p8inf,
+                apr_pool_cleanup_null);
+
+        if ((pkey = EVP_PKCS82PKEY(p8inf)) == NULL) {
+            redwax_openssl_print_errors(r);
+            return APR_EINVAL;
+        }
+
+        apr_pool_cleanup_register(r->pool, pkey, cleanup_evp_pkey,
+                apr_pool_cleanup_null);
+
+        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, (PKCS8_PRIV_KEY_INFO *)p8inf);
+
+        key = apr_array_push(r->keys_in);
+
+        apr_pool_create(&key->pool, r->pool);
+
+        // fixme duped by normalise key?
+        switch(EVP_PKEY_base_id(pkey)) {
+        case EVP_PKEY_RSA: {
+            key->common.type = REDWAX_KEY_RSA;
+            break;
+        }
+        }
+
+        key->len = BIO_get_mem_data(kbio, &key->der);
+
+        key->origin = file;
+
+        if ((label = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) {
+
+            if (label->type == V_ASN1_BMPSTRING) {
+
+                key->label = OPENSSL_uni2utf8(label->value.bmpstring->data,
+                        label->value.bmpstring->length);
+                key->label_len = strlen(key->label);
+
+                apr_pool_cleanup_register(r->pool, key->label, cleanup_alloc,
+                        apr_pool_cleanup_null);
+            }
+        }
+
+        rt_run_normalise_key(r, key, 1);
+
+        redwax_print_error(r, "pkcs12-in: private key: %s\n",
+                redwax_pencode_base16_binary(r->pool, key->common.id_der,
+                        key->common.id_len, REDWAX_ENCODE_LOWER, NULL));
+
+        break;
+    }
+    case NID_certBag: {
+
+        redwax_certificate_t *cert;
+
+        BIO *bio;
+        const ASN1_TYPE *label;
+
+        X509 *x;
+
+        if (PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate) {
+            break;
+        }
+
+        x = PKCS12_SAFEBAG_get1_cert(bag);
+
+        apr_pool_cleanup_register(r->pool, x, cleanup_x509,
+                apr_pool_cleanup_null);
+
+        if (!x) {
+            redwax_print_error(r, "Could not read certificate from '%s', skipping.\n",
+                    file);
+            redwax_openssl_print_errors(r);
+            break;
+        }
+
+        if (X509_check_ca(x)) {
+
+            cert = apr_array_push(r->intermediates_in);
+
+            apr_pool_create(&cert->pool, r->pool);
+
+            cert->common.type = REDWAX_CERTIFICATE_X509;
+            cert->common.category = REDWAX_CERTIFICATE_INTERMEDIATE;
+
+            redwax_print_error(r, "pkcs12-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, "pkcs12-in: certificate: %s\n",
+                    redwax_openssl_name(cert->pool,
+                            X509_get_subject_name(x)));
+
+        }
+
+        if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+            return APR_ENOMEM;
+        }
+
+        apr_pool_cleanup_register(r->pool, bio, cleanup_bio,
+                apr_pool_cleanup_null);
+
+        i2d_X509_bio(bio, x);
+
+        cert->len = BIO_get_mem_data(bio, &cert->der);
+
+        cert->origin = file;
+
+        if ((label = PKCS12_SAFEBAG_get0_attr(bag, NID_friendlyName))) {
+
+            if (label->type == V_ASN1_BMPSTRING) {
+
+                cert->label = OPENSSL_uni2utf8(label->value.bmpstring->data,
+                        label->value.bmpstring->length);
+                cert->label_len = strlen(cert->label);
+
+                apr_pool_cleanup_register(r->pool, cert->label, cleanup_alloc,
+                        apr_pool_cleanup_null);
+            }
+        }
+
+        rt_run_normalise_certificate(r, cert, 1);
+
+        break;
+    }
+    case NID_safeContentsBag: {
+
+        return import_bags(r, file, secret, PKCS12_SAFEBAG_get0_safes(bag),
+                                     pass, pass_len);
+
+    }
+    default:
+
+        break;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t import_bags(redwax_tool_t *r, const char *file, const char *secret,
+        const STACK_OF(PKCS12_SAFEBAG) *bags, const char **pass, apr_size_t *pass_len)
+{
+    int i;
+
+    apr_status_t status;
+
+    for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
+
+        status = import_bag(r, file, secret, sk_PKCS12_SAFEBAG_value(bags, i), pass, pass_len);
+
+        if (APR_SUCCESS != status) {
+            return status;
+        }
+
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_process_pkcs12_in(redwax_tool_t *r,
+        const char *file, const char *secret)
+{
+    PKCS12 *p12 = NULL;
+    STACK_OF(PKCS7) *asafes = NULL;
+    STACK_OF(PKCS12_SAFEBAG) *bags;
+
+    BIO *bio;
+
+    const char *pass = NULL;
+    apr_size_t pass_len = 0;
+
+    apr_status_t status;
+
+    int i, bagnid;
+
+    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_read_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 (!(p12 = d2i_PKCS12_bio(bio, NULL))) {
+        redwax_openssl_print_errors(r);
+        return APR_ENOENT;
+    }
+
+    if (!(asafes = PKCS12_unpack_authsafes(p12))) {
+        redwax_openssl_print_errors(r);
+        return APR_ENOENT;
+    }
+
+    apr_pool_cleanup_register(r->pool, asafes, cleanup_sk_pkcs7,
+            apr_pool_cleanup_null);
+
+    for (i = 0; i < sk_PKCS7_num(asafes); i++) {
+
+        PKCS7 *p7;
+
+        p7 = sk_PKCS7_value(asafes, i);
+
+        bagnid = OBJ_obj2nid(p7->type);
+
+        if (bagnid == NID_pkcs7_data) {
+
+            bags = PKCS12_unpack_p7data(p7);
+        }
+        else if (bagnid == NID_pkcs7_encrypted) {
+
+            if (!pass) {
+                pass = encrypt_secret(r, "PKCS12 import passphrase", file,
+                        secret,
+                        REDWAX_PKCS12_MIN, REDWAX_PKCS12_MAX, r->pool);
+                if (!pass) {
+                    return APR_ENOENT;
+                }
+                pass_len = strlen(pass);
+            }
+
+            bags = PKCS12_unpack_p7encdata(p7, pass, pass_len);
+        }
+        else {
+            continue;
+        }
+
+        if (!bags) {
+            redwax_openssl_print_errors(r);
+            return APR_ENOENT;
+        }
+        apr_pool_cleanup_register(r->pool, bags, cleanup_sk_pkcs2_safebag,
+                apr_pool_cleanup_null);
+
+        status = import_bags(r, file, secret, bags, &pass, &pass_len);
+        if (APR_SUCCESS != status) {
+            return status;
+        }
+
+    }
 
     return APR_SUCCESS;
 }
@@ -3084,6 +3471,7 @@
     rt_hook_complete_verify_param(redwax_openssl_complete_verify_param, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_set_verify_param(redwax_openssl_set_verify_param, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_process_pem_in(redwax_openssl_process_pem_in, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_process_pkcs12_in(redwax_openssl_process_pkcs12_in, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_complete_filter(redwax_openssl_complete_filter_verify, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_process_filter(redwax_openssl_process_filter_verify, NULL, NULL, APR_HOOK_MIDDLE);
     rt_hook_complete_filter(redwax_openssl_complete_filter_search, NULL, NULL, APR_HOOK_MIDDLE);



More information about the rs-commit mailing list