diff --git a/.gitignore b/.gitignore
index 5f094c1..4798d97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/sscg-2.3.3-stripped.tar.xz
+SOURCES/sscg-3.0.0.tar.xz
diff --git a/.sscg.metadata b/.sscg.metadata
index 7151dd4..ba54818 100644
--- a/.sscg.metadata
+++ b/.sscg.metadata
@@ -1 +1 @@
-6e880fc36f7d1ebf4a9668dbcb9276b3afcb2904 SOURCES/sscg-2.3.3-stripped.tar.xz
+81e3b33e118edff96583314ceb4bfde9a1e6b45c SOURCES/sscg-3.0.0.tar.xz
diff --git a/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch b/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch
new file mode 100644
index 0000000..5ad7b9d
--- /dev/null
+++ b/SOURCES/0001-Drop-usage-of-ERR_GET_FUNC.patch
@@ -0,0 +1,34 @@
+From d2277e711bb16e3b98f43565e71b7865b5fed423 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh@redhat.com>
+Date: Sat, 7 Aug 2021 11:48:04 -0400
+Subject: [PATCH 1/2] Drop usage of ERR_GET_FUNC()
+
+This macro was dropped in OpenSSL 3.0 and has actually not been
+providing a valid return code for some time.
+
+Related: rhbz#1964837
+
+Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
+---
+ include/sscg.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/include/sscg.h b/include/sscg.h
+index faf86ba4f68e186bd35c7bc3ec77b98b8e37d253..851dc93175607e5223a70ef40a5feb24b7b69215 100644
+--- a/include/sscg.h
++++ b/include/sscg.h
+@@ -94,11 +94,10 @@
+       if (_sslret != 1)                                                       \
+         {                                                                     \
+           /* Get information about error from OpenSSL */                      \
+           unsigned long _ssl_error = ERR_get_error ();                        \
+           if ((ERR_GET_LIB (_ssl_error) == ERR_LIB_UI) &&                     \
+-              (ERR_GET_FUNC (_ssl_error) == UI_F_UI_SET_RESULT_EX) &&         \
+               ((ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_LARGE) ||      \
+                (ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_SMALL)))       \
+             {                                                                 \
+               fprintf (                                                       \
+                 stderr,                                                       \
+-- 
+2.33.0
+
diff --git a/SOURCES/0001-Generate-manpage.patch b/SOURCES/0001-Generate-manpage.patch
deleted file mode 100644
index cc55444..0000000
--- a/SOURCES/0001-Generate-manpage.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 71e2451c6ba4d5f17de9e24687b66b93f2e58954 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Mon, 17 Sep 2018 09:58:25 -0400
-Subject: [PATCH 1/6] Generate manpage
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- meson.build | 22 +++++++++++++++++++++-
- 1 file changed, 21 insertions(+), 1 deletion(-)
-
-diff --git a/meson.build b/meson.build
-index e6f33475cce6891d17656bcd10e1afabd43bdc07..a2ca4ba1472bfff61fbbd30ba1ddc7ecc89e723c 100644
---- a/meson.build
-+++ b/meson.build
-@@ -7,7 +7,7 @@ project('sscg', 'c',
-           'b_asneeded=true',
-         ],
-         license : 'MIT',
--        meson_version : '>=0.36.0')
-+        meson_version : '>=0.40.0')
- 
- cc = meson.get_compiler('c')
- test_cflags = [
-@@ -141,3 +141,23 @@ configure_file(
-     output : 'config.h',
-     configuration : cdata)
- 
-+# Generate a manpage from the POPT documentation
-+help2man = find_program('help2man')
-+
-+manpage = custom_target('manpage',
-+    output : 'sscg.8',
-+    capture : true,
-+    command : [
-+        help2man,
-+        '-s', '8',
-+        '-n', 'Tool for generating x.509 certificates',
-+        '-N',
-+        sscg,
-+    ],
-+    install : true,
-+    build_by_default : true,
-+    install_dir : join_paths(
-+        get_option('prefix'),
-+        get_option('mandir'),
-+        'man8'),
-+)
--- 
-2.23.0
-
diff --git a/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch b/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch
deleted file mode 100644
index 3fd62ce..0000000
--- a/SOURCES/0002-Adjust-defaults-based-on-system-security-level.patch
+++ /dev/null
@@ -1,208 +0,0 @@
-From 942d9fa4f582a372af3d0bd499f073760dec2335 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Tue, 27 Nov 2018 13:24:37 -0500
-Subject: [PATCH 2/6] Adjust defaults based on system security level
-
-Also permit arbitrary keylengths.
-
-Disallow keylengths smaller than the configured system minimum.
-
-Resolves: rhbz#1653323
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- config.h.in    |  1 -
- include/sscg.h |  1 +
- meson.build    | 10 ++++++--
- src/sscg.c     | 64 ++++++++++++++++++++++++++++++++++++++++++++++----
- 4 files changed, 68 insertions(+), 8 deletions(-)
- delete mode 100644 config.h.in
-
-diff --git a/config.h.in b/config.h.in
-deleted file mode 100644
-index 6044a4355f6c8bfac8d36e533f48f395c597e5ac..0000000000000000000000000000000000000000
---- a/config.h.in
-+++ /dev/null
-@@ -1 +0,0 @@
--#define PACKAGE_VERSION "@version@"
-diff --git a/include/sscg.h b/include/sscg.h
-index 2bd42bbee965c754efb91febd10b6a94af6f508e..3e97cfe49a5cd8fc734ecf43a94156e376227eb7 100644
---- a/include/sscg.h
-+++ b/include/sscg.h
-@@ -139,6 +139,7 @@ struct sscg_options
- 
-   /* Encryption requirements */
-   int key_strength;
-+  int minimum_key_strength;
-   const EVP_MD *hash_fn;
- 
-   /* Output Files */
-diff --git a/meson.build b/meson.build
-index a2ca4ba1472bfff61fbbd30ba1ddc7ecc89e723c..c7b08ed3d6dff686f08a90ca869ba5881a9e8aaa 100644
---- a/meson.build
-+++ b/meson.build
-@@ -34,6 +34,7 @@ endforeach
- 
- pkg = import('pkgconfig')
- crypto = dependency('libcrypto')
-+ssl = dependency('libssl')
- path_utils = dependency('path_utils')
- talloc = dependency('talloc')
- 
-@@ -49,6 +50,10 @@ else
-     popt_incdirs = include_directories('subprojects/popt')
- endif
- 
-+has_get_sec_level = cc.has_function(
-+    'SSL_CTX_get_security_level',
-+    dependencies: [ ssl])
-+
- sscg_lib_srcs = [
-     'src/authority.c',
-     'src/bignum.c',
-@@ -70,6 +75,7 @@ sscg_lib = static_library(
-     sources : sscg_lib_srcs,
-     dependencies : [
-         crypto,
-+        ssl,
-         talloc,
-     ],
-     install : false,
-@@ -135,9 +141,9 @@ init_bignum_test = executable(
- test('init_bignum_test', init_bignum_test)
- 
- cdata = configuration_data()
--cdata.set('version', meson.project_version())
-+cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
-+cdata.set('HAVE_SSL_CTX_GET_SECURITY_LEVEL', has_get_sec_level)
- configure_file(
--    input : 'config.h.in',
-     output : 'config.h',
-     configuration : cdata)
- 
-diff --git a/src/sscg.c b/src/sscg.c
-index b2c7cbbfd9dc69d9f55a18bc91ed6023c0e64c2e..85a42404aa94524b560755d506b893300a4414cd 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -17,6 +17,7 @@
-     Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
- */
- 
-+#define _GNU_SOURCE
- #include <popt.h>
- #include <stdlib.h>
- #include <stdio.h>
-@@ -25,6 +26,7 @@
- #include <path_utils.h>
- #include <unistd.h>
- #include <openssl/evp.h>
-+#include <openssl/ssl.h>
- #include <sys/param.h>
- 
- #include "config.h"
-@@ -32,11 +34,59 @@
- #include "include/authority.h"
- #include "include/service.h"
- 
-+static int
-+get_security_level (void)
-+{
-+#ifdef HAVE_SSL_CTX_GET_SECURITY_LEVEL
-+  SSL_CTX *ssl_ctx = SSL_CTX_new (TLS_method ());
-+  int security_level = SSL_CTX_get_security_level (ssl_ctx);
-+  SSL_CTX_free (ssl_ctx);
-+  ssl_ctx = NULL;
-+  return security_level;
-+#else
-+  return 0;
-+#endif
-+}
-+
- static int
- set_default_options (struct sscg_options *opts)
- {
-+  int security_level = get_security_level ();
-+
-   opts->lifetime = 3650;
--  opts->key_strength = 2048;
-+
-+  /* Select the default key strength based on the system security level
-+   * See:
-+   * https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_security_level.html
-+   * for the specification of the minimums.
-+   */
-+  switch (security_level)
-+    {
-+    case 0:
-+    case 1:
-+    case 2:
-+      /* Security level 2 and below permits lower key-strengths, but SSCG
-+       * will set a minimum of 2048 bits
-+       */
-+      opts->key_strength = 2048;
-+      break;
-+
-+    case 3: opts->key_strength = 3072; break;
-+
-+    case 4: opts->key_strength = 7680; break;
-+
-+    default:
-+      /* Unknown security level. Default to the highest we know about */
-+      fprintf (stderr,
-+               "Unknown system security level %d. Defaulting to highest-known "
-+               "level.\n",
-+               security_level);
-+      /* Fall through */
-+
-+    case 5: opts->key_strength = 15360; break;
-+    }
-+
-+  opts->minimum_key_strength = opts->key_strength;
-   return 0;
- }
- 
-@@ -117,6 +167,7 @@ main (int argc, const char **argv)
-   size_t i;
-   poptContext pc;
-   struct sscg_options *options;
-+  char *minimum_key_strength_help = NULL;
- 
-   char *country = NULL;
-   char *state = NULL;
-@@ -172,6 +223,9 @@ main (int argc, const char **argv)
-   if (ret != EOK)
-     goto done;
- 
-+  minimum_key_strength_help =
-+    talloc_asprintf (main_ctx, "%d or larger", options->minimum_key_strength);
-+
-   options->verbosity = SSCG_DEFAULT;
-   struct poptOption long_options[] = {
-     POPT_AUTOHELP{ "quiet",
-@@ -293,7 +347,7 @@ main (int argc, const char **argv)
-       &options->key_strength,
-       0,
-       _ ("Strength of the certificate private keys in bits."),
--      _ ("{512,1024,2048,4096}") },
-+      minimum_key_strength_help },
-     {
-       "hash-alg",
-       '\0',
-@@ -529,11 +583,11 @@ main (int argc, const char **argv)
-         }
-     }
- 
--  if (options->key_strength != 512 && options->key_strength != 1024 &&
--      options->key_strength != 2048 && options->key_strength != 4096)
-+  if (options->key_strength < options->minimum_key_strength)
-     {
-       fprintf (stderr,
--               "Key strength must be one of {512, 1024, 2048, 4096}.\n");
-+               "Key strength must be at least %d bits.\n",
-+               options->minimum_key_strength);
-       ret = EINVAL;
-       goto done;
-     }
--- 
-2.23.0
-
diff --git a/SOURCES/0002-Correct-certificate-lifetime-calculation.patch b/SOURCES/0002-Correct-certificate-lifetime-calculation.patch
new file mode 100644
index 0000000..5a0b87b
--- /dev/null
+++ b/SOURCES/0002-Correct-certificate-lifetime-calculation.patch
@@ -0,0 +1,46 @@
+From 87604820a935f87a8f533e3f294419d27c0514eb Mon Sep 17 00:00:00 2001
+From: Allison Karlitskaya <allison.karlitskaya@redhat.com>
+Date: Tue, 26 Oct 2021 12:32:13 +0200
+Subject: [PATCH 2/2] Correct certificate lifetime calculation
+
+sscg allows passing the certificate lifetime, as a number of days, as a
+commandline argument.  It converts this value to seconds using the
+formula
+
+  days * 24 * 3650
+
+which is incorrect.  The correct value is 3600.
+
+This effectively adds an extra 20 minutes to the lifetime of the
+certificate for each day as given on the commandline, and was enough to
+cause some new integration tests in cockpit to fail.
+
+Interestingly, 3650 is the old default value for the number of days of
+certificate validity (~10 years) so this probably slipped in as a sort
+of muscle-memory-assisted typo.
+
+Let's just write `24 * 60 * 60` to make things clear.
+---
+ src/x509.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/x509.c b/src/x509.c
+index dc1594a4bdcb9d81607f0fe5ad2d4562e5edb533..7c7e4dfe56d5756862f3e0f851941e846ce96f31 100644
+--- a/src/x509.c
++++ b/src/x509.c
+@@ -416,11 +416,11 @@ sscg_sign_x509_csr (TALLOC_CTX *mem_ctx,
+       X509_set_issuer_name (cert, X509_REQ_get_subject_name (csr));
+     }
+ 
+   /* set time */
+   X509_gmtime_adj (X509_get_notBefore (cert), 0);
+-  X509_gmtime_adj (X509_get_notAfter (cert), days * 24 * 3650);
++  X509_gmtime_adj (X509_get_notAfter (cert), days * 24 * 60 * 60);
+ 
+   /* set subject */
+   subject = X509_NAME_dup (X509_REQ_get_subject_name (csr));
+   sslret = X509_set_subject_name (cert, subject);
+   CHECK_SSL (sslret, X509_set_subject_name);
+-- 
+2.33.0
+
diff --git a/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch b/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch
deleted file mode 100644
index 66e8224..0000000
--- a/SOURCES/0003-Adjust-hash-defaults-based-on-system-security-level.patch
+++ /dev/null
@@ -1,115 +0,0 @@
-From 298015e8a7cf35cc0de581203b44826d2ae1d406 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Wed, 28 Nov 2018 08:00:08 -0500
-Subject: [PATCH 3/6] Adjust hash defaults based on system security level
-
-Unlike the key-strength, this does not set a minimum level because
-it's not a simple calculation. We will have to rely on libcrypto
-rejecting any explicitly-set algorithms as a violation of policy.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- include/sscg.h |  1 +
- src/sscg.c     | 40 +++++++++++++++++++++-------------------
- 2 files changed, 22 insertions(+), 19 deletions(-)
-
-diff --git a/include/sscg.h b/include/sscg.h
-index 3e97cfe49a5cd8fc734ecf43a94156e376227eb7..fc90b81a0060af28529f3be6922b1b1501559300 100644
---- a/include/sscg.h
-+++ b/include/sscg.h
-@@ -140,6 +140,7 @@ struct sscg_options
-   /* Encryption requirements */
-   int key_strength;
-   int minimum_key_strength;
-+  char *hash_alg;
-   const EVP_MD *hash_fn;
- 
-   /* Output Files */
-diff --git a/src/sscg.c b/src/sscg.c
-index 85a42404aa94524b560755d506b893300a4414cd..58855f764480d24d6c0f57460b22a3a83281e37e 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -66,14 +66,21 @@ set_default_options (struct sscg_options *opts)
-     case 1:
-     case 2:
-       /* Security level 2 and below permits lower key-strengths, but SSCG
--       * will set a minimum of 2048 bits
-+       * will set a minimum of 2048 bits and the sha256 hash algorithm.
-        */
-+      opts->hash_alg = talloc_strdup (opts, "sha256");
-       opts->key_strength = 2048;
-       break;
- 
--    case 3: opts->key_strength = 3072; break;
-+    case 3:
-+      opts->hash_alg = talloc_strdup (opts, "sha256");
-+      opts->key_strength = 3072;
-+      break;
- 
--    case 4: opts->key_strength = 7680; break;
-+    case 4:
-+      opts->hash_alg = talloc_strdup (opts, "sha384");
-+      opts->key_strength = 7680;
-+      break;
- 
-     default:
-       /* Unknown security level. Default to the highest we know about */
-@@ -83,7 +90,10 @@ set_default_options (struct sscg_options *opts)
-                security_level);
-       /* Fall through */
- 
--    case 5: opts->key_strength = 15360; break;
-+    case 5:
-+      opts->hash_alg = talloc_strdup (opts, "sha512");
-+      opts->key_strength = 15360;
-+      break;
-     }
- 
-   opts->minimum_key_strength = opts->key_strength;
-@@ -177,7 +187,6 @@ main (int argc, const char **argv)
-   char *email = NULL;
-   char *hostname = NULL;
-   char *packagename;
--  char *hash_alg = NULL;
-   char **alternative_names = NULL;
- 
-   char *ca_file = NULL;
-@@ -351,10 +360,10 @@ main (int argc, const char **argv)
-     {
-       "hash-alg",
-       '\0',
--      POPT_ARG_STRING,
--      &hash_alg,
-+      POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
-+      &options->hash_alg,
-       0,
--      _ ("Hashing algorithm to use for signing. (default: sha256)"),
-+      _ ("Hashing algorithm to use for signing."),
-       _ ("{sha256,sha384,sha512}"),
-     },
-     {
-@@ -592,17 +601,10 @@ main (int argc, const char **argv)
-       goto done;
-     }
- 
--  if (!hash_alg)
--    {
--      /* Default to SHA256 */
--      options->hash_fn = EVP_sha256 ();
--    }
--  else
--    {
--      /* TODO: restrict this to approved hashes.
--         * For now, we'll only list SHA[256|384|512] in the help */
--      options->hash_fn = EVP_get_digestbyname (hash_alg);
--    }
-+  /* TODO: restrict this to approved hashes.
-+   * For now, we'll only list SHA[256|384|512] in the help */
-+  options->hash_fn = EVP_get_digestbyname (options->hash_alg);
-+
-   if (!options->hash_fn)
-     {
-       fprintf (stderr, "Unsupported hashing algorithm.");
--- 
-2.23.0
-
diff --git a/SOURCES/0003-Truncate-IP-address-in-SAN.patch b/SOURCES/0003-Truncate-IP-address-in-SAN.patch
new file mode 100644
index 0000000..c492f38
--- /dev/null
+++ b/SOURCES/0003-Truncate-IP-address-in-SAN.patch
@@ -0,0 +1,68 @@
+From 0875cd6169e876c4296a307631d49b801fc686dc Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh@redhat.com>
+Date: Tue, 8 Mar 2022 16:33:35 -0500
+Subject: [PATCH] Truncate IP address in SAN
+
+In OpenSSL 1.1, this was done automatically when addind a SAN extension,
+but in OpenSSL 3.0 it is rejected as an invalid input.
+
+Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
+---
+ src/x509.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/src/x509.c b/src/x509.c
+index 7c7e4dfe56d5756862f3e0f851941e846ce96f31..e828ec725b23d7ea79393151e7bb436e2f61bdb8 100644
+--- a/src/x509.c
++++ b/src/x509.c
+@@ -131,10 +131,11 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx,
+   size_t i;
+   X509_NAME *subject;
+   char *alt_name = NULL;
+   char *tmp = NULL;
+   char *san = NULL;
++  char *slash = NULL;
+   TALLOC_CTX *tmp_ctx;
+   X509_EXTENSION *ex = NULL;
+   struct sscg_x509_req *csr;
+ 
+   /* Make sure we have a key available */
+@@ -265,10 +266,16 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx,
+                 tmp_ctx, "DNS:%s", certinfo->subject_alt_names[i]);
+             }
+           else
+             {
+               san = talloc_strdup (tmp_ctx, certinfo->subject_alt_names[i]);
++              /* SAN IP addresses cannot include the subnet mask */
++              if ((slash = strchr (san, '/')))
++                {
++                  /* Truncate at the slash */
++                  *slash = '\0';
++                }
+             }
+           CHECK_MEM (san);
+ 
+           if (strnlen (san, MAXHOSTNAMELEN + 5) > MAXHOSTNAMELEN + 4)
+             {
+@@ -287,11 +294,17 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx,
+           alt_name = tmp;
+         }
+     }
+ 
+   ex = X509V3_EXT_conf_nid (NULL, NULL, NID_subject_alt_name, alt_name);
+-  CHECK_MEM (ex);
++  if (!ex)
++    {
++      ret = EINVAL;
++      fprintf (stderr, "Invalid subjectAlternativeName: %s\n", alt_name);
++      goto done;
++    }
++
+   sk_X509_EXTENSION_push (certinfo->extensions, ex);
+ 
+   /* Set the public key for the certificate */
+   sslret = X509_REQ_set_pubkey (csr->x509_req, spkey->evp_pkey);
+   CHECK_SSL (sslret, X509_REQ_set_pubkey (OU));
+-- 
+2.35.1
+
diff --git a/SOURCES/0004-Properly-check-all-return-values.patch b/SOURCES/0004-Properly-check-all-return-values.patch
deleted file mode 100644
index 9225fe7..0000000
--- a/SOURCES/0004-Properly-check-all-return-values.patch
+++ /dev/null
@@ -1,51 +0,0 @@
-From 9e4497d1dd2a337be1f69e0cfb24ce8080690ccf Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Wed, 28 Nov 2018 09:16:29 -0500
-Subject: [PATCH 4/6] Properly check all return values
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- src/authority.c | 1 +
- src/service.c   | 1 +
- src/x509.c      | 1 +
- 3 files changed, 3 insertions(+)
-
-diff --git a/src/authority.c b/src/authority.c
-index b735868416b7fb5d016f0854baf0f27cd5f98b26..4e0dccc6c1210beffb38acd9f7dfb6108ca4a4ad 100644
---- a/src/authority.c
-+++ b/src/authority.c
-@@ -180,6 +180,7 @@ create_private_CA (TALLOC_CTX *mem_ctx,
- 
-   /* Finalize the CSR */
-   ret = sscg_x509v3_csr_finalize (ca_certinfo, pkey, csr);
-+  CHECK_OK (ret);
- 
-   if (options->verbosity >= SSCG_DEBUG)
-     {
-diff --git a/src/service.c b/src/service.c
-index b292e94063f032fd3c34a8134702063ea46bfa0c..34c976dbe905528000b181c24d1fa95da3cd1377 100644
---- a/src/service.c
-+++ b/src/service.c
-@@ -126,6 +126,7 @@ create_service_cert (TALLOC_CTX *mem_ctx,
- 
-   /* Finalize the CSR */
-   ret = sscg_x509v3_csr_finalize (svc_certinfo, pkey, csr);
-+  CHECK_OK (ret);
- 
-   if (options->verbosity >= SSCG_DEBUG)
-     {
-diff --git a/src/x509.c b/src/x509.c
-index 6d152fc969d745cc5cf085116c8688866f9d6ab4..18f0627bc64e7cb503a9e81c36dbe726186d1144 100644
---- a/src/x509.c
-+++ b/src/x509.c
-@@ -41,6 +41,7 @@ sscg_generate_serial (TALLOC_CTX *mem_ctx, struct sscg_bignum **serial)
-     }
- 
-   ret = sscg_init_bignum (tmp_ctx, 0, &bn);
-+  CHECK_OK (ret);
- 
-   /* We'll create a random number of sizeof(unsigned long) - 1 bits
-        to use as the serial. We use unsigned long to ensure that it
--- 
-2.23.0
-
diff --git a/SOURCES/0005-Add-password-support-for-private-keys.patch b/SOURCES/0005-Add-password-support-for-private-keys.patch
deleted file mode 100644
index 4e21a9a..0000000
--- a/SOURCES/0005-Add-password-support-for-private-keys.patch
+++ /dev/null
@@ -1,273 +0,0 @@
-From 7190d08e1a166455e767769492b8c6b9f41bc0da Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Wed, 5 Jun 2019 17:08:23 -0400
-Subject: [PATCH 5/6] Add password support for private keys
-
-Fixes: https://github.com/sgallagher/sscg/issues/14
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- include/sscg.h |   7 +++
- src/sscg.c     | 152 ++++++++++++++++++++++++++++++++++++++++++++++++-
- 2 files changed, 157 insertions(+), 2 deletions(-)
-
-diff --git a/include/sscg.h b/include/sscg.h
-index fc90b81a0060af28529f3be6922b1b1501559300..ce9a7916e9432d0843d82af61d56ea7238ded682 100644
---- a/include/sscg.h
-+++ b/include/sscg.h
-@@ -141,8 +141,15 @@ struct sscg_options
-   int key_strength;
-   int minimum_key_strength;
-   char *hash_alg;
-+  char *cipher_alg;
-+  const EVP_CIPHER *cipher;
-   const EVP_MD *hash_fn;
- 
-+  bool ca_key_pass_prompt;
-+  char *ca_key_pass;
-+  bool cert_key_pass_prompt;
-+  char *cert_key_pass;
-+
-   /* Output Files */
-   char *ca_file;
-   char *ca_key_file;
-diff --git a/src/sscg.c b/src/sscg.c
-index 58855f764480d24d6c0f57460b22a3a83281e37e..9dc926c77038105ca881a612cccd1913bc2d42f1 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -97,6 +97,9 @@ set_default_options (struct sscg_options *opts)
-     }
- 
-   opts->minimum_key_strength = opts->key_strength;
-+
-+  opts->cipher_alg = talloc_strdup (opts, "aes-256-cbc");
-+
-   return 0;
- }
- 
-@@ -170,6 +173,42 @@ done:
-   return ret;
- }
- 
-+
-+/* This function takes a copy of a string into a talloc hierarchy and memsets
-+ * the original string to zeroes to avoid leaking it when that memory is freed.
-+ */
-+static char *
-+sscg_secure_string_steal (TALLOC_CTX *mem_ctx, char *src)
-+{
-+  char *dest = talloc_strdup (mem_ctx, src);
-+
-+  memset (src, 0, strlen (src));
-+
-+  return dest;
-+}
-+
-+
-+static int
-+sscg_options_destructor (TALLOC_CTX *opts)
-+{
-+  struct sscg_options *options =
-+    talloc_get_type_abort (opts, struct sscg_options);
-+
-+  /* Zero out the memory before freeing it so we don't leak passwords */
-+  if (options->ca_key_pass)
-+    {
-+      memset (options->ca_key_pass, 0, strlen (options->ca_key_pass));
-+    }
-+
-+  if (options->cert_key_pass)
-+    {
-+      memset (options->cert_key_pass, 0, strlen (options->cert_key_pass));
-+    }
-+
-+  return 0;
-+}
-+
-+
- int
- main (int argc, const char **argv)
- {
-@@ -196,8 +235,11 @@ main (int argc, const char **argv)
- 
-   int ca_mode = 0644;
-   int ca_key_mode = 0600;
-+  char *ca_key_password = NULL;
-+
-   int cert_mode = 0644;
-   int cert_key_mode = 0600;
-+  char *cert_key_password = NULL;
- 
-   char *create_mode = NULL;
- 
-@@ -227,6 +269,7 @@ main (int argc, const char **argv)
- 
-   options = talloc_zero (main_ctx, struct sscg_options);
-   CHECK_MEM (options);
-+  talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor);
- 
-   ret = set_default_options (options);
-   if (ret != EOK)
-@@ -366,6 +409,16 @@ main (int argc, const char **argv)
-       _ ("Hashing algorithm to use for signing."),
-       _ ("{sha256,sha384,sha512}"),
-     },
-+    {
-+      "cipher-alg",
-+      '\0',
-+      POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
-+      &options->cipher_alg,
-+      0,
-+      _ ("Cipher to use for encrypting key files."),
-+      _ ("{des-ede3-cbc,aes-256-cbc}"),
-+    },
-+
-     {
-       "ca-file",
-       '\0',
-@@ -404,6 +457,29 @@ main (int argc, const char **argv)
-       _ ("File mode of the created CA key. (default: 0600)"),
-       _ ("0600"),
-     },
-+    {
-+      "ca-key-password",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &ca_key_password,
-+      0,
-+      _ ("Provide a password for the CA key file. Note that this will be "
-+         "visible in the process table for all users, so it should be used "
-+         "for testing purposes only. Use --ca-keypassfile or "
-+         "--ca-key-password-prompt for secure password entry."),
-+      NULL
-+    },
-+
-+    {
-+      "ca-key-password-prompt",
-+      'C',
-+      POPT_ARG_NONE,
-+      &options->ca_key_pass_prompt,
-+      0,
-+      _ ("Prompt to enter a password for the CA key file."),
-+      NULL
-+    },
-+
-     {
-       "cert-file",
-       '\0',
-@@ -442,6 +518,29 @@ main (int argc, const char **argv)
-       _ ("File mode of the created certificate key. (default: 0600)"),
-       _ ("0600"),
-     },
-+    {
-+      "cert-key-password",
-+      'p',
-+      POPT_ARG_STRING,
-+      &cert_key_password,
-+      0,
-+      _ ("Provide a password for the service key file. Note that this will be "
-+         "visible in the process table for all users, so this flag should be "
-+         "used for testing purposes only. Use --cert-keypassfile or "
-+         "--cert-key-password-prompt for secure password entry."),
-+      NULL
-+    },
-+
-+    {
-+      "cert-key-password-prompt",
-+      'P',
-+      POPT_ARG_NONE,
-+      &options->cert_key_pass_prompt,
-+      0,
-+      _ ("Prompt to enter a password for the service key file."),
-+      NULL
-+    },
-+
-     POPT_TABLEEND
-   };
- 
-@@ -592,6 +691,20 @@ main (int argc, const char **argv)
-         }
-     }
- 
-+  /* Password handling */
-+  if (ca_key_password)
-+    {
-+      options->ca_key_pass =
-+        sscg_secure_string_steal (options, ca_key_password);
-+    }
-+
-+  if (cert_key_password)
-+    {
-+      options->cert_key_pass =
-+        sscg_secure_string_steal (options, cert_key_password);
-+    }
-+
-+
-   if (options->key_strength < options->minimum_key_strength)
-     {
-       fprintf (stderr,
-@@ -601,6 +714,15 @@ main (int argc, const char **argv)
-       goto done;
-     }
- 
-+  /* Make sure we have a valid cipher */
-+  options->cipher = EVP_get_cipherbyname (options->cipher_alg);
-+  if (!options->cipher)
-+    {
-+      fprintf (stderr, "Invalid cipher specified: %s\n", options->cipher_alg);
-+      ret = EINVAL;
-+      goto done;
-+    }
-+
-   /* TODO: restrict this to approved hashes.
-    * For now, we'll only list SHA[256|384|512] in the help */
-   options->hash_fn = EVP_get_digestbyname (options->hash_alg);
-@@ -696,8 +818,21 @@ main (int argc, const char **argv)
-   cert_key_out = BIO_new_file (options->cert_key_file, create_mode);
-   CHECK_BIO (cert_key_out, options->cert_key_file);
- 
-+  /* This function has a default mechanism for prompting for the
-+   * password if it is passed a cipher and gets a NULL password.
-+   *
-+   * Only pass the cipher if we have a password or were instructed
-+   * to prompt for one.
-+   */
-   sret = PEM_write_bio_PrivateKey (
--    cert_key_out, svc_key->evp_pkey, NULL, NULL, 0, NULL, NULL);
-+    cert_key_out,
-+    svc_key->evp_pkey,
-+    options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher :
-+                                                              NULL,
-+    (unsigned char *)options->cert_key_pass,
-+    options->cert_key_pass ? strlen (options->cert_key_pass) : 0,
-+    NULL,
-+    NULL);
-   CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
-   BIO_get_fp (cert_key_out, &fp);
- 
-@@ -776,8 +911,21 @@ main (int argc, const char **argv)
-         }
-       CHECK_BIO (ca_key_out, options->ca_key_file);
- 
-+      /* This function has a default mechanism for prompting for the
-+       * password if it is passed a cipher and gets a NULL password.
-+       *
-+       * Only pass the cipher if we have a password or were instructed
-+       * to prompt for one.
-+       */
-       sret = PEM_write_bio_PrivateKey (
--        ca_key_out, cakey->evp_pkey, NULL, NULL, 0, NULL, NULL);
-+        ca_key_out,
-+        cakey->evp_pkey,
-+        options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher :
-+                                                              NULL,
-+        (unsigned char *)options->ca_key_pass,
-+        options->ca_key_pass ? strlen (options->ca_key_pass) : 0,
-+        NULL,
-+        NULL);
-       CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA));
-       BIO_get_fp (ca_key_out, &fp);
-       if (options->verbosity >= SSCG_DEBUG)
--- 
-2.23.0
-
diff --git a/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch b/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch
deleted file mode 100644
index 6487436..0000000
--- a/SOURCES/0006-Allow-specifying-keyfile-password-by-file.patch
+++ /dev/null
@@ -1,153 +0,0 @@
-From 9cb7daa54708dcf5e6500cd20ec7b1cc2f6f6350 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Mon, 10 Jun 2019 10:15:42 -0400
-Subject: [PATCH 6/6] Allow specifying keyfile password by file
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- src/sscg.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 84 insertions(+)
-
-diff --git a/src/sscg.c b/src/sscg.c
-index 9dc926c77038105ca881a612cccd1913bc2d42f1..a02e4df66c6cf9ec1865f425b4a15da82fbfdc72 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -34,6 +34,10 @@
- #include "include/authority.h"
- #include "include/service.h"
- 
-+
-+/* Same as OpenSSL CLI */
-+#define MAX_PW_LEN 1024
-+
- static int
- get_security_level (void)
- {
-@@ -209,6 +213,44 @@ sscg_options_destructor (TALLOC_CTX *opts)
- }
- 
- 
-+static char *
-+sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path)
-+{
-+  int i;
-+  BIO *pwdbio = NULL;
-+  char tpass[MAX_PW_LEN];
-+  char *tmp = NULL;
-+  char *password = NULL;
-+
-+  pwdbio = BIO_new_file (path, "r");
-+  if (pwdbio == NULL)
-+    {
-+      fprintf (stderr, "Can't open file %s\n", path);
-+      return NULL;
-+    }
-+
-+  i = BIO_gets (pwdbio, tpass, MAX_PW_LEN);
-+  BIO_free_all (pwdbio);
-+  pwdbio = NULL;
-+
-+  if (i <= 0)
-+    {
-+      fprintf (stderr, "Error reading password from BIO\n");
-+      return NULL;
-+    }
-+
-+  tmp = strchr (tpass, '\n');
-+  if (tmp != NULL)
-+    *tmp = 0;
-+
-+  password = talloc_strdup (mem_ctx, tpass);
-+
-+  memset (tpass, 0, MAX_PW_LEN);
-+
-+  return password;
-+}
-+
-+
- int
- main (int argc, const char **argv)
- {
-@@ -236,10 +278,12 @@ main (int argc, const char **argv)
-   int ca_mode = 0644;
-   int ca_key_mode = 0600;
-   char *ca_key_password = NULL;
-+  char *ca_key_passfile = NULL;
- 
-   int cert_mode = 0644;
-   int cert_key_mode = 0600;
-   char *cert_key_password = NULL;
-+  char *cert_key_passfile = NULL;
- 
-   char *create_mode = NULL;
- 
-@@ -470,6 +514,16 @@ main (int argc, const char **argv)
-       NULL
-     },
- 
-+    {
-+      "ca-key-passfile",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &ca_key_passfile,
-+      0,
-+      _ ("A file containing the password to encrypt the CA key file."),
-+      NULL
-+    },
-+
-     {
-       "ca-key-password-prompt",
-       'C',
-@@ -531,6 +585,16 @@ main (int argc, const char **argv)
-       NULL
-     },
- 
-+    {
-+      "cert-key-passfile",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &cert_key_passfile,
-+      0,
-+      _ ("A file containing the password to encrypt the service key file."),
-+      NULL
-+    },
-+
-     {
-       "cert-key-password-prompt",
-       'P',
-@@ -697,12 +761,32 @@ main (int argc, const char **argv)
-       options->ca_key_pass =
-         sscg_secure_string_steal (options, ca_key_password);
-     }
-+  else if (ca_key_passfile)
-+    {
-+      options->ca_key_pass = sscg_read_pw_file (options, ca_key_passfile);
-+      if (!options->ca_key_pass)
-+        {
-+          fprintf (
-+            stderr, "Failed to read passphrase from %s", ca_key_passfile);
-+          goto done;
-+        }
-+    }
- 
-   if (cert_key_password)
-     {
-       options->cert_key_pass =
-         sscg_secure_string_steal (options, cert_key_password);
-     }
-+  else if (cert_key_passfile)
-+    {
-+      options->cert_key_pass = sscg_read_pw_file (options, cert_key_passfile);
-+      if (!options->cert_key_pass)
-+        {
-+          fprintf (
-+            stderr, "Failed to read passphrase from %s", cert_key_passfile);
-+          goto done;
-+        }
-+    }
- 
- 
-   if (options->key_strength < options->minimum_key_strength)
--- 
-2.23.0
-
diff --git a/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch b/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch
deleted file mode 100644
index e22236e..0000000
--- a/SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch
+++ /dev/null
@@ -1,2651 +0,0 @@
-From ceed1c19b6002164482eb358570a91a9563ce694 Mon Sep 17 00:00:00 2001
-From: Tim Burke <tim.burke@gmail.com>
-Date: Wed, 2 Oct 2019 13:10:23 -0700
-Subject: [PATCH 7/7] Add support for client certificates and dhparams
-
-Resolves: rhbz#1720667
-
-Add --crl-file option
-
-... to (optionally) create an empty Certificate Revocation List file.
-
-Default the mode for the file to 0644, similar to the default for the CA
-certificate. User can override this with a new --crl-mode option.
-
-Closes #10.
-
-Add function for DH parameter generation
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Generate DH parameters file
-
-Adds CLI options to enable and control dhparam file generation.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Add serverAuth extendedKeyUsage for server certificates
-
-Related to https://github.com/sgallagher/sscg/issues/13
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Rename create_service_cert() to create_cert()
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Use a common macro for default file modes
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Add I/O utility routines
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Rework output file handling
-
-SSCG will now verify that it can open all of the files before it
-starts processing through them. In addition, it now has better
-validation that it isn't storing two keys into the same file, or
-dhparams/CRL in a file with other content.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Check for invalid file combinations
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Add support for client certificates
-
-Fixes: https://github.com/sgallagher/sscg/issues/13
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- include/{service.h => cert.h} |  19 +-
- include/dhparams.h            |  42 ++
- include/io_utils.h            |  93 +++++
- include/sscg.h                |  99 ++++-
- meson.build                   |  51 ++-
- meson_options.txt             |  24 ++
- src/cert.c                    | 191 +++++++++
- src/dhparams.c                | 146 +++++++
- src/io_utils.c                | 424 +++++++++++++++++++
- src/service.c                 | 164 --------
- src/sscg.c                    | 753 +++++++++++++++++++++-------------
- test/dhparams_test.c          | 106 +++++
- 12 files changed, 1651 insertions(+), 461 deletions(-)
- rename include/{service.h => cert.h} (67%)
- create mode 100644 include/dhparams.h
- create mode 100644 include/io_utils.h
- create mode 100644 meson_options.txt
- create mode 100644 src/cert.c
- create mode 100644 src/dhparams.c
- create mode 100644 src/io_utils.c
- delete mode 100644 src/service.c
- create mode 100644 test/dhparams_test.c
-
-diff --git a/include/service.h b/include/cert.h
-similarity index 67%
-rename from include/service.h
-rename to include/cert.h
-index 20083caf905f8e70360539053134c0e9702c304a..11d04e50ebb2e08af781d0aa14b9aec930ff2f50 100644
---- a/include/service.h
-+++ b/include/cert.h
-@@ -21,15 +21,16 @@
- #include "x509.h"
- #include "key.h"
- 
--#ifndef _SERVICE_H
--#define _SERVICE_H
-+#ifndef _SSCG_CERT_H
-+#define _SSCG_CERT_H
- 
- int
--create_service_cert (TALLOC_CTX *mem_ctx,
--                     const struct sscg_options *options,
--                     struct sscg_x509_cert *ca_cert,
--                     struct sscg_evp_pkey *ca_key,
--                     struct sscg_x509_cert **_svc_cert,
--                     struct sscg_evp_pkey **_svc_key);
-+create_cert (TALLOC_CTX *mem_ctx,
-+             const struct sscg_options *options,
-+             struct sscg_x509_cert *ca_cert,
-+             struct sscg_evp_pkey *ca_key,
-+             enum sscg_cert_type type,
-+             struct sscg_x509_cert **_svc_cert,
-+             struct sscg_evp_pkey **_svc_key);
- 
--#endif /* _SERVICE_H */
-+#endif /* _SSCG_CERT_H */
-diff --git a/include/dhparams.h b/include/dhparams.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..baa4fb3b297c5747fcce20f6950239779f71f19a
---- /dev/null
-+++ b/include/dhparams.h
-@@ -0,0 +1,42 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+#ifndef _SSCG_DHPARAMS_H
-+#define _SSCG_DHPARAMS_H
-+
-+#include <talloc.h>
-+
-+#include "include/sscg.h"
-+
-+struct sscg_dhparams
-+{
-+  int prime_len;
-+  int generator;
-+  DH *dh;
-+  BN_GENCB *cb;
-+};
-+
-+int
-+create_dhparams (TALLOC_CTX *mem_ctx,
-+                 enum sscg_verbosity options,
-+                 int prime_len,
-+                 int generator,
-+                 struct sscg_dhparams **_dhparams);
-+
-+#endif /* _SSCG_DHPARAMS_H */
-diff --git a/include/io_utils.h b/include/io_utils.h
-new file mode 100644
-index 0000000000000000000000000000000000000000..6a89a476b3d982447b6603153c6765835cd67464
---- /dev/null
-+++ b/include/io_utils.h
-@@ -0,0 +1,93 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+#ifndef _SSCG_IO_UTILS_H
-+#define _SSCG_IO_UTILS_H
-+
-+#include <openssl/ssl.h>
-+#include <stdbool.h>
-+#include <talloc.h>
-+
-+#include "include/sscg.h"
-+
-+
-+struct sscg_stream
-+{
-+  BIO *bio;
-+  char *path;
-+  int mode;
-+  int filetypes;
-+};
-+
-+
-+int
-+sscg_normalize_path (TALLOC_CTX *mem_ctx,
-+                     const char *path,
-+                     char **_normalized_path);
-+
-+
-+struct sscg_stream *
-+sscg_io_utils_get_stream_by_path (struct sscg_stream **streams,
-+                                  const char *normalized_path);
-+
-+
-+struct sscg_stream *
-+sscg_io_utils_get_stream_by_type (struct sscg_stream **streams,
-+                                  enum sscg_file_type filetype);
-+
-+
-+BIO *
-+sscg_io_utils_get_bio_by_type (struct sscg_stream **streams,
-+                               enum sscg_file_type filetype);
-+
-+
-+const char *
-+sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
-+                                enum sscg_file_type filetype);
-+
-+
-+/**
-+ * sscg_io_utils_add_output_file:
-+ * @streams: The array of streams from the sscg_options
-+ * @filetype:
-+ * @path: The path to the file on disk.
-+ * @mode: The filesystem mode this file should have when written to disk.
-+ * See chmod(1) for the possible values.
-+ * @overwrite: If true, replace any existing file at @normalized_path. If
-+ * false, opening will fail if it already exists and return an error.
-+ *
-+ * Prepares all output filenames to be opened. Files are not created until
-+ * sscg_io_utils_open_output_files() is called.
-+ */
-+int
-+sscg_io_utils_add_output_file (struct sscg_stream **streams,
-+                               enum sscg_file_type filetype,
-+                               const char *path,
-+                               int mode);
-+
-+
-+int
-+sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite);
-+
-+/* If this function fails, some of the output files may be left as 0400 */
-+int
-+sscg_io_utils_finalize_output_files (struct sscg_stream **streams);
-+
-+
-+#endif /* _SSCG_IO_UTILS_H */
-diff --git a/include/sscg.h b/include/sscg.h
-index ce9a7916e9432d0843d82af61d56ea7238ded682..2744404c25c68ed905ca621bb955e0c04b33ca81 100644
---- a/include/sscg.h
-+++ b/include/sscg.h
-@@ -27,6 +27,8 @@
- #include <talloc.h>
- #include <stdint.h>
- 
-+#include "include/io_utils.h"
-+
- #ifndef _SSCG_H
- #define _SSCG_H
- 
-@@ -108,6 +110,13 @@
-     }                                                                         \
-   while (0)
- 
-+
-+#define SSCG_CERT_DEFAULT_MODE 0644
-+#define SSCG_CERT_DEFAULT_MODE_HELP _ ("0644")
-+#define SSCG_KEY_DEFAULT_MODE 0600
-+#define SSCG_KEY_DEFAULT_MODE_HELP _ ("0600")
-+
-+
- enum sscg_verbosity
- {
-   SSCG_QUIET = -1,
-@@ -116,6 +125,75 @@ enum sscg_verbosity
-   SSCG_DEBUG
- };
- 
-+extern int verbosity;
-+
-+const char *sscg_get_verbosity_name (enum sscg_verbosity);
-+
-+#define SSCG_LOG(_level, _format, ...)                                        \
-+  do                                                                          \
-+    {                                                                         \
-+      if (verbosity >= _level)                                                \
-+        {                                                                     \
-+          printf ("%s", sscg_get_verbosity_name (_level));                    \
-+          printf (_format, ##__VA_ARGS__);                                    \
-+        }                                                                     \
-+    }                                                                         \
-+  while (0)
-+
-+#define SSCG_ERROR(_format, ...)                                              \
-+  do                                                                          \
-+    {                                                                         \
-+      if (verbosity > SSCG_QUIET)                                             \
-+        {                                                                     \
-+          fprintf (stderr, "ERROR: ");                                        \
-+          fprintf (stderr, _format, ##__VA_ARGS__);                           \
-+        }                                                                     \
-+    }                                                                         \
-+  while (0)
-+
-+
-+enum sscg_file_type
-+{
-+  SSCG_FILE_TYPE_UNKNOWN = -1,
-+  SSCG_FILE_TYPE_CA,
-+  SSCG_FILE_TYPE_CA_KEY,
-+  SSCG_FILE_TYPE_SVC,
-+  SSCG_FILE_TYPE_SVC_KEY,
-+  SSCG_FILE_TYPE_CLIENT,
-+  SSCG_FILE_TYPE_CLIENT_KEY,
-+  SSCG_FILE_TYPE_CRL,
-+  SSCG_FILE_TYPE_DHPARAMS,
-+
-+  SSCG_NUM_FILE_TYPES
-+};
-+
-+#define SSCG_FILE_TYPE_KEYS                                                   \
-+  ((1 << SSCG_FILE_TYPE_CA_KEY) | (1 << SSCG_FILE_TYPE_SVC_KEY) |             \
-+   (1 << SSCG_FILE_TYPE_CLIENT_KEY))
-+
-+#define SSCG_FILE_TYPE_SVC_TYPES                                              \
-+  ((1 << SSCG_FILE_TYPE_SVC) | (1 << SSCG_FILE_TYPE_SVC_KEY))
-+
-+#define SSCG_FILE_TYPE_CLIENT_TYPES                                           \
-+  ((1 << SSCG_FILE_TYPE_CLIENT) | (1 << SSCG_FILE_TYPE_CLIENT_KEY))
-+
-+#define SSCG_FILE_TYPE_CA_TYPES                                               \
-+  ((1 << SSCG_FILE_TYPE_CA) | (1 << SSCG_FILE_TYPE_CA_KEY))
-+
-+const char *
-+sscg_get_file_type_name (enum sscg_file_type _type);
-+
-+#define GET_BIO(_type) sscg_io_utils_get_bio_by_type (options->streams, _type)
-+
-+#define GET_PATH(_type)                                                       \
-+  sscg_io_utils_get_path_by_type (options->streams, _type)
-+
-+#define ANNOUNCE_WRITE(_type)                                                 \
-+  SSCG_LOG (SSCG_DEFAULT,                                                     \
-+            "Wrote %s to %s\n",                                               \
-+            sscg_get_file_type_name (_type),                                  \
-+            GET_PATH (_type));
-+
- struct sscg_options
- {
-   /* How noisy to be when printing information */
-@@ -149,15 +227,28 @@ struct sscg_options
-   char *ca_key_pass;
-   bool cert_key_pass_prompt;
-   char *cert_key_pass;
-+  bool client_key_pass_prompt;
-+  char *client_key_pass;
- 
-   /* Output Files */
--  char *ca_file;
--  char *ca_key_file;
--  char *cert_file;
--  char *cert_key_file;
-+  struct sscg_stream **streams;
-+
-+  /* Diffie-Hellman Parameters */
-+  int dhparams_prime_len;
-+  int dhparams_generator;
- 
-   /* Overwrite the output files */
-   bool overwrite;
- };
- 
-+
-+enum sscg_cert_type
-+{
-+  SSCG_CERT_TYPE_UNKNOWN = -1,
-+  SSCG_CERT_TYPE_SERVER,
-+  SSCG_CERT_TYPE_CLIENT,
-+
-+  SSCG_NUM_CERT_TYPES
-+};
-+
- #endif /* _SSCG_H */
-diff --git a/meson.build b/meson.build
-index c7b08ed3d6dff686f08a90ca869ba5881a9e8aaa..eb339ea8c768adab6d576736fbe476b83529e78d 100644
---- a/meson.build
-+++ b/meson.build
-@@ -52,21 +52,30 @@ endif
- 
- has_get_sec_level = cc.has_function(
-     'SSL_CTX_get_security_level',
--    dependencies: [ ssl])
-+    dependencies: [ ssl ])
-+
-+has_generator_3 = cc.has_header_symbol(
-+    'openssl/dh.h',
-+    'DH_GENERATOR_3',
-+    dependencies: [ ssl ])
- 
- sscg_lib_srcs = [
-     'src/authority.c',
-     'src/bignum.c',
-+    'src/cert.c',
-+    'src/dhparams.c',
-+    'src/io_utils.c',
-     'src/key.c',
--    'src/service.c',
-     'src/x509.c',
- ]
- 
- sscg_lib_hdrs = [
-     'include/authority.h',
-     'include/bignum.h',
-+    'include/cert.h',
-+    'include/dhparams.h',
-+    'include/io_utils.h',
-     'include/key.h',
--    'include/service.h',
-     'include/x509.h',
- ]
- 
-@@ -140,6 +149,42 @@ init_bignum_test = executable(
- )
- test('init_bignum_test', init_bignum_test)
- 
-+dhparams_test = executable(
-+    'dhparams_test',
-+    'test/dhparams_test.c',
-+    link_with : sscg_lib,
-+    dependencies: [],
-+    install : false
-+)
-+
-+# Test generating 512-bit, 1024-bit and 2048-bit DH params with multiple
-+# generators. 4096-bit and larger takes over ten minutes, so it's excluded from
-+# the test suite by default.
-+prime_lengths = [ 512, 1024 ]
-+dhparam_timeout = 120
-+
-+if get_option('run_slow_tests')
-+    prime_lengths = prime_lengths + [ 2048, 4096 ]
-+    dhparam_timeout = 900
-+endif
-+
-+generators = [ 2, 5 ]
-+
-+if (has_generator_3)
-+    generators += [ 3 ]
-+endif
-+
-+foreach prime_len : prime_lengths
-+    foreach g : generators
-+        test('dhparams_test_' + prime_len.to_string() + '_' + g.to_string(),
-+             dhparams_test,
-+             args: [ prime_len.to_string(), g.to_string() ],
-+             timeout: dhparam_timeout)
-+    endforeach
-+endforeach
-+
-+
-+
- cdata = configuration_data()
- cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
- cdata.set('HAVE_SSL_CTX_GET_SECURITY_LEVEL', has_get_sec_level)
-diff --git a/meson_options.txt b/meson_options.txt
-new file mode 100644
-index 0000000000000000000000000000000000000000..1d8ff959b3c3d3f2aa6dc928ba38efeedfbe5c96
---- /dev/null
-+++ b/meson_options.txt
-@@ -0,0 +1,24 @@
-+# This file is part of sscg.
-+#
-+# sscg is free software: you can redistribute it and/or modify
-+# it under the terms of the GNU General Public License as published by
-+# the Free Software Foundation, either version 3 of the License, or
-+# (at your option) any later version.
-+#
-+# sscg is distributed in the hope that it will be useful,
-+# but WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+# GNU General Public License for more details.
-+
-+# You should have received a copy of the GNU General Public License
-+# along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+#
-+# Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+
-+# Generating 4096-bit Diffie-Hellman parameters can take over ten minutes on a
-+# fast system. We skip testing it by default.
-+
-+# Some tests take a long time (dozens of seconds or even minutes)
-+# For general development, we will skip them and run them only in the CI
-+# environment.
-+option('run_slow_tests', type : 'boolean', value : false)
-diff --git a/src/cert.c b/src/cert.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..0377d1357a3881a9705fcb09fdfe2a9c78cc5ed4
---- /dev/null
-+++ b/src/cert.c
-@@ -0,0 +1,191 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+
-+#include "include/sscg.h"
-+#include "include/cert.h"
-+#include "include/x509.h"
-+#include "include/key.h"
-+
-+int
-+create_cert (TALLOC_CTX *mem_ctx,
-+             const struct sscg_options *options,
-+             struct sscg_x509_cert *ca_cert,
-+             struct sscg_evp_pkey *ca_key,
-+             enum sscg_cert_type type,
-+             struct sscg_x509_cert **_cert,
-+             struct sscg_evp_pkey **_key)
-+{
-+  int ret;
-+  size_t i;
-+  struct sscg_bignum *e;
-+  struct sscg_bignum *serial;
-+  struct sscg_cert_info *certinfo;
-+  struct sscg_x509_req *csr;
-+  struct sscg_evp_pkey *pkey;
-+  struct sscg_x509_cert *cert;
-+  X509_EXTENSION *ex = NULL;
-+  EXTENDED_KEY_USAGE *extended;
-+  TALLOC_CTX *tmp_ctx = NULL;
-+
-+  tmp_ctx = talloc_new (NULL);
-+  CHECK_MEM (tmp_ctx);
-+
-+  /* create a serial number for this certificate */
-+  ret = sscg_generate_serial (tmp_ctx, &serial);
-+  CHECK_OK (ret);
-+
-+  certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn);
-+  CHECK_MEM (certinfo);
-+
-+  /* Populate cert_info from options */
-+  certinfo->country = talloc_strdup (certinfo, options->country);
-+  CHECK_MEM (certinfo->country);
-+
-+  certinfo->state = talloc_strdup (certinfo, options->state);
-+  CHECK_MEM (certinfo->state);
-+
-+  certinfo->locality = talloc_strdup (certinfo, options->locality);
-+  CHECK_MEM (certinfo->locality);
-+
-+  certinfo->org = talloc_strdup (certinfo, options->org);
-+  CHECK_MEM (certinfo->org);
-+
-+  certinfo->org_unit = talloc_strdup (certinfo, options->org_unit);
-+  CHECK_MEM (certinfo->org_unit);
-+
-+  certinfo->email = talloc_strdup (certinfo, options->email);
-+  CHECK_MEM (certinfo->email);
-+
-+  certinfo->cn = talloc_strdup (certinfo, options->hostname);
-+  CHECK_MEM (certinfo->cn);
-+
-+  if (options->subject_alt_names)
-+    {
-+      for (i = 0; options->subject_alt_names[i]; i++)
-+        {
-+          certinfo->subject_alt_names = talloc_realloc (
-+            certinfo, certinfo->subject_alt_names, char *, i + 2);
-+          CHECK_MEM (certinfo->subject_alt_names);
-+
-+          certinfo->subject_alt_names[i] = talloc_strdup (
-+            certinfo->subject_alt_names, options->subject_alt_names[i]);
-+          CHECK_MEM (certinfo->subject_alt_names[i]);
-+
-+          /* Add a NULL terminator to the end */
-+          certinfo->subject_alt_names[i + 1] = NULL;
-+        }
-+    }
-+
-+  /* Ensure that this certificate may not sign other certificates */
-+  /* Add key extensions */
-+  ex = X509V3_EXT_conf_nid (
-+    NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment");
-+  CHECK_MEM (ex);
-+  sk_X509_EXTENSION_push (certinfo->extensions, ex);
-+
-+  extended = sk_ASN1_OBJECT_new_null ();
-+
-+  switch (type)
-+    {
-+    case SSCG_CERT_TYPE_SERVER:
-+      sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_server_auth));
-+      break;
-+
-+    case SSCG_CERT_TYPE_CLIENT:
-+      sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_client_auth));
-+      break;
-+
-+    default:
-+      fprintf (stdout, "Unknown certificate type!");
-+      ret = EINVAL;
-+      goto done;
-+    }
-+
-+
-+  ex = X509V3_EXT_i2d (NID_ext_key_usage, 0, extended);
-+  sk_ASN1_OBJECT_pop_free (extended, ASN1_OBJECT_free);
-+  sk_X509_EXTENSION_push (certinfo->extensions, ex);
-+
-+  /* Mark it as not a CA */
-+  ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE");
-+  CHECK_MEM (ex);
-+  sk_X509_EXTENSION_push (certinfo->extensions, ex);
-+
-+  /* Use an exponent value of RSA F4 aka 0x10001 (65537) */
-+  ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e);
-+  CHECK_OK (ret);
-+
-+  /* Generate an RSA keypair for this CA */
-+  if (options->verbosity >= SSCG_VERBOSE)
-+    {
-+      fprintf (stdout, "Generating RSA key for certificate.\n");
-+    }
-+  /* TODO: support DSA keys as well */
-+  ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey);
-+  CHECK_OK (ret);
-+
-+  /* Create a certificate signing request for the private CA */
-+  if (options->verbosity >= SSCG_VERBOSE)
-+    {
-+      fprintf (stdout, "Generating CSR for certificate.\n");
-+    }
-+  ret = sscg_x509v3_csr_new (tmp_ctx, certinfo, pkey, &csr);
-+  CHECK_OK (ret);
-+
-+  /* Finalize the CSR */
-+  ret = sscg_x509v3_csr_finalize (certinfo, pkey, csr);
-+  CHECK_OK (ret);
-+
-+  if (options->verbosity >= SSCG_DEBUG)
-+    {
-+      const char *tempcert =
-+        SSCG_CERT_TYPE_SERVER ? "./debug-service.csr" : "debug-client.csr";
-+
-+      fprintf (stderr, "DEBUG: Writing certificate CSR to %s\n", tempcert);
-+      BIO *csr_out = BIO_new_file (tempcert, "w");
-+      int sslret = PEM_write_bio_X509_REQ (csr_out, csr->x509_req);
-+      CHECK_SSL (sslret, PEM_write_bio_X509_REQ);
-+    }
-+
-+  /* Sign the certificate */
-+
-+  if (options->verbosity >= SSCG_VERBOSE)
-+    {
-+      fprintf (stdout, "Signing CSR for certificate. \n");
-+    }
-+
-+  ret = sscg_sign_x509_csr (tmp_ctx,
-+                            csr,
-+                            serial,
-+                            options->lifetime,
-+                            ca_cert,
-+                            ca_key,
-+                            options->hash_fn,
-+                            &cert);
-+  CHECK_OK (ret);
-+
-+  *_cert = talloc_steal (mem_ctx, cert);
-+  *_key = talloc_steal (mem_ctx, pkey);
-+
-+  ret = EOK;
-+done:
-+  talloc_free (tmp_ctx);
-+  return ret;
-+}
-diff --git a/src/dhparams.c b/src/dhparams.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..f9b64629709beb857a948a7f2f42eb805d76c557
---- /dev/null
-+++ b/src/dhparams.c
-@@ -0,0 +1,146 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+#include <assert.h>
-+
-+#include "include/sscg.h"
-+#include "include/dhparams.h"
-+
-+
-+static int
-+_sscg_dhparams_destructor (TALLOC_CTX *ctx);
-+
-+static int
-+dh_cb (int p, int n, BN_GENCB *cb);
-+
-+int
-+create_dhparams (TALLOC_CTX *mem_ctx,
-+                 enum sscg_verbosity verbosity,
-+                 int prime_len,
-+                 int generator,
-+                 struct sscg_dhparams **_dhparams)
-+{
-+  int ret;
-+  struct sscg_dhparams *dhparams = NULL;
-+  TALLOC_CTX *tmp_ctx = NULL;
-+
-+  /* First validate the input */
-+  assert (_dhparams && !*_dhparams);
-+
-+  if (prime_len <= 0)
-+    {
-+      fprintf (stderr, "Prime length must be a positive integer");
-+      ret = ERANGE;
-+      goto done;
-+    }
-+
-+  if (generator <= 0)
-+    {
-+      fprintf (stderr, "Generator must be a positive integer");
-+      ret = ERANGE;
-+      goto done;
-+    }
-+
-+  tmp_ctx = talloc_new (NULL);
-+  CHECK_MEM (tmp_ctx);
-+
-+  dhparams = talloc_zero (tmp_ctx, struct sscg_dhparams);
-+  CHECK_MEM (dhparams);
-+
-+  dhparams->prime_len = prime_len;
-+  dhparams->generator = generator;
-+  talloc_set_destructor ((TALLOC_CTX *)dhparams, _sscg_dhparams_destructor);
-+
-+  if (verbosity >= SSCG_DEFAULT)
-+    {
-+      fprintf (stdout,
-+               "Generating DH parameters of length %d and generator %d. "
-+               "This will take a long time.\n",
-+               dhparams->prime_len,
-+               dhparams->generator);
-+    }
-+
-+  dhparams->dh = DH_new ();
-+
-+  if (verbosity >= SSCG_VERBOSE)
-+    {
-+#if OPENSSL_VERSION_NUMBER < 0x10100000L
-+      dhparams->cb = talloc_zero (dhparams, BN_GENCB);
-+#else
-+      dhparams->cb = BN_GENCB_new ();
-+#endif
-+      if (dhparams->cb == NULL)
-+        {
-+          ERR_print_errors_fp (stderr);
-+          ret = ENOMEM;
-+          goto done;
-+        }
-+
-+      BN_GENCB_set (dhparams->cb, dh_cb, NULL);
-+    }
-+
-+  if (!DH_generate_parameters_ex (
-+        dhparams->dh, dhparams->prime_len, dhparams->generator, dhparams->cb))
-+    {
-+      ERR_print_errors_fp (stderr);
-+      ret = EIO;
-+      goto done;
-+    }
-+
-+  ret = EOK;
-+  *_dhparams = talloc_steal (mem_ctx, dhparams);
-+
-+done:
-+  talloc_free (tmp_ctx);
-+  return ret;
-+}
-+
-+static int
-+_sscg_dhparams_destructor (TALLOC_CTX *ctx)
-+{
-+  struct sscg_dhparams *params =
-+    talloc_get_type_abort (ctx, struct sscg_dhparams);
-+
-+  if (params->dh != NULL)
-+    {
-+      DH_free (params->dh);
-+      params->dh = NULL;
-+    }
-+
-+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-+  if (params->cb != NULL)
-+    {
-+      BN_GENCB_free (params->cb);
-+      params->cb = NULL;
-+    }
-+#endif
-+
-+  return 0;
-+}
-+
-+static int
-+dh_cb (int p, int n, BN_GENCB *cb)
-+{
-+  static const char symbols[] = ".+*\n";
-+  char c = (p >= 0 && (size_t)p < sizeof (symbols) - 1) ? symbols[p] : '?';
-+
-+  fprintf (stdout, "%c", c);
-+
-+  return 1;
-+}
-diff --git a/src/io_utils.c b/src/io_utils.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..809a1da0e455afa0dba0796a5f7ac406742328a1
---- /dev/null
-+++ b/src/io_utils.c
-@@ -0,0 +1,424 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+
-+#include <assert.h>
-+#include <path_utils.h>
-+#include <string.h>
-+#include <talloc.h>
-+
-+#include "include/io_utils.h"
-+#include "include/sscg.h"
-+
-+int
-+sscg_normalize_path (TALLOC_CTX *mem_ctx,
-+                     const char *path,
-+                     char **_normalized_path)
-+{
-+  int ret;
-+  char *normalized_path = NULL;
-+
-+  TALLOC_CTX *tmp_ctx = talloc_new (NULL);
-+  CHECK_MEM (tmp_ctx);
-+
-+  normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX);
-+  CHECK_MEM (normalized_path);
-+
-+  ret = make_normalized_absolute_path (normalized_path, PATH_MAX, path);
-+  CHECK_OK (ret);
-+
-+  *_normalized_path = talloc_strdup (mem_ctx, normalized_path);
-+  CHECK_MEM (*_normalized_path);
-+
-+  ret = EOK;
-+
-+done:
-+  talloc_free (normalized_path);
-+  talloc_free (tmp_ctx);
-+  return ret;
-+}
-+
-+
-+static int
-+sscg_stream_destructor (TALLOC_CTX *ptr)
-+{
-+  struct sscg_stream *stream = talloc_get_type_abort (ptr, struct sscg_stream);
-+
-+  BIO_free (stream->bio);
-+
-+  return 0;
-+}
-+
-+
-+struct sscg_stream *
-+sscg_io_utils_get_stream_by_path (struct sscg_stream **streams,
-+                                  const char *normalized_path)
-+{
-+  struct sscg_stream *stream = NULL;
-+
-+  /* First see if this path already exists in the list */
-+  for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
-+    {
-+      if (strcmp (normalized_path, stream->path) == 0)
-+        break;
-+    }
-+
-+  return stream;
-+}
-+
-+
-+struct sscg_stream *
-+sscg_io_utils_get_stream_by_type (struct sscg_stream **streams,
-+                                  enum sscg_file_type filetype)
-+{
-+  struct sscg_stream *stream = NULL;
-+
-+  /* First see if this path already exists in the list */
-+  for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
-+    {
-+      SSCG_LOG (SSCG_DEBUG,
-+                "Checking for 0x%.4x in 0x%.4x\n",
-+                (1 << filetype),
-+                stream->filetypes);
-+      if (stream->filetypes & (1 << filetype))
-+        {
-+          SSCG_LOG (SSCG_DEBUG,
-+                    "Found file type %s in %s\n",
-+                    sscg_get_file_type_name (filetype),
-+                    stream->path);
-+          break;
-+        }
-+    }
-+
-+  if (!stream)
-+    SSCG_LOG (SSCG_DEBUG,
-+              "Could not locate file type: %s. Skipping.\n",
-+              sscg_get_file_type_name (filetype));
-+
-+  return stream;
-+}
-+
-+
-+BIO *
-+sscg_io_utils_get_bio_by_type (struct sscg_stream **streams,
-+                               enum sscg_file_type filetype)
-+{
-+  struct sscg_stream *_tmp_stream =
-+    sscg_io_utils_get_stream_by_type (streams, filetype);
-+
-+  if (_tmp_stream)
-+    {
-+      return _tmp_stream->bio;
-+    }
-+
-+  return NULL;
-+}
-+
-+
-+const char *
-+sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
-+                                enum sscg_file_type filetype)
-+{
-+  struct sscg_stream *_tmp_stream =
-+    sscg_io_utils_get_stream_by_type (streams, filetype);
-+
-+  if (_tmp_stream)
-+    {
-+      return _tmp_stream->path;
-+    }
-+
-+  return NULL;
-+}
-+
-+
-+int
-+sscg_io_utils_add_output_file (struct sscg_stream **streams,
-+                               enum sscg_file_type filetype,
-+                               const char *path,
-+                               int mode)
-+{
-+  int ret, i;
-+  TALLOC_CTX *tmp_ctx = NULL;
-+  struct sscg_stream *stream = NULL;
-+  char *normalized_path = NULL;
-+
-+  /* If we haven't been passed a path, just return; it's probably an optional
-+   * output file
-+   */
-+  if (path == NULL)
-+    {
-+      SSCG_LOG (SSCG_DEBUG,
-+                "Got a NULL path with filetype: %s\n",
-+                sscg_get_file_type_name (filetype));
-+      return EOK;
-+    }
-+
-+  tmp_ctx = talloc_new (NULL);
-+  CHECK_MEM (tmp_ctx);
-+
-+  /* Get the normalized version of the path */
-+  ret = sscg_normalize_path (tmp_ctx, path, &normalized_path);
-+  CHECK_OK (ret);
-+
-+  SSCG_LOG (SSCG_DEBUG,
-+            "%s file path: %s\n",
-+            sscg_get_file_type_name (filetype),
-+            normalized_path);
-+
-+  /* First see if this path already exists in the list */
-+  stream = sscg_io_utils_get_stream_by_path (streams, normalized_path);
-+
-+  if (stream == NULL)
-+    {
-+      /* The path wasn't found, so open it and create it */
-+
-+      /* First advance the index to the end */
-+      for (i = 0; streams[i]; i++)
-+        ;
-+
-+      /* This should never happen. The streams array should always be
-+       * sized to the maximum number of known types. If we are asked to add
-+       * more entries to the array than we have known file types, it must be
-+       * due to a bug.
-+       */
-+      assert (i < SSCG_NUM_FILE_TYPES);
-+
-+      stream = talloc_zero (tmp_ctx, struct sscg_stream);
-+      CHECK_MEM (stream);
-+      talloc_set_destructor ((TALLOC_CTX *)stream, sscg_stream_destructor);
-+
-+      stream->path = talloc_steal (stream, normalized_path);
-+      CHECK_MEM (stream->path);
-+
-+      streams[i] = talloc_steal (streams, stream);
-+    }
-+
-+  /* Always set the mode to the most-restrictive one requested */
-+  SSCG_LOG (SSCG_DEBUG, "Requested mode: %o\n", mode);
-+  if (stream->mode)
-+    stream->mode &= mode;
-+  else
-+    stream->mode = mode;
-+  SSCG_LOG (SSCG_DEBUG, "Actual mode: %o\n", stream->mode);
-+
-+  /* Add the file type */
-+  stream->filetypes |= (1 << filetype);
-+
-+  ret = EOK;
-+
-+done:
-+  talloc_free (tmp_ctx);
-+  return ret;
-+}
-+
-+
-+enum io_utils_errors
-+{
-+  IO_UTILS_OK = 0,
-+  IO_UTILS_TOOMANYKEYS,
-+  IO_UTILS_DHPARAMS_NON_EXCLUSIVE,
-+  IO_UTILS_CRL_NON_EXCLUSIVE,
-+  IO_UTILS_SVC_UNMATCHED,
-+  IO_UTILS_CLIENT_UNMATCHED,
-+  IO_UTILS_CA_UNMATCHED
-+};
-+
-+static enum io_utils_errors
-+io_utils_validate (struct sscg_stream **streams)
-+{
-+  enum io_utils_errors ret;
-+  struct sscg_stream *stream = NULL;
-+  int keybits;
-+  int allbits = 0;
-+
-+  for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
-+    {
-+      SSCG_LOG (SSCG_DEBUG, "filetypes: 0x%.4x\n", stream->filetypes);
-+
-+      allbits |= stream->filetypes;
-+
-+      /* No file may contain two different private keys */
-+      /* First check if any private keys are in this file */
-+      if ((keybits = stream->filetypes & SSCG_FILE_TYPE_KEYS))
-+        {
-+          /* Next check if there is exactly one private key in the remainder.
-+           * The following bitwise magic checks whether the value is exactly a
-+           * power of two (meaning only one bit is set). If the result is
-+           * nonzero, more than one bit was set and we have been asked to
-+           * include multiple keys into the same file.
-+           */
-+          if (keybits & (keybits - 1))
-+            {
-+              ret = IO_UTILS_TOOMANYKEYS;
-+              goto done;
-+            }
-+        }
-+
-+      /* The dhparams file may only contain dhparams */
-+      if ((stream->filetypes & (1 << SSCG_FILE_TYPE_DHPARAMS)) &&
-+          (stream->filetypes ^ (1 << SSCG_FILE_TYPE_DHPARAMS)))
-+        {
-+          ret = IO_UTILS_DHPARAMS_NON_EXCLUSIVE;
-+          goto done;
-+        }
-+
-+      /* The CRL file may only contain certificate revocations */
-+      if ((stream->filetypes & (1 << SSCG_FILE_TYPE_CRL)) &&
-+          (stream->filetypes ^ (1 << SSCG_FILE_TYPE_CRL)))
-+        {
-+          ret = IO_UTILS_CRL_NON_EXCLUSIVE;
-+          goto done;
-+        }
-+    }
-+
-+  SSCG_LOG (SSCG_DEBUG, "allbits: 0x%.4x\n", allbits);
-+
-+  /* If the public or private key is present for the service cert, the other
-+   * must be present also
-+   */
-+  if ((allbits & SSCG_FILE_TYPE_SVC_TYPES) &&
-+      ((allbits & SSCG_FILE_TYPE_SVC_TYPES) != SSCG_FILE_TYPE_SVC_TYPES))
-+    {
-+      ret = IO_UTILS_SVC_UNMATCHED;
-+      goto done;
-+    }
-+
-+  /* If the public or private key is present for the client cert, the other
-+   * must be present also
-+   */
-+  if ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) &&
-+      ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) != SSCG_FILE_TYPE_CLIENT_TYPES))
-+    {
-+      ret = IO_UTILS_CLIENT_UNMATCHED;
-+      goto done;
-+    }
-+
-+  /* If the private key is present for the CA cert, the public key must be
-+   * present also
-+   */
-+  if ((allbits & (1 << SSCG_FILE_TYPE_CA_KEY)) &&
-+      !(allbits & (1 << SSCG_FILE_TYPE_CA)))
-+    {
-+      ret = IO_UTILS_CA_UNMATCHED;
-+      goto done;
-+    }
-+
-+
-+  ret = IO_UTILS_OK;
-+
-+done:
-+  return ret;
-+}
-+
-+
-+int
-+sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite)
-+{
-+  int ret;
-+  TALLOC_CTX *tmp_ctx = NULL;
-+  enum io_utils_errors validation_result;
-+  char *create_mode = NULL;
-+  struct sscg_stream *stream = NULL;
-+
-+  validation_result = io_utils_validate (streams);
-+  switch (validation_result)
-+    {
-+    case IO_UTILS_TOOMANYKEYS:
-+      SSCG_ERROR ("Attempted to output multiple keys to the same file.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_CRL_NON_EXCLUSIVE:
-+      SSCG_ERROR ("The CRL file may not include other content.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_DHPARAMS_NON_EXCLUSIVE:
-+      SSCG_ERROR ("The dhparams file may not include other content.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_SVC_UNMATCHED:
-+      SSCG_ERROR (
-+        "The service certificate must have both public and private key "
-+        "locations specified.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_CLIENT_UNMATCHED:
-+      SSCG_ERROR (
-+        "The client certificate must have both public and private key "
-+        "locations specified.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_CA_UNMATCHED:
-+      SSCG_ERROR (
-+        "The CA certificate must have a public key location specified.\n");
-+      ret = EINVAL;
-+      goto done;
-+
-+    case IO_UTILS_OK: break;
-+    }
-+
-+  tmp_ctx = talloc_new (NULL);
-+  CHECK_MEM (tmp_ctx);
-+
-+  if (overwrite)
-+    create_mode = talloc_strdup (tmp_ctx, "w");
-+  else
-+    create_mode = talloc_strdup (tmp_ctx, "wx");
-+  CHECK_MEM (create_mode);
-+
-+  for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
-+    {
-+      SSCG_LOG (SSCG_DEBUG, "Opening %s\n", stream->path);
-+      stream->bio = BIO_new_file (stream->path, create_mode);
-+      CHECK_BIO (stream->bio, stream->path);
-+    }
-+
-+  ret = EOK;
-+done:
-+  talloc_free (tmp_ctx);
-+  return ret;
-+}
-+
-+
-+int
-+sscg_io_utils_finalize_output_files (struct sscg_stream **streams)
-+{
-+  struct sscg_stream *stream = NULL;
-+  FILE *fp;
-+
-+  for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
-+    {
-+      /* Set the final permissions mode */
-+      SSCG_LOG (SSCG_DEBUG,
-+                "Setting %s file permissions to %o\n",
-+                stream->path,
-+                stream->mode);
-+      BIO_get_fp (stream->bio, &fp);
-+
-+      errno = 0;
-+      if (fchmod (fileno (fp), stream->mode) != 0)
-+        return errno;
-+    }
-+
-+  return EOK;
-+}
-diff --git a/src/service.c b/src/service.c
-deleted file mode 100644
-index 34c976dbe905528000b181c24d1fa95da3cd1377..0000000000000000000000000000000000000000
---- a/src/service.c
-+++ /dev/null
-@@ -1,164 +0,0 @@
--/*
--    This file is part of sscg.
--
--    sscg is free software: you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation, either version 3 of the License, or
--    (at your option) any later version.
--
--    sscg is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
--
--    Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
--*/
--
--
--#include "include/sscg.h"
--#include "include/service.h"
--#include "include/x509.h"
--#include "include/key.h"
--
--int
--create_service_cert (TALLOC_CTX *mem_ctx,
--                     const struct sscg_options *options,
--                     struct sscg_x509_cert *ca_cert,
--                     struct sscg_evp_pkey *ca_key,
--                     struct sscg_x509_cert **_svc_cert,
--                     struct sscg_evp_pkey **_svc_key)
--{
--  int ret;
--  size_t i;
--  struct sscg_bignum *e;
--  struct sscg_bignum *serial;
--  struct sscg_cert_info *svc_certinfo;
--  struct sscg_x509_req *csr;
--  struct sscg_evp_pkey *pkey;
--  struct sscg_x509_cert *cert;
--  X509_EXTENSION *ex = NULL;
--  TALLOC_CTX *tmp_ctx = NULL;
--
--  tmp_ctx = talloc_new (NULL);
--  CHECK_MEM (tmp_ctx);
--
--  /* create a serial number for this certificate */
--  ret = sscg_generate_serial (tmp_ctx, &serial);
--  CHECK_OK (ret);
--
--  svc_certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn);
--  CHECK_MEM (svc_certinfo);
--
--  /* Populate cert_info from options */
--  svc_certinfo->country = talloc_strdup (svc_certinfo, options->country);
--  CHECK_MEM (svc_certinfo->country);
--
--  svc_certinfo->state = talloc_strdup (svc_certinfo, options->state);
--  CHECK_MEM (svc_certinfo->state);
--
--  svc_certinfo->locality = talloc_strdup (svc_certinfo, options->locality);
--  CHECK_MEM (svc_certinfo->locality);
--
--  svc_certinfo->org = talloc_strdup (svc_certinfo, options->org);
--  CHECK_MEM (svc_certinfo->org);
--
--  svc_certinfo->org_unit = talloc_strdup (svc_certinfo, options->org_unit);
--  CHECK_MEM (svc_certinfo->org_unit);
--
--  svc_certinfo->email = talloc_strdup (svc_certinfo, options->email);
--  CHECK_MEM (svc_certinfo->email);
--
--  svc_certinfo->cn = talloc_strdup (svc_certinfo, options->hostname);
--  CHECK_MEM (svc_certinfo->cn);
--
--  if (options->subject_alt_names)
--    {
--      for (i = 0; options->subject_alt_names[i]; i++)
--        {
--          svc_certinfo->subject_alt_names = talloc_realloc (
--            svc_certinfo, svc_certinfo->subject_alt_names, char *, i + 2);
--          CHECK_MEM (svc_certinfo->subject_alt_names);
--
--          svc_certinfo->subject_alt_names[i] = talloc_strdup (
--            svc_certinfo->subject_alt_names, options->subject_alt_names[i]);
--          CHECK_MEM (svc_certinfo->subject_alt_names[i]);
--
--          /* Add a NULL terminator to the end */
--          svc_certinfo->subject_alt_names[i + 1] = NULL;
--        }
--    }
--
--  /* Ensure that this certificate may not sign other certificates */
--  /* Add key extensions */
--  ex = X509V3_EXT_conf_nid (
--    NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment");
--  CHECK_MEM (ex);
--  sk_X509_EXTENSION_push (svc_certinfo->extensions, ex);
--
--  /* Mark it as not a CA */
--  ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE");
--  CHECK_MEM (ex);
--  sk_X509_EXTENSION_push (svc_certinfo->extensions, ex);
--
--  /* Use an exponent value of RSA F4 aka 0x10001 (65537) */
--  ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e);
--  CHECK_OK (ret);
--
--  /* Generate an RSA keypair for this CA */
--  if (options->verbosity >= SSCG_VERBOSE)
--    {
--      fprintf (stdout, "Generating RSA key for service certificate.\n");
--    }
--  /* TODO: support DSA keys as well */
--  ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey);
--  CHECK_OK (ret);
--
--  /* Create a certificate signing request for the private CA */
--  if (options->verbosity >= SSCG_VERBOSE)
--    {
--      fprintf (stdout, "Generating CSR for service certificate.\n");
--    }
--  ret = sscg_x509v3_csr_new (tmp_ctx, svc_certinfo, pkey, &csr);
--  CHECK_OK (ret);
--
--  /* Finalize the CSR */
--  ret = sscg_x509v3_csr_finalize (svc_certinfo, pkey, csr);
--  CHECK_OK (ret);
--
--  if (options->verbosity >= SSCG_DEBUG)
--    {
--      fprintf (stderr,
--               "DEBUG: Writing service certificate CSR to ./debug-svc.csr\n");
--      BIO *svc_csr_out = BIO_new_file ("./debug-svc.csr", "w");
--      int sslret = PEM_write_bio_X509_REQ (svc_csr_out, csr->x509_req);
--      CHECK_SSL (sslret, PEM_write_bio_X509_REQ);
--    }
--
--  /* Sign the certificate */
--
--  if (options->verbosity >= SSCG_VERBOSE)
--    {
--      fprintf (stdout, "Signing CSR for service certificate. \n");
--    }
--
--  ret = sscg_sign_x509_csr (tmp_ctx,
--                            csr,
--                            serial,
--                            options->lifetime,
--                            ca_cert,
--                            ca_key,
--                            options->hash_fn,
--                            &cert);
--  CHECK_OK (ret);
--
--  *_svc_cert = talloc_steal (mem_ctx, cert);
--  *_svc_key = talloc_steal (mem_ctx, pkey);
--
--  ret = EOK;
--done:
--  talloc_free (tmp_ctx);
--  return ret;
--}
-diff --git a/src/sscg.c b/src/sscg.c
-index a02e4df66c6cf9ec1865f425b4a15da82fbfdc72..470af815d91f5170a1e8fe00006dbaee4d07b209 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -32,7 +32,12 @@
- #include "config.h"
- #include "include/sscg.h"
- #include "include/authority.h"
--#include "include/service.h"
-+#include "include/cert.h"
-+#include "include/dhparams.h"
-+#include "include/io_utils.h"
-+
-+
-+int verbosity;
- 
- 
- /* Same as OpenSSL CLI */
-@@ -58,6 +63,8 @@ set_default_options (struct sscg_options *opts)
-   int security_level = get_security_level ();
- 
-   opts->lifetime = 3650;
-+  opts->dhparams_prime_len = 2048;
-+  opts->dhparams_generator = 2;
- 
-   /* Select the default key strength based on the system security level
-    * See:
-@@ -132,51 +139,6 @@ print_options (struct sscg_options *opts)
-   fprintf (stdout, "=================\n");
- }
- 
--static int
--_sscg_normalize_path (TALLOC_CTX *mem_ctx,
--                      const char *path,
--                      const char *path_default,
--                      char **_normalized_path)
--{
--  int ret;
--  char *orig_path = NULL;
--  char *normalized_path = NULL;
--
--  TALLOC_CTX *tmp_ctx = talloc_new (NULL);
--  CHECK_MEM (tmp_ctx);
--
--  if (path)
--    {
--      orig_path = talloc_strdup (tmp_ctx, path);
--    }
--  else
--    {
--      if (!path_default)
--        {
--          /* If no default is set and no path was provided,
--             * return NULL */
--          *_normalized_path = NULL;
--          ret = EOK;
--          goto done;
--        }
--      orig_path = talloc_strdup (tmp_ctx, path_default);
--      CHECK_MEM (orig_path);
--    }
--
--  normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX);
--  CHECK_MEM (normalized_path);
--
--  ret = make_normalized_absolute_path (normalized_path, PATH_MAX, orig_path);
--  CHECK_OK (ret);
--
--  *_normalized_path = talloc_steal (mem_ctx, normalized_path);
--  ret = EOK;
--
--done:
--  talloc_free (tmp_ctx);
--  return ret;
--}
--
- 
- /* This function takes a copy of a string into a talloc hierarchy and memsets
-  * the original string to zeroes to avoid leaking it when that memory is freed.
-@@ -251,6 +213,53 @@ sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path)
- }
- 
- 
-+const char *
-+sscg_get_verbosity_name (enum sscg_verbosity type)
-+{
-+  switch (type)
-+    {
-+    case SSCG_DEFAULT:
-+    case SSCG_VERBOSE: return "";
-+
-+    case SSCG_DEBUG: return "DEBUG: ";
-+
-+    default: break;
-+    }
-+
-+  /* If it wasn't one of these, we have a bug */
-+  return "Unknown Verbosity (bug):";
-+}
-+
-+
-+const char *
-+sscg_get_file_type_name (enum sscg_file_type type)
-+{
-+  switch (type)
-+    {
-+    case SSCG_FILE_TYPE_CA: return "CA certificate";
-+
-+    case SSCG_FILE_TYPE_CA_KEY: return "CA certificate key";
-+
-+    case SSCG_FILE_TYPE_SVC: return "service certificate";
-+
-+    case SSCG_FILE_TYPE_SVC_KEY: return "service certificate key";
-+
-+    case SSCG_FILE_TYPE_CLIENT: return "client auth certificate";
-+
-+    case SSCG_FILE_TYPE_CLIENT_KEY: return "client auth certificate key";
-+
-+    case SSCG_FILE_TYPE_CRL: return "certificate revocation list";
-+
-+    case SSCG_FILE_TYPE_DHPARAMS: return "Diffie-Hellman parameters";
-+
-+    default: break;
-+    }
-+
-+  /* If it wasn't one of these, we have a bug */
-+  return "Unknown (bug)";
-+}
-+
-+
- int
- main (int argc, const char **argv)
- {
-@@ -274,36 +283,48 @@ main (int argc, const char **argv)
-   char *ca_key_file = NULL;
-   char *cert_file = NULL;
-   char *cert_key_file = NULL;
-+  char *client_file = NULL;
-+  char *client_key_file = NULL;
-+  char *dhparams_file = NULL;
- 
--  int ca_mode = 0644;
--  int ca_key_mode = 0600;
-+  int ca_mode = SSCG_CERT_DEFAULT_MODE;
-+  int ca_key_mode = SSCG_KEY_DEFAULT_MODE;
-   char *ca_key_password = NULL;
-   char *ca_key_passfile = NULL;
- 
--  int cert_mode = 0644;
--  int cert_key_mode = 0600;
-+  int crl_mode = SSCG_CERT_DEFAULT_MODE;
-+  char *crl_file = NULL;
-+
-+  int cert_mode = SSCG_CERT_DEFAULT_MODE;
-+  int cert_key_mode = SSCG_KEY_DEFAULT_MODE;
-   char *cert_key_password = NULL;
-   char *cert_key_passfile = NULL;
- 
--  char *create_mode = NULL;
-+  int client_mode = SSCG_CERT_DEFAULT_MODE;
-+  int client_key_mode = SSCG_KEY_DEFAULT_MODE;
-+  char *client_key_password = NULL;
-+  char *client_key_passfile = NULL;
- 
-   struct sscg_x509_cert *cacert;
-   struct sscg_evp_pkey *cakey;
-   struct sscg_x509_cert *svc_cert;
-   struct sscg_evp_pkey *svc_key;
-+  struct sscg_x509_cert *client_cert;
-+  struct sscg_evp_pkey *client_key;
- 
--  BIO *ca_out = NULL;
--  BIO *ca_key_out = NULL;
--  BIO *cert_out = NULL;
--  BIO *cert_key_out = NULL;
--
--  FILE *fp;
-+  int dhparams_mode = SSCG_CERT_DEFAULT_MODE;
-+  struct sscg_dhparams *dhparams = NULL;
- 
-   /* Always use umask 0577 for generating certificates and keys
-        This means that it's opened as write-only by the effective
-        user. */
-   umask (0577);
- 
-+#if OPENSSL_VERSION_NUMBER < 0x10100000L
-+  /* In OpenSSL <1.1.0, we need to initialize the library. */
-+  OpenSSL_add_all_algorithms ();
-+#endif
-+
-   TALLOC_CTX *main_ctx = talloc_new (NULL);
-   if (!main_ctx)
-     {
-@@ -315,6 +336,9 @@ main (int argc, const char **argv)
-   CHECK_MEM (options);
-   talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor);
- 
-+  options->streams =
-+    talloc_zero_array (options, struct sscg_stream *, SSCG_NUM_FILE_TYPES);
-+
-   ret = set_default_options (options);
-   if (ret != EOK)
-     goto done;
-@@ -323,101 +347,144 @@ main (int argc, const char **argv)
-     talloc_asprintf (main_ctx, "%d or larger", options->minimum_key_strength);
- 
-   options->verbosity = SSCG_DEFAULT;
-+  // clang-format off
-   struct poptOption long_options[] = {
--    POPT_AUTOHELP{ "quiet",
--                   'q',
--                   POPT_ARG_VAL,
--                   &options->verbosity,
--                   SSCG_QUIET,
--                   _ ("Display no output unless there is an error."),
--                   NULL },
--    { "verbose",
-+    POPT_AUTOHELP
-+
-+    {
-+      "quiet",
-+      'q',
-+      POPT_ARG_VAL,
-+      &options->verbosity,
-+      SSCG_QUIET,
-+       ("Display no output unless there is an error."),
-+      NULL
-+    },
-+
-+    {
-+      "verbose",
-       'v',
-       POPT_ARG_VAL,
-       &options->verbosity,
-       SSCG_VERBOSE,
-       _ ("Display progress messages."),
--      NULL },
--    { "debug",
-+      NULL
-+    },
-+
-+    {
-+      "debug",
-       'd',
-       POPT_ARG_VAL,
-       &options->verbosity,
-       SSCG_DEBUG,
-       _ ("Enable logging of debug messages. Implies verbose. Warning! "
-          "This will print private key information to the screen!"),
--      NULL },
--    { "version",
-+      NULL
-+    },
-+
-+    {
-+      "version",
-       'V',
-       POPT_ARG_NONE,
-       &options->print_version,
-       0,
-       _ ("Display the version number and exit."),
--      NULL },
--    { "force",
-+      NULL
-+    },
-+
-+    {
-+      "force",
-       'f',
-       POPT_ARG_NONE,
-       &options->overwrite,
-       0,
-       _ ("Overwrite any pre-existing files in the requested locations"),
--      NULL },
--    { "lifetime",
-+      NULL
-+    },
-+
-+    {
-+      "lifetime",
-       '\0',
-       POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
-       &options->lifetime,
-       0,
-       _ ("Certificate lifetime (days)."),
--      _ ("1-3650") },
--    { "country",
-+      _ ("1-3650")
-+    },
-+
-+    {
-+      "country",
-       '\0',
-       POPT_ARG_STRING,
-       &country,
-       0,
-       _ ("Certificate DN: Country (C). (default: \"US\")"),
--      _ ("US, CZ, etc.") },
--    { "state",
-+      _ ("US, CZ, etc.")
-+    },
-+
-+    {
-+      "state",
-       '\0',
-       POPT_ARG_STRING,
-       &state,
-       0,
-       _ ("Certificate DN: State or Province (ST)."),
--      _ ("Massachusetts, British Columbia, etc.") },
--    { "locality",
-+      _ ("Massachusetts, British Columbia, etc.")
-+    },
-+
-+    {
-+      "locality",
-       '\0',
-       POPT_ARG_STRING,
-       &locality,
-       0,
-       _ ("Certificate DN: Locality (L)."),
--      _ ("Westford, Paris, etc.") },
--    { "organization",
-+      _ ("Westford, Paris, etc.")
-+    },
-+
-+    {
-+      "organization",
-       '\0',
-       POPT_ARG_STRING,
-       &organization,
-       0,
-       _ ("Certificate DN: Organization (O). (default: \"Unspecified\")"),
--      _ ("My Company") },
--    { "organizational-unit",
-+      _ ("My Company")
-+    },
-+
-+    {
-+      "organizational-unit",
-       '\0',
-       POPT_ARG_STRING,
-       &organizational_unit,
-       0,
-       _ ("Certificate DN: Organizational Unit (OU)."),
--      _ ("Engineering, etc.") },
--    { "email",
-+      _ ("Engineering, etc.")
-+    },
-+
-+    {
-+      "email",
-       '\0',
-       POPT_ARG_STRING,
-       &email,
-       0,
-       _ ("Certificate DN: Email Address (Email)."),
--      _ ("myname@example.com") },
--    { "hostname",
-+      _ ("myname@example.com")
-+    },
-+
-+    {
-+      "hostname",
-       '\0',
-       POPT_ARG_STRING,
-       &hostname,
-       0,
-       _ ("The valid hostname of the certificate. Must be an FQDN. (default: "
-          "current system FQDN)"),
--      _ ("server.example.com") },
--    { "subject-alt-name",
-+      _ ("server.example.com")
-+    },
-+
-+    {
-+      "subject-alt-name",
-       '\0',
-       POPT_ARG_ARGV,
-       &alternative_names,
-@@ -427,7 +494,9 @@ main (int argc, const char **argv)
-          "supported by RFC 5280 such as "
-          "IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy "
-          "May be specified multiple times."),
--      _ ("alt.example.com") },
-+      _ ("alt.example.com")
-+    },
-+
-     {
-       "package",
-       '\0',
-@@ -437,7 +506,9 @@ main (int argc, const char **argv)
-       _ ("Unused. Retained for compatibility with earlier versions of sscg."),
-       NULL,
-     },
--    { "key-strength",
-+
-+    {
-+      "key-strength",
-       '\0',
-       POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
-       &options->key_strength,
-@@ -453,6 +524,7 @@ main (int argc, const char **argv)
-       _ ("Hashing algorithm to use for signing."),
-       _ ("{sha256,sha384,sha512}"),
-     },
-+
-     {
-       "cipher-alg",
-       '\0',
-@@ -473,15 +545,17 @@ main (int argc, const char **argv)
-          "\"./ca.crt\")"),
-       NULL,
-     },
-+
-     {
-       "ca-mode",
-       '\0',
-       POPT_ARG_INT,
-       &ca_mode,
-       0,
--      _ ("File mode of the created CA certificate. (default: 0644)"),
--      _ ("0644"),
-+      _ ("File mode of the created CA certificate."),
-+      SSCG_CERT_DEFAULT_MODE_HELP,
-     },
-+
-     {
-       "ca-key-file",
-       '\0',
-@@ -492,15 +566,17 @@ main (int argc, const char **argv)
-          "the key will be destroyed rather than written to the disk."),
-       NULL,
-     },
-+
-     {
-       "ca-key-mode",
-       '\0',
-       POPT_ARG_INT,
-       &ca_key_mode,
-       0,
--      _ ("File mode of the created CA key. (default: 0600)"),
--      _ ("0600"),
-+      _ ("File mode of the created CA key."),
-+      SSCG_KEY_DEFAULT_MODE_HELP,
-     },
-+
-     {
-       "ca-key-password",
-       '\0',
-@@ -534,6 +610,28 @@ main (int argc, const char **argv)
-       NULL
-     },
- 
-+    {
-+      "crl-file",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &crl_file,
-+      0,
-+      _ ("Path where an (empty) Certificate Revocation List file will be "
-+         "created, for applications that expect such a file to exist. If "
-+         "unspecified, no such file will be created."),
-+      NULL
-+    },
-+
-+    {
-+      "crl-mode",
-+      '\0',
-+      POPT_ARG_INT,
-+      &crl_mode,
-+      0,
-+      _ ("File mode of the created Certificate Revocation List."),
-+      SSCG_CERT_DEFAULT_MODE_HELP,
-+    },
-+
-     {
-       "cert-file",
-       '\0',
-@@ -544,15 +642,17 @@ main (int argc, const char **argv)
-          "(default \"./service.pem\")"),
-       NULL,
-     },
-+
-     {
-       "cert-mode",
-       '\0',
-       POPT_ARG_INT,
-       &cert_mode,
-       0,
--      _ ("File mode of the created certificate. (default: 0644)"),
--      _ ("0644"),
-+      _ ("File mode of the created certificate."),
-+      SSCG_CERT_DEFAULT_MODE_HELP,
-     },
-+
-     {
-       "cert-key-file",
-       '\0',
-@@ -563,15 +663,17 @@ main (int argc, const char **argv)
-          "(default \"service-key.pem\")"),
-       NULL,
-     },
-+
-     {
-       "cert-key-mode",
-       '\0',
-       POPT_ARG_INT,
-       &cert_key_mode,
-       0,
--      _ ("File mode of the created certificate key. (default: 0600)"),
--      _ ("0600"),
-+      _ ("File mode of the created certificate key."),
-+      SSCG_KEY_DEFAULT_MODE_HELP,
-     },
-+
-     {
-       "cert-key-password",
-       'p',
-@@ -605,8 +707,115 @@ main (int argc, const char **argv)
-       NULL
-     },
- 
-+    {
-+      "client-file",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &client_file,
-+      0,
-+      _ ("Path where a client authentication certificate will be stored."),
-+      NULL
-+    },
-+    {
-+      "client-mode",
-+      '\0',
-+      POPT_ARG_INT,
-+      &client_mode,
-+      0,
-+      _ ("File mode of the created certificate."),
-+      SSCG_CERT_DEFAULT_MODE_HELP,
-+    },
-+
-+    {
-+      "client-key-file",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &client_key_file,
-+      0,
-+      _ ("Path where the client's private key will be stored. "
-+         "(default is client-file with a .key suffix, if "
-+         "--client-file was passed, otherwise this file will not "
-+         "be generated.)"),
-+      NULL,
-+    },
-+
-+    {
-+      "client-key-mode",
-+      '\0',
-+      POPT_ARG_INT,
-+      &client_key_mode,
-+      0,
-+      _ ("File mode of the created certificate key."),
-+      SSCG_KEY_DEFAULT_MODE_HELP,
-+    },
-+
-+    {
-+      "client-key-password",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &client_key_password,
-+      0,
-+      _ ("Provide a password for the client key file. Note that this will be "
-+         "visible in the process table for all users, so this flag should be "
-+         "used for testing purposes only. Use --client-keypassfile or "
-+         "--client-key-password-prompt for secure password entry."),
-+      NULL
-+    },
-+
-+    {
-+      "client-key-passfile",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &client_key_passfile,
-+      0,
-+      _ ("A file containing the password to encrypt the client key file."),
-+      NULL
-+    },
-+
-+    {
-+      "client-key-password-prompt",
-+      '\0',
-+      POPT_ARG_NONE,
-+      &options->client_key_pass_prompt,
-+      0,
-+      _ ("Prompt to enter a password for the client key file."),
-+      NULL
-+    },
-+
-+    {
-+      "dhparams-file",
-+      '\0',
-+      POPT_ARG_STRING,
-+      &dhparams_file,
-+      0,
-+      _("A file to contain a set of generated Diffie-Hellman parameters. "
-+        "If unspecified, no such file will be created."),
-+      NULL
-+    },
-+
-+    {
-+      "dhparams-prime-len",
-+      '\0',
-+      POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
-+      &options->dhparams_prime_len,
-+      0,
-+      _ ("The length of the prime number to generate for dhparams, in bits."),
-+      NULL
-+    },
-+
-+    {
-+      "dhparams-generator",
-+      '\0',
-+      POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
-+      &options->dhparams_generator,
-+      0,
-+      _ ("The generator value for dhparams."),
-+      _("{2,3,5}")
-+    },
-+
-     POPT_TABLEEND
-   };
-+  // clang-format on
- 
-   pc = poptGetContext (argv[0], argc, argv, long_options, 0);
-   while ((opt = poptGetNextOpt (pc)) != -1)
-@@ -630,6 +839,8 @@ main (int argc, const char **argv)
-       return 0;
-     }
- 
-+  verbosity = options->verbosity;
-+
-   /* Process the Subject information */
- 
-   if (country)
-@@ -788,6 +999,22 @@ main (int argc, const char **argv)
-         }
-     }
- 
-+  if (client_key_password)
-+    {
-+      options->client_key_pass =
-+        sscg_secure_string_steal (options, client_key_password);
-+    }
-+  else if (client_key_passfile)
-+    {
-+      options->client_key_pass =
-+        sscg_read_pw_file (options, client_key_passfile);
-+      if (!options->client_key_pass)
-+        {
-+          fprintf (
-+            stderr, "Failed to read passphrase from %s", client_key_passfile);
-+          goto done;
-+        }
-+    }
- 
-   if (options->key_strength < options->minimum_key_strength)
-     {
-@@ -822,86 +1049,115 @@ main (int argc, const char **argv)
-   if (options->verbosity >= SSCG_VERBOSE)
-     print_options (options);
- 
--  /* Get the paths of the output files */
--  ret = _sscg_normalize_path (options, ca_file, "./ca.crt", &options->ca_file);
-+  /* Prepare the output files */
-+  ret = sscg_io_utils_add_output_file (options->streams,
-+                                       SSCG_FILE_TYPE_CA,
-+                                       ca_file ? ca_file : "./ca.crt",
-+                                       ca_mode);
-   CHECK_OK (ret);
- 
--  ret =
--    _sscg_normalize_path (options, ca_key_file, NULL, &options->ca_key_file);
-+  ret = sscg_io_utils_add_output_file (
-+    options->streams, SSCG_FILE_TYPE_CA_KEY, ca_key_file, ca_key_mode);
-   CHECK_OK (ret);
--  if (options->verbosity >= SSCG_DEBUG)
--    {
--      fprintf (stdout,
--               "DEBUG: CA Key file path: %s\n",
--               options->ca_key_file ? options->ca_key_file : "(N/A)");
--    }
--
--  ret = _sscg_normalize_path (
--    options, cert_file, "./service.pem", &options->cert_file);
-+
-+  ret = sscg_io_utils_add_output_file (options->streams,
-+                                       SSCG_FILE_TYPE_SVC,
-+                                       cert_file ? cert_file : "./service.pem",
-+                                       cert_mode);
-+  CHECK_OK (ret);
-+
-+  ret = sscg_io_utils_add_output_file (options->streams,
-+                                       SSCG_FILE_TYPE_SVC_KEY,
-+                                       cert_key_file ? cert_key_file :
-+                                                       "./service-key.pem",
-+                                       cert_key_mode);
-+  CHECK_OK (ret);
-+
-+
-+  ret = sscg_io_utils_add_output_file (
-+    options->streams, SSCG_FILE_TYPE_CLIENT, client_file, client_mode);
-+  CHECK_OK (ret);
-+
-+
-+  ret = sscg_io_utils_add_output_file (options->streams,
-+                                       SSCG_FILE_TYPE_CLIENT_KEY,
-+                                       client_key_file ? client_key_file :
-+                                                         client_file,
-+                                       client_key_mode);
-+  CHECK_OK (ret);
-+
-+  ret = sscg_io_utils_add_output_file (
-+    options->streams, SSCG_FILE_TYPE_CRL, crl_file, crl_mode);
-   CHECK_OK (ret);
- 
--  ret = _sscg_normalize_path (
--    options, cert_key_file, "./service-key.pem", &options->cert_key_file);
-+  ret = sscg_io_utils_add_output_file (
-+    options->streams, SSCG_FILE_TYPE_DHPARAMS, dhparams_file, dhparams_mode);
-   CHECK_OK (ret);
- 
-   poptFreeContext (pc);
- 
--  /* Validate the file paths */
-+  /* Validate and open the file paths */
-+  ret = sscg_io_utils_open_output_files (options->streams, options->overwrite);
-+  CHECK_OK (ret);
- 
--  /* Only one key can exist in a single file */
--  if (options->ca_key_file &&
--      strcmp (options->ca_key_file, options->cert_key_file) == 0)
--    {
--      fprintf (stderr,
--               "Certificate key and CA key may not be in the same file.\n");
--      ret = EINVAL;
--      goto done;
--    }
--
--  /* The CA key must not be in the same file as the service cert */
--  if (options->ca_key_file &&
--      strcmp (options->ca_key_file, options->cert_file) == 0)
--    {
--      fprintf (
--        stderr,
--        "CA key and service certificate may not be in the same file.\n");
--      ret = EINVAL;
--      goto done;
--    }
- 
-   /* Generate the private CA for the certificate */
-   ret = create_private_CA (main_ctx, options, &cacert, &cakey);
-   CHECK_OK (ret);
- 
-   /* Generate the service certificate and sign it with the private CA */
--  ret = create_service_cert (
--    main_ctx, options, cacert, cakey, &svc_cert, &svc_key);
-+  ret = create_cert (main_ctx,
-+                     options,
-+                     cacert,
-+                     cakey,
-+                     SSCG_CERT_TYPE_SERVER,
-+                     &svc_cert,
-+                     &svc_key);
-   CHECK_OK (ret);
- 
-+  /* If requested, generate the client auth certificate and sign it with the
-+   * private CA.
-+   */
-+  if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
-+    {
-+      ret = create_cert (main_ctx,
-+                         options,
-+                         cacert,
-+                         cakey,
-+                         SSCG_CERT_TYPE_CLIENT,
-+                         &client_cert,
-+                         &client_key);
-+      CHECK_OK (ret);
-+    }
-+
- 
-   /* ==== Output the final files ==== */
- 
--  /* Set the file-creation mode */
--  if (options->overwrite)
--    {
--      create_mode = talloc_strdup (main_ctx, "w");
--    }
--  else
--    {
--      create_mode = talloc_strdup (main_ctx, "wx");
--    }
--  CHECK_MEM (create_mode);
- 
--  /* Create certificate private key file */
--  if (options->verbosity >= SSCG_DEFAULT)
-+  /* Write private keys first */
-+
-+  if (GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY))
-     {
--      fprintf (
--        stdout, "Writing svc private key to %s \n", options->cert_key_file);
-+      /* This function has a default mechanism for prompting for the
-+       * password if it is passed a cipher and gets a NULL password.
-+       *
-+       * Only pass the cipher if we have a password or were instructed
-+       * to prompt for one.
-+       */
-+      sret = PEM_write_bio_PrivateKey (
-+        GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY),
-+        client_key->evp_pkey,
-+        options->client_key_pass_prompt || options->client_key_pass ?
-+          options->cipher :
-+          NULL,
-+        (unsigned char *)options->client_key_pass,
-+        options->client_key_pass ? strlen (options->client_key_pass) : 0,
-+        NULL,
-+        NULL);
-+      CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
-+      ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
-     }
- 
--  cert_key_out = BIO_new_file (options->cert_key_file, create_mode);
--  CHECK_BIO (cert_key_out, options->cert_key_file);
--
-   /* This function has a default mechanism for prompting for the
-    * password if it is passed a cipher and gets a NULL password.
-    *
-@@ -909,7 +1165,7 @@ main (int argc, const char **argv)
-    * to prompt for one.
-    */
-   sret = PEM_write_bio_PrivateKey (
--    cert_key_out,
-+    GET_BIO (SSCG_FILE_TYPE_SVC_KEY),
-     svc_key->evp_pkey,
-     options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher :
-                                                               NULL,
-@@ -918,83 +1174,11 @@ main (int argc, const char **argv)
-     NULL,
-     NULL);
-   CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
--  BIO_get_fp (cert_key_out, &fp);
--
--  if (options->verbosity >= SSCG_DEBUG)
--    {
--      fprintf (stdout,
--               "DEBUG: Setting svc key file permissions to %o\n",
--               cert_key_mode);
--    }
--  fchmod (fileno (fp), cert_key_mode);
--
--  BIO_free (cert_key_out);
--  cert_key_out = NULL;
--
--
--  /* Create service public certificate */
--  if (options->verbosity >= SSCG_DEFAULT)
--    {
--      fprintf (stdout,
--               "Writing service public certificate to %s\n",
--               options->cert_file);
--    }
--  if (strcmp (options->cert_key_file, options->cert_file) == 0)
--    {
--      cert_out = BIO_new_file (options->cert_file, "a");
--    }
--  else
--    {
--      cert_out = BIO_new_file (options->cert_file, create_mode);
--    }
--  CHECK_BIO (cert_out, options->cert_file);
--
--  sret = PEM_write_bio_X509 (cert_out, svc_cert->certificate);
--  CHECK_SSL (sret, PEM_write_bio_X509 (svc));
--  BIO_get_fp (cert_out, &fp);
--
--  /* If this file matches the keyfile, do not set its permissions */
--  if (strcmp (options->cert_file, options->cert_key_file) == 0)
--    {
--      if (options->verbosity >= SSCG_DEBUG)
--        {
--          fprintf (stdout,
--                   "DEBUG: Not setting service cert file permissions: "
--                   "superseded by the key\n");
--        }
--    }
--  else
--    {
--      if (options->verbosity >= SSCG_DEBUG)
--        {
--          fprintf (stdout,
--                   "DEBUG: Setting service cert file permissions to %o\n",
--                   cert_mode);
--        }
--      fchmod (fileno (fp), cert_mode);
--    }
--  BIO_free (cert_out);
--  cert_out = NULL;
--
-+  ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
- 
-   /* Create CA private key, if requested */
--  if (options->ca_key_file)
-+  if (GET_BIO (SSCG_FILE_TYPE_CA_KEY))
-     {
--      if (options->verbosity >= SSCG_DEFAULT)
--        {
--          fprintf (
--            stdout, "Writing CA private key to %s\n", options->ca_key_file);
--        }
--      if (strcmp (options->ca_file, options->ca_key_file) == 0)
--        {
--          ca_key_out = BIO_new_file (options->ca_key_file, "a");
--        }
--      else
--        {
--          ca_key_out = BIO_new_file (options->ca_key_file, create_mode);
--        }
--      CHECK_BIO (ca_key_out, options->ca_key_file);
--
-       /* This function has a default mechanism for prompting for the
-        * password if it is passed a cipher and gets a NULL password.
-        *
-@@ -1002,7 +1186,7 @@ main (int argc, const char **argv)
-        * to prompt for one.
-        */
-       sret = PEM_write_bio_PrivateKey (
--        ca_key_out,
-+        GET_BIO (SSCG_FILE_TYPE_CA_KEY),
-         cakey->evp_pkey,
-         options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher :
-                                                               NULL,
-@@ -1011,73 +1195,80 @@ main (int argc, const char **argv)
-         NULL,
-         NULL);
-       CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA));
--      BIO_get_fp (ca_key_out, &fp);
--      if (options->verbosity >= SSCG_DEBUG)
--        {
--          fprintf (stdout,
--                   "DEBUG: Setting CA key file permissions to %o\n",
--                   ca_key_mode);
--        }
--      fchmod (fileno (fp), ca_key_mode);
--      BIO_free (ca_key_out);
--      ca_key_out = NULL;
-+      ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA_KEY);
-     }
- 
-+  /* Public keys come next, in chain order */
-+
-+  /* Start with the client certificate */
-+  if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
-+    {
-+      sret = PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_CLIENT),
-+                                 client_cert->certificate);
-+      CHECK_SSL (sret, PEM_write_bio_X509 (client));
-+      ANNOUNCE_WRITE (SSCG_FILE_TYPE_CLIENT);
-+    }
-+
-+  /* Create service public certificate */
-+  sret =
-+    PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_SVC), svc_cert->certificate);
-+  CHECK_SSL (sret, PEM_write_bio_X509 (svc));
-+  ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC);
-+
- 
-   /* Create CA public certificate */
--  if (options->verbosity >= SSCG_DEFAULT)
--    {
--      fprintf (
--        stdout, "Writing CA public certificate to %s\n", options->ca_file);
--    }
--  if (strcmp (options->ca_file, options->cert_file) == 0)
--    {
--      ca_out = BIO_new_file (options->ca_file, "a");
--    }
--  else
--    {
--      ca_out = BIO_new_file (options->ca_file, create_mode);
--    }
--  CHECK_BIO (ca_out, options->ca_file);
--
--  sret = PEM_write_bio_X509 (ca_out, cacert->certificate);
-+  struct sscg_stream *stream =
-+    sscg_io_utils_get_stream_by_type (options->streams, SSCG_FILE_TYPE_CA);
-+  sret = PEM_write_bio_X509 (stream->bio, cacert->certificate);
-   CHECK_SSL (sret, PEM_write_bio_X509 (CA));
--  BIO_get_fp (ca_out, &fp);
--  /* If this file matches the keyfile, do not set its permissions */
--  if (options->ca_key_file &&
--      strcmp (options->ca_file, options->ca_key_file) == 0)
-+  ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA);
-+
-+
-+  /* Then write any non-certificate files */
-+
-+  /* Create CRL file */
-+  if (GET_BIO (SSCG_FILE_TYPE_CRL))
-     {
--      if (options->verbosity >= SSCG_DEBUG)
--        {
--          fprintf (
--            stdout,
--            "DEBUG: Not setting CA file permissions: superseded by a key\n");
--        }
-+      /* The CRL file is left intentionally blank, so do nothing here. The
-+       * file was created as empty, so it will just be closed and have its
-+       * permissions set later.
-+       */
-+      ANNOUNCE_WRITE (SSCG_FILE_TYPE_CRL);
-     }
--  else
-+
-+
-+  /* Create DH parameters file */
-+  if (GET_BIO (SSCG_FILE_TYPE_DHPARAMS))
-     {
--      if (options->verbosity >= SSCG_DEBUG)
--        {
--          fprintf (
--            stdout, "DEBUG: Setting CA file permissions to %o\n", ca_mode);
--        }
--      fchmod (fileno (fp), ca_mode);
-+      /* Open the file before generating the parameters. This avoids wasting
-+       * the time to generate them if the destination is not writable.
-+       */
-+
-+      ret = create_dhparams (main_ctx,
-+                             options->verbosity,
-+                             options->dhparams_prime_len,
-+                             options->dhparams_generator,
-+                             &dhparams);
-+      CHECK_OK (ret);
-+
-+      /* Export the DH parameters to the file */
-+      sret = PEM_write_bio_DHparams (GET_BIO (SSCG_FILE_TYPE_DHPARAMS),
-+                                     dhparams->dh);
-+      CHECK_SSL (sret, PEM_write_bio_DHparams ());
-+      ANNOUNCE_WRITE (SSCG_FILE_TYPE_DHPARAMS);
-     }
--  BIO_free (cert_out);
--  cert_out = NULL;
- 
- 
-+  /* Set the final file permissions */
-+  sscg_io_utils_finalize_output_files (options->streams);
-+
-   ret = EOK;
-+
- done:
--  BIO_free (cert_key_out);
--  BIO_free (cert_out);
--  BIO_free (ca_key_out);
--  BIO_free (ca_out);
--
-   talloc_zfree (main_ctx);
-   if (ret != EOK)
-     {
--      fprintf (stderr, "%s\n", strerror (ret));
-+      SSCG_ERROR ("%s\n", strerror (ret));
-     }
-   return ret;
- }
-diff --git a/test/dhparams_test.c b/test/dhparams_test.c
-new file mode 100644
-index 0000000000000000000000000000000000000000..b054b40ea73ca98870836bd89ea10677be8fb075
---- /dev/null
-+++ b/test/dhparams_test.c
-@@ -0,0 +1,106 @@
-+/*
-+    This file is part of sscg.
-+
-+    sscg is free software: you can redistribute it and/or modify
-+    it under the terms of the GNU General Public License as published by
-+    the Free Software Foundation, either version 3 of the License, or
-+    (at your option) any later version.
-+
-+    sscg is distributed in the hope that it will be useful,
-+    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+    GNU General Public License for more details.
-+
-+    You should have received a copy of the GNU General Public License
-+    along with sscg.  If not, see <http://www.gnu.org/licenses/>.
-+
-+    Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
-+*/
-+
-+#include <errno.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <talloc.h>
-+#include <openssl/err.h>
-+
-+#include "include/dhparams.h"
-+
-+int
-+main (int argc, char **argv)
-+{
-+  int ret, sret, prime_len, generator;
-+  struct sscg_dhparams *params = NULL;
-+  TALLOC_CTX *main_ctx = NULL;
-+
-+  if (getenv ("SSCG_SKIP_DHPARAMS"))
-+    {
-+      /* Skip this test */
-+      return 77;
-+    }
-+
-+  errno = 0;
-+  prime_len = strtol (argv[1], NULL, 0);
-+  if (errno)
-+    {
-+      fprintf (stderr, "Prime length was not a valid integer.");
-+      ret = errno;
-+      goto done;
-+    }
-+
-+  errno = 0;
-+  generator = strtol (argv[2], NULL, 0);
-+  if (errno)
-+    {
-+      fprintf (stderr, "Generator was not a valid integer.");
-+      ret = errno;
-+      goto done;
-+    }
-+
-+  main_ctx = talloc_new (NULL);
-+
-+  ret = create_dhparams (main_ctx, SSCG_DEBUG, prime_len, generator, &params);
-+  if (ret != EOK)
-+    {
-+      fprintf (stderr,
-+               "Could not generate DH parameters: [%s]",
-+               ERR_error_string (ERR_get_error (), NULL));
-+      goto done;
-+    }
-+
-+  if (!DH_check (params->dh, &sret))
-+    {
-+      ERR_print_errors_fp (stderr);
-+      goto done;
-+    }
-+  if (sret & DH_CHECK_P_NOT_PRIME)
-+    fprintf (stderr, "p value is not prime\n");
-+  if (sret & DH_CHECK_P_NOT_SAFE_PRIME)
-+    fprintf (stderr, "p value is not a safe prime\n");
-+  if (sret & DH_CHECK_Q_NOT_PRIME)
-+    fprintf (stderr, "q value is not a prime\n");
-+  if (sret & DH_CHECK_INVALID_Q_VALUE)
-+    fprintf (stderr, "q value is invalid\n");
-+  if (sret & DH_CHECK_INVALID_J_VALUE)
-+    fprintf (stderr, "j value is invalid\n");
-+  if (sret & DH_UNABLE_TO_CHECK_GENERATOR)
-+    fprintf (stderr, "unable to check the generator value\n");
-+  if (sret & DH_NOT_SUITABLE_GENERATOR)
-+    fprintf (stderr, "the g value is not a generator\n");
-+
-+  if (sret != 0)
-+    {
-+      /*
-+       * We have generated parameters but DH_check() indicates they are
-+       * invalid! This should never happen!
-+       */
-+      fprintf (stderr, "ERROR: Invalid parameters generated\n");
-+      ret = EIO;
-+      goto done;
-+    }
-+
-+  ret = EOK;
-+
-+done:
-+  talloc_free (main_ctx);
-+  return ret;
-+}
--- 
-2.23.0
-
diff --git a/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch b/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch
deleted file mode 100644
index 152464a..0000000
--- a/SOURCES/0008-Fix-client-cert-issues-found-by-CI-tests.patch
+++ /dev/null
@@ -1,98 +0,0 @@
-From 8afa0ce578ecd5cc3a397707fdb163cc169b9bd1 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Fri, 13 Dec 2019 08:25:01 -0500
-Subject: [PATCH 8/8] Fix client-cert issues found by CI tests
-
-Resolves: rhbz#1720667
-
-Better error message for client certs without public key file
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix memory leak in sscg_sign_x509_csr()
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Address clang-analyzer warning
-
-clang-analyzer determined that it was possible for the GET_BIO()
-return value to have changed between conditional creation of the
-client certificate and writing it out. This patch stores the result
-of the lookup so it's certain to be consistent.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- src/io_utils.c | 4 ++--
- src/sscg.c     | 8 +++++---
- src/x509.c     | 1 +
- 3 files changed, 8 insertions(+), 5 deletions(-)
-
-diff --git a/src/io_utils.c b/src/io_utils.c
-index 809a1da0e455afa0dba0796a5f7ac406742328a1..a2502afb20f4bcb536428f3528900c2bb06997f5 100644
---- a/src/io_utils.c
-+++ b/src/io_utils.c
-@@ -363,8 +363,8 @@ sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite)
- 
-     case IO_UTILS_CLIENT_UNMATCHED:
-       SSCG_ERROR (
--        "The client certificate must have both public and private key "
--        "locations specified.\n");
-+        "The client certificate must have the public key location "
-+        "specified.\n");
-       ret = EINVAL;
-       goto done;
- 
-diff --git a/src/sscg.c b/src/sscg.c
-index 470af815d91f5170a1e8fe00006dbaee4d07b209..f34a43b83e562d0bd7da9a77e25911762db83693 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -300,6 +300,7 @@ main (int argc, const char **argv)
-   char *cert_key_password = NULL;
-   char *cert_key_passfile = NULL;
- 
-+  bool build_client_cert = false;
-   int client_mode = SSCG_CERT_DEFAULT_MODE;
-   int client_key_mode = SSCG_KEY_DEFAULT_MODE;
-   char *client_key_password = NULL;
-@@ -1118,7 +1119,8 @@ main (int argc, const char **argv)
-   /* If requested, generate the client auth certificate and sign it with the
-    * private CA.
-    */
--  if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
-+  build_client_cert = !!(GET_BIO (SSCG_FILE_TYPE_CLIENT));
-+  if (build_client_cert)
-     {
-       ret = create_cert (main_ctx,
-                          options,
-@@ -1136,7 +1138,7 @@ main (int argc, const char **argv)
- 
-   /* Write private keys first */
- 
--  if (GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY))
-+  if (build_client_cert)
-     {
-       /* This function has a default mechanism for prompting for the
-        * password if it is passed a cipher and gets a NULL password.
-@@ -1201,7 +1203,7 @@ main (int argc, const char **argv)
-   /* Public keys come next, in chain order */
- 
-   /* Start with the client certificate */
--  if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
-+  if (build_client_cert)
-     {
-       sret = PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_CLIENT),
-                                  client_cert->certificate);
-diff --git a/src/x509.c b/src/x509.c
-index 18f0627bc64e7cb503a9e81c36dbe726186d1144..c173f539791fbbc51e52e6b121e587dca43924d4 100644
---- a/src/x509.c
-+++ b/src/x509.c
-@@ -482,5 +482,6 @@ done:
-       *_cert = talloc_steal (mem_ctx, scert);
-     }
-   X509_NAME_free (subject);
-+  talloc_free(tmp_ctx);
-   return ret;
- }
--- 
-2.23.0
-
diff --git a/SOURCES/0009-Fix-help-message-for-client-key-file.patch b/SOURCES/0009-Fix-help-message-for-client-key-file.patch
deleted file mode 100644
index 04e0777..0000000
--- a/SOURCES/0009-Fix-help-message-for-client-key-file.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From fa6be1a9bbc8c5d42a248e398e3aac08078e311e Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Fri, 13 Dec 2019 11:51:43 -0500
-Subject: [PATCH 9/9] Fix help message for --client-key-file
-
-Resolves: rhbz#1720667
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Further clarify --client-key-file help message
-
-Resolves: rhbz#1720667
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- src/sscg.c | 4 +---
- 1 file changed, 1 insertion(+), 3 deletions(-)
-
-diff --git a/src/sscg.c b/src/sscg.c
-index f34a43b83e562d0bd7da9a77e25911762db83693..4d009a67488e83c4332f58ee52f7d6ea72a8ddbd 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -734,9 +734,7 @@ main (int argc, const char **argv)
-       &client_key_file,
-       0,
-       _ ("Path where the client's private key will be stored. "
--         "(default is client-file with a .key suffix, if "
--         "--client-file was passed, otherwise this file will not "
--         "be generated.)"),
-+         "(default is the client-file)"),
-       NULL,
-     },
- 
--- 
-2.24.1
-
diff --git a/SOURCES/0010-Better-validation-of-command-line-arguments.patch b/SOURCES/0010-Better-validation-of-command-line-arguments.patch
deleted file mode 100644
index 7e934e9..0000000
--- a/SOURCES/0010-Better-validation-of-command-line-arguments.patch
+++ /dev/null
@@ -1,920 +0,0 @@
-From 87530e9ebc872761c06506f3cb6a4fa5c494a614 Mon Sep 17 00:00:00 2001
-From: Stephen Gallagher <sgallagh@redhat.com>
-Date: Tue, 7 Jan 2020 14:32:01 -0500
-Subject: [PATCH 10/10] Better validation of command line arguments
-
-Check that key passphrases are within 4-1023 characters
-
-OpenSSL CLI tools cannot handle files with passphrases outside this
-range.
-
-Resolves: rhbz#1784441
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Output private keys with 2048 iteration count
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Rework passphrase handling
-
-Handle passphrases as part of the sscg_stream for a file. This will
-allow us to check for relevance as well as reducing code duplication.
-
-Resolves: rhbz#1784443
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix wrong x509 version in CSR
-
-This was, fortunately, not causing any problems because the signing
-process resulted in the certificates being generated with the
-correct version. It's best to be correct anyway.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix memory leaks
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix alignment issue with popt
-
-The boolean values need to be explicitly defined as int because
-a bool may not be aligned properly. It was working prior to some
-recent changes by lucky accident.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Prevent uninitialized read error
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Add missing newline for error message
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix OpenSSL 1.0 support
-
-The symbol UI_F_UI_SET_RESULT changed to UI_F_UI_SET_RESULT_EX in
-OpenSSL 1.1, but no other semantics changed that we care about.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix formatting
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Fix missing error check
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
-
-Read long password files properly
-
-Long passphrase files may require more than a single call to BIO_read()
-to gather the whole string.
-
-Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
----
- include/io_utils.h |  37 ++++++-
- include/key.h      |   6 +-
- include/sscg.h     |  42 +++++---
- include/x509.h     |   6 +-
- meson.build        |   1 +
- src/io_utils.c     | 199 ++++++++++++++++++++++++++++++++++++-
- src/sscg.c         | 239 +++++++--------------------------------------
- src/x509.c         |   6 +-
- 8 files changed, 310 insertions(+), 226 deletions(-)
-
-diff --git a/include/io_utils.h b/include/io_utils.h
-index 6a89a476b3d982447b6603153c6765835cd67464..907097c7ff1f7ae3c3adf35d0dfba0f5763dc8c0 100644
---- a/include/io_utils.h
-+++ b/include/io_utils.h
-@@ -24,6 +24,7 @@
- #include <stdbool.h>
- #include <talloc.h>
- 
-+#include "include/key.h"
- #include "include/sscg.h"
- 
- 
-@@ -33,6 +34,9 @@ struct sscg_stream
-   char *path;
-   int mode;
-   int filetypes;
-+
-+  bool pass_prompt;
-+  char *passphrase;
- };
- 
- 
-@@ -69,8 +73,6 @@ sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
-  * @path: The path to the file on disk.
-  * @mode: The filesystem mode this file should have when written to disk.
-  * See chmod(1) for the possible values.
-- * @overwrite: If true, replace any existing file at @normalized_path. If
-- * false, opening will fail if it already exists and return an error.
-  *
-  * Prepares all output filenames to be opened. Files are not created until
-  * sscg_io_utils_open_output_files() is called.
-@@ -82,9 +84,40 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams,
-                                int mode);
- 
- 
-+/**
-+ * sscg_io_utils_add_output_key:
-+ * @streams: The array of streams from the sscg_options
-+ * @filetype:
-+ * @path: The path to the file on disk.
-+ * @mode: The filesystem mode this file should have when written to disk.
-+ * See chmod(1) for the possible values.
-+ * @pass_prompt: Whether the user should be prompted to enter a passphrase
-+ * interactively.
-+ * @passphrase: The passphrase supplied at the command line.
-+ * @passfile: The path to a file containing the passphrase.
-+ *
-+ * Prepares all output filenames to be opened. Files are not created until
-+ * sscg_io_utils_open_output_files() is called.
-+ */
-+int
-+sscg_io_utils_add_output_key (struct sscg_stream **streams,
-+                              enum sscg_file_type filetype,
-+                              const char *path,
-+                              int mode,
-+                              bool pass_prompt,
-+                              char *passphrase,
-+                              char *passfile);
-+
-+
- int
- sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite);
- 
-+int
-+sscg_io_utils_write_privatekey (struct sscg_stream **streams,
-+                                enum sscg_file_type filetype,
-+                                struct sscg_evp_pkey *key,
-+                                struct sscg_options *options);
-+
- /* If this function fails, some of the output files may be left as 0400 */
- int
- sscg_io_utils_finalize_output_files (struct sscg_stream **streams);
-diff --git a/include/key.h b/include/key.h
-index ef871d6937e2fc805a445d6686263b023a38eaaa..4c32cad04950ee7fd75ec4144147eb919280c00a 100644
---- a/include/key.h
-+++ b/include/key.h
-@@ -17,15 +17,15 @@
-     Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
- */
- 
-+#ifndef _SSCG_KEY_H
-+#define _SSCG_KEY_H
-+
- #include <openssl/rsa.h>
- #include <openssl/evp.h>
- 
- #include "include/sscg.h"
- #include "include/bignum.h"
- 
--#ifndef _SSCG_KEY_H
--#define _SSCG_KEY_H
--
- struct sscg_evp_pkey
- {
-   EVP_PKEY *evp_pkey;
-diff --git a/include/sscg.h b/include/sscg.h
-index 2744404c25c68ed905ca621bb955e0c04b33ca81..96b78152ccc492deafbbc61eb98702562a8fe5e6 100644
---- a/include/sscg.h
-+++ b/include/sscg.h
-@@ -20,17 +20,18 @@
- /* This is a master header file that should be included by all
-    sscg source files. */
- 
-+
-+#ifndef _SSCG_H
-+#define _SSCG_H
-+
- #include <errno.h>
- #include <openssl/ssl.h>
- #include <openssl/err.h>
-+#include <openssl/ui.h>
- #include <stdbool.h>
- #include <talloc.h>
- #include <stdint.h>
- 
--#include "include/io_utils.h"
--
--#ifndef _SSCG_H
--#define _SSCG_H
- 
- /* TODO: implement internationalization */
- 
-@@ -81,15 +82,34 @@
-     }                                                                         \
-   while (0)
- 
-+/* The function changed in 1.1, but the library and reason names did not */
-+#ifndef UI_F_UI_SET_RESULT_EX
-+#define UI_F_UI_SET_RESULT_EX UI_F_UI_SET_RESULT
-+#endif
-+
- #define CHECK_SSL(_sslret, _fn)                                               \
-   do                                                                          \
-     {                                                                         \
-       if (_sslret != 1)                                                       \
-         {                                                                     \
-           /* Get information about error from OpenSSL */                      \
-+          unsigned long _ssl_error = ERR_get_error ();                        \
-+          if ((ERR_GET_LIB (_ssl_error) == ERR_LIB_UI) &&                     \
-+              (ERR_GET_FUNC (_ssl_error) == UI_F_UI_SET_RESULT_EX) &&         \
-+              ((ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_LARGE) ||      \
-+               (ERR_GET_REASON (_ssl_error) == UI_R_RESULT_TOO_SMALL)))       \
-+            {                                                                 \
-+              fprintf (                                                       \
-+                stderr,                                                       \
-+                "Passphrases must be between %d and %d characters. \n",       \
-+                SSCG_MIN_KEY_PASS_LEN,                                        \
-+                SSCG_MAX_KEY_PASS_LEN);                                       \
-+              ret = EINVAL;                                                   \
-+              goto done;                                                      \
-+            }                                                                 \
-           fprintf (stderr,                                                    \
-                    "Error occurred in " #_fn ": [%s].\n",                     \
--                   ERR_error_string (ERR_get_error (), NULL));                \
-+                   ERR_error_string (_ssl_error, NULL));                      \
-           ret = EIO;                                                          \
-           goto done;                                                          \
-         }                                                                     \
-@@ -223,12 +243,9 @@ struct sscg_options
-   const EVP_CIPHER *cipher;
-   const EVP_MD *hash_fn;
- 
--  bool ca_key_pass_prompt;
--  char *ca_key_pass;
--  bool cert_key_pass_prompt;
--  char *cert_key_pass;
--  bool client_key_pass_prompt;
--  char *client_key_pass;
-+  int ca_key_pass_prompt;
-+  int cert_key_pass_prompt;
-+  int client_key_pass_prompt;
- 
-   /* Output Files */
-   struct sscg_stream **streams;
-@@ -251,4 +268,7 @@ enum sscg_cert_type
-   SSCG_NUM_CERT_TYPES
- };
- 
-+#define SSCG_MIN_KEY_PASS_LEN 4
-+#define SSCG_MAX_KEY_PASS_LEN 1023
-+
- #endif /* _SSCG_H */
-diff --git a/include/x509.h b/include/x509.h
-index 865cd0018d3ea77915cd86349e333ae6f4de2af0..cc7e498d06c4d2e503d7d8748dfd5386f9ad0794 100644
---- a/include/x509.h
-+++ b/include/x509.h
-@@ -17,6 +17,9 @@
-     Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
- */
- 
-+#ifndef _SSCG_X509_H
-+#define _SSCG_X509_H
-+
- #include <openssl/x509.h>
- #include <openssl/x509v3.h>
- 
-@@ -24,9 +27,6 @@
- #include "include/bignum.h"
- #include "include/key.h"
- 
--#ifndef _SSCG_X509_H
--#define _SSCG_X509_H
--
- struct sscg_cert_info
- {
-   /* === Input Data === */
-diff --git a/meson.build b/meson.build
-index eb339ea8c768adab6d576736fbe476b83529e78d..3d8937ce73dc84f652f6fdad461a1468a532f0f2 100644
---- a/meson.build
-+++ b/meson.build
-@@ -76,6 +76,7 @@ sscg_lib_hdrs = [
-     'include/dhparams.h',
-     'include/io_utils.h',
-     'include/key.h',
-+    'include/sscg.h',
-     'include/x509.h',
- ]
- 
-diff --git a/src/io_utils.c b/src/io_utils.c
-index a2502afb20f4bcb536428f3528900c2bb06997f5..1b8bc41c3849acbe4657ae14dfe55e3010957129 100644
---- a/src/io_utils.c
-+++ b/src/io_utils.c
-@@ -24,8 +24,14 @@
- #include <talloc.h>
- 
- #include "include/io_utils.h"
-+#include "include/key.h"
- #include "include/sscg.h"
- 
-+
-+/* Same as OpenSSL CLI */
-+#define MAX_PW_LEN 1024
-+
-+
- int
- sscg_normalize_path (TALLOC_CTX *mem_ctx,
-                      const char *path,
-@@ -62,6 +68,12 @@ sscg_stream_destructor (TALLOC_CTX *ptr)
- 
-   BIO_free (stream->bio);
- 
-+  /* Zero out the memory before freeing it so we don't leak passwords */
-+  if (stream->passphrase)
-+    {
-+      memset (stream->passphrase, 0, strnlen (stream->passphrase, MAX_PW_LEN));
-+    }
-+
-   return 0;
- }
- 
-@@ -147,11 +159,101 @@ sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
- }
- 
- 
-+/* This function takes a copy of a string into a talloc hierarchy and memsets
-+ * the original string to zeroes to avoid leaking it when that memory is freed.
-+ */
-+static char *
-+sscg_secure_string_steal (TALLOC_CTX *mem_ctx, char *src)
-+{
-+  char *dest = talloc_strdup (mem_ctx, src);
-+
-+  memset ((void *)src, 0, strlen (src));
-+
-+  return dest;
-+}
-+
-+
-+static int
-+validate_passphrase (struct sscg_stream *stream)
-+{
-+  /* Ignore non-key types */
-+  if (!(stream->filetypes & SSCG_FILE_TYPE_KEYS))
-+    return EOK;
-+
-+  /* Ignore unset passwords; these will be prompted for when writing out the
-+   * key file
-+   */
-+  if (!stream->passphrase)
-+    return EOK;
-+
-+  size_t pass_len = strnlen (stream->passphrase, SSCG_MAX_KEY_PASS_LEN + 1);
-+
-+  if ((pass_len < SSCG_MIN_KEY_PASS_LEN) || (pass_len > SSCG_MAX_KEY_PASS_LEN))
-+    {
-+      SSCG_ERROR ("Passphrases must be between %d and %d characters. \n",
-+                  SSCG_MIN_KEY_PASS_LEN,
-+                  SSCG_MAX_KEY_PASS_LEN);
-+      return EINVAL;
-+    }
-+  return EOK;
-+}
-+
-+
-+static char *
-+sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path)
-+{
-+  int i;
-+  BIO *pwdbio = NULL;
-+  char tpass[MAX_PW_LEN + 1];
-+  int offset = 0;
-+  char *tmp = NULL;
-+  char *password = NULL;
-+
-+  pwdbio = BIO_new_file (path, "r");
-+  if (pwdbio == NULL)
-+    {
-+      fprintf (stderr, "Can't open file %s\n", path);
-+      return NULL;
-+    }
-+
-+  /* Read up to one more character than the MAX_PW_LEN */
-+  for (offset = 0;
-+       (i = BIO_read (pwdbio, tpass + offset, MAX_PW_LEN + 1 - offset)) > 0 &&
-+       offset < (MAX_PW_LEN + 1);
-+       offset += i)
-+    ;
-+
-+  tpass[MAX_PW_LEN] = '\0';
-+
-+  BIO_free_all (pwdbio);
-+  pwdbio = NULL;
-+
-+  if (i < 0)
-+    {
-+      fprintf (stderr, "Error reading password from BIO\n");
-+      return NULL;
-+    }
-+
-+  tmp = strchr (tpass, '\n');
-+  if (tmp != NULL)
-+    *tmp = 0;
-+
-+  password = talloc_strdup (mem_ctx, tpass);
-+
-+  memset (tpass, 0, MAX_PW_LEN + 1);
-+
-+  return password;
-+}
-+
-+
- int
--sscg_io_utils_add_output_file (struct sscg_stream **streams,
--                               enum sscg_file_type filetype,
--                               const char *path,
--                               int mode)
-+sscg_io_utils_add_output_key (struct sscg_stream **streams,
-+                              enum sscg_file_type filetype,
-+                              const char *path,
-+                              int mode,
-+                              bool pass_prompt,
-+                              char *passphrase,
-+                              char *passfile)
- {
-   int ret, i;
-   TALLOC_CTX *tmp_ctx = NULL;
-@@ -163,6 +265,22 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams,
-    */
-   if (path == NULL)
-     {
-+      if (pass_prompt)
-+        {
-+          SSCG_ERROR (
-+            "Passphrase prompt requested for %s, but no file path provided.\n",
-+            sscg_get_file_type_name (filetype));
-+          return EINVAL;
-+        }
-+
-+      if (passphrase)
-+        {
-+          SSCG_ERROR (
-+            "Passphrase provided for %s, but no file path provided.\n",
-+            sscg_get_file_type_name (filetype));
-+          return EINVAL;
-+        }
-+
-       SSCG_LOG (SSCG_DEBUG,
-                 "Got a NULL path with filetype: %s\n",
-                 sscg_get_file_type_name (filetype));
-@@ -220,6 +338,31 @@ sscg_io_utils_add_output_file (struct sscg_stream **streams,
-   /* Add the file type */
-   stream->filetypes |= (1 << filetype);
- 
-+
-+  /* Set the password options */
-+  stream->pass_prompt = pass_prompt;
-+
-+  if (passphrase)
-+    {
-+      stream->passphrase = sscg_secure_string_steal (stream, passphrase);
-+      ret = validate_passphrase (stream);
-+      if (ret != EOK)
-+        goto done;
-+    }
-+  else if (passfile)
-+    {
-+      stream->passphrase = sscg_read_pw_file (stream, passfile);
-+      if (!stream->passphrase)
-+        {
-+          fprintf (stderr, "Failed to read passphrase from %s", passfile);
-+          ret = EIO;
-+          goto done;
-+        }
-+    }
-+  ret = validate_passphrase (stream);
-+  if (ret != EOK)
-+    goto done;
-+
-   ret = EOK;
- 
- done:
-@@ -228,6 +371,17 @@ done:
- }
- 
- 
-+int
-+sscg_io_utils_add_output_file (struct sscg_stream **streams,
-+                               enum sscg_file_type filetype,
-+                               const char *path,
-+                               int mode)
-+{
-+  return sscg_io_utils_add_output_key (
-+    streams, filetype, path, mode, false, NULL, NULL);
-+}
-+
-+
- enum io_utils_errors
- {
-   IO_UTILS_OK = 0,
-@@ -400,6 +554,43 @@ done:
- }
- 
- 
-+int
-+sscg_io_utils_write_privatekey (struct sscg_stream **streams,
-+                                enum sscg_file_type filetype,
-+                                struct sscg_evp_pkey *key,
-+                                struct sscg_options *options)
-+{
-+  int ret, sret;
-+
-+  struct sscg_stream *stream =
-+    sscg_io_utils_get_stream_by_type (streams, filetype);
-+  if (stream)
-+    {
-+      /* This function has a default mechanism for prompting for the
-+       * password if it is passed a cipher and gets a NULL password.
-+       *
-+       * Only pass the cipher if we have a password or were instructed
-+       * to prompt for one.
-+       */
-+      sret = PEM_write_bio_PKCS8PrivateKey (
-+        stream->bio,
-+        key->evp_pkey,
-+        stream->pass_prompt || stream->passphrase ? options->cipher : NULL,
-+        stream->passphrase,
-+        stream->passphrase ? strlen (stream->passphrase) : 0,
-+        NULL,
-+        NULL);
-+      CHECK_SSL (sret, PEM_write_bio_PKCS8PrivateKey);
-+      ANNOUNCE_WRITE (filetype);
-+    }
-+
-+  ret = EOK;
-+
-+done:
-+  return ret;
-+}
-+
-+
- int
- sscg_io_utils_finalize_output_files (struct sscg_stream **streams)
- {
-diff --git a/src/sscg.c b/src/sscg.c
-index 4d009a67488e83c4332f58ee52f7d6ea72a8ddbd..96a9be1232d890590e97c126f8f4a78d571d7247 100644
---- a/src/sscg.c
-+++ b/src/sscg.c
-@@ -18,6 +18,7 @@
- */
- 
- #define _GNU_SOURCE
-+#include <assert.h>
- #include <popt.h>
- #include <stdlib.h>
- #include <stdio.h>
-@@ -40,9 +41,6 @@
- int verbosity;
- 
- 
--/* Same as OpenSSL CLI */
--#define MAX_PW_LEN 1024
--
- static int
- get_security_level (void)
- {
-@@ -140,79 +138,6 @@ print_options (struct sscg_options *opts)
- }
- 
- 
--/* This function takes a copy of a string into a talloc hierarchy and memsets
-- * the original string to zeroes to avoid leaking it when that memory is freed.
-- */
--static char *
--sscg_secure_string_steal (TALLOC_CTX *mem_ctx, char *src)
--{
--  char *dest = talloc_strdup (mem_ctx, src);
--
--  memset (src, 0, strlen (src));
--
--  return dest;
--}
--
--
--static int
--sscg_options_destructor (TALLOC_CTX *opts)
--{
--  struct sscg_options *options =
--    talloc_get_type_abort (opts, struct sscg_options);
--
--  /* Zero out the memory before freeing it so we don't leak passwords */
--  if (options->ca_key_pass)
--    {
--      memset (options->ca_key_pass, 0, strlen (options->ca_key_pass));
--    }
--
--  if (options->cert_key_pass)
--    {
--      memset (options->cert_key_pass, 0, strlen (options->cert_key_pass));
--    }
--
--  return 0;
--}
--
--
--static char *
--sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path)
--{
--  int i;
--  BIO *pwdbio = NULL;
--  char tpass[MAX_PW_LEN];
--  char *tmp = NULL;
--  char *password = NULL;
--
--  pwdbio = BIO_new_file (path, "r");
--  if (pwdbio == NULL)
--    {
--      fprintf (stderr, "Can't open file %s\n", path);
--      return NULL;
--    }
--
--  i = BIO_gets (pwdbio, tpass, MAX_PW_LEN);
--  BIO_free_all (pwdbio);
--  pwdbio = NULL;
--
--  if (i <= 0)
--    {
--      fprintf (stderr, "Error reading password from BIO\n");
--      return NULL;
--    }
--
--  tmp = strchr (tpass, '\n');
--  if (tmp != NULL)
--    *tmp = 0;
--
--  password = talloc_strdup (mem_ctx, tpass);
--
--  memset (tpass, 0, MAX_PW_LEN);
--
--  return password;
--}
--
--
- const char *
- sscg_get_verbosity_name (enum sscg_verbosity type)
- {
-@@ -310,12 +235,14 @@ main (int argc, const char **argv)
-   struct sscg_evp_pkey *cakey;
-   struct sscg_x509_cert *svc_cert;
-   struct sscg_evp_pkey *svc_key;
--  struct sscg_x509_cert *client_cert;
--  struct sscg_evp_pkey *client_key;
-+  struct sscg_x509_cert *client_cert = NULL;
-+  struct sscg_evp_pkey *client_key = NULL;
- 
-   int dhparams_mode = SSCG_CERT_DEFAULT_MODE;
-   struct sscg_dhparams *dhparams = NULL;
- 
-+  struct sscg_stream *stream = NULL;
-+
-   /* Always use umask 0577 for generating certificates and keys
-        This means that it's opened as write-only by the effective
-        user. */
-@@ -335,7 +262,6 @@ main (int argc, const char **argv)
- 
-   options = talloc_zero (main_ctx, struct sscg_options);
-   CHECK_MEM (options);
--  talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor);
- 
-   options->streams =
-     talloc_zero_array (options, struct sscg_stream *, SSCG_NUM_FILE_TYPES);
-@@ -965,56 +891,6 @@ main (int argc, const char **argv)
-         }
-     }
- 
--  /* Password handling */
--  if (ca_key_password)
--    {
--      options->ca_key_pass =
--        sscg_secure_string_steal (options, ca_key_password);
--    }
--  else if (ca_key_passfile)
--    {
--      options->ca_key_pass = sscg_read_pw_file (options, ca_key_passfile);
--      if (!options->ca_key_pass)
--        {
--          fprintf (
--            stderr, "Failed to read passphrase from %s", ca_key_passfile);
--          goto done;
--        }
--    }
--
--  if (cert_key_password)
--    {
--      options->cert_key_pass =
--        sscg_secure_string_steal (options, cert_key_password);
--    }
--  else if (cert_key_passfile)
--    {
--      options->cert_key_pass = sscg_read_pw_file (options, cert_key_passfile);
--      if (!options->cert_key_pass)
--        {
--          fprintf (
--            stderr, "Failed to read passphrase from %s", cert_key_passfile);
--          goto done;
--        }
--    }
--
--  if (client_key_password)
--    {
--      options->client_key_pass =
--        sscg_secure_string_steal (options, client_key_password);
--    }
--  else if (client_key_passfile)
--    {
--      options->client_key_pass =
--        sscg_read_pw_file (options, client_key_passfile);
--      if (!options->client_key_pass)
--        {
--          fprintf (
--            stderr, "Failed to read passphrase from %s", client_key_passfile);
--          goto done;
--        }
--    }
--
-   if (options->key_strength < options->minimum_key_strength)
-     {
-       fprintf (stderr,
-@@ -1055,8 +931,13 @@ main (int argc, const char **argv)
-                                        ca_mode);
-   CHECK_OK (ret);
- 
--  ret = sscg_io_utils_add_output_file (
--    options->streams, SSCG_FILE_TYPE_CA_KEY, ca_key_file, ca_key_mode);
-+  ret = sscg_io_utils_add_output_key (options->streams,
-+                                      SSCG_FILE_TYPE_CA_KEY,
-+                                      ca_key_file,
-+                                      ca_key_mode,
-+                                      options->ca_key_pass_prompt,
-+                                      ca_key_password,
-+                                      ca_key_passfile);
-   CHECK_OK (ret);
- 
-   ret = sscg_io_utils_add_output_file (options->streams,
-@@ -1065,11 +946,14 @@ main (int argc, const char **argv)
-                                        cert_mode);
-   CHECK_OK (ret);
- 
--  ret = sscg_io_utils_add_output_file (options->streams,
--                                       SSCG_FILE_TYPE_SVC_KEY,
--                                       cert_key_file ? cert_key_file :
--                                                       "./service-key.pem",
--                                       cert_key_mode);
-+  ret = sscg_io_utils_add_output_key (options->streams,
-+                                      SSCG_FILE_TYPE_SVC_KEY,
-+                                      cert_key_file ? cert_key_file :
-+                                                      "./service-key.pem",
-+                                      cert_key_mode,
-+                                      options->cert_key_pass_prompt,
-+                                      cert_key_password,
-+                                      cert_key_passfile);
-   CHECK_OK (ret);
- 
- 
-@@ -1078,11 +962,14 @@ main (int argc, const char **argv)
-   CHECK_OK (ret);
- 
- 
--  ret = sscg_io_utils_add_output_file (options->streams,
--                                       SSCG_FILE_TYPE_CLIENT_KEY,
--                                       client_key_file ? client_key_file :
--                                                         client_file,
--                                       client_key_mode);
-+  ret = sscg_io_utils_add_output_key (options->streams,
-+                                      SSCG_FILE_TYPE_CLIENT_KEY,
-+                                      client_key_file ? client_key_file :
-+                                                        client_file,
-+                                      client_key_mode,
-+                                      options->client_key_pass_prompt,
-+                                      client_key_password,
-+                                      client_key_passfile);
-   CHECK_OK (ret);
- 
-   ret = sscg_io_utils_add_output_file (
-@@ -1136,67 +1023,17 @@ main (int argc, const char **argv)
- 
-   /* Write private keys first */
- 
--  if (build_client_cert)
--    {
--      /* This function has a default mechanism for prompting for the
--       * password if it is passed a cipher and gets a NULL password.
--       *
--       * Only pass the cipher if we have a password or were instructed
--       * to prompt for one.
--       */
--      sret = PEM_write_bio_PrivateKey (
--        GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY),
--        client_key->evp_pkey,
--        options->client_key_pass_prompt || options->client_key_pass ?
--          options->cipher :
--          NULL,
--        (unsigned char *)options->client_key_pass,
--        options->client_key_pass ? strlen (options->client_key_pass) : 0,
--        NULL,
--        NULL);
--      CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
--      ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
--    }
-+  ret = sscg_io_utils_write_privatekey (
-+    options->streams, SSCG_FILE_TYPE_CLIENT_KEY, client_key, options);
-+  CHECK_OK (ret);
- 
--  /* This function has a default mechanism for prompting for the
--   * password if it is passed a cipher and gets a NULL password.
--   *
--   * Only pass the cipher if we have a password or were instructed
--   * to prompt for one.
--   */
--  sret = PEM_write_bio_PrivateKey (
--    GET_BIO (SSCG_FILE_TYPE_SVC_KEY),
--    svc_key->evp_pkey,
--    options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher :
--                                                              NULL,
--    (unsigned char *)options->cert_key_pass,
--    options->cert_key_pass ? strlen (options->cert_key_pass) : 0,
--    NULL,
--    NULL);
--  CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
--  ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
-+  ret = sscg_io_utils_write_privatekey (
-+    options->streams, SSCG_FILE_TYPE_SVC_KEY, svc_key, options);
-+  CHECK_OK (ret);
- 
--  /* Create CA private key, if requested */
--  if (GET_BIO (SSCG_FILE_TYPE_CA_KEY))
--    {
--      /* This function has a default mechanism for prompting for the
--       * password if it is passed a cipher and gets a NULL password.
--       *
--       * Only pass the cipher if we have a password or were instructed
--       * to prompt for one.
--       */
--      sret = PEM_write_bio_PrivateKey (
--        GET_BIO (SSCG_FILE_TYPE_CA_KEY),
--        cakey->evp_pkey,
--        options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher :
--                                                              NULL,
--        (unsigned char *)options->ca_key_pass,
--        options->ca_key_pass ? strlen (options->ca_key_pass) : 0,
--        NULL,
--        NULL);
--      CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA));
--      ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA_KEY);
--    }
-+  ret = sscg_io_utils_write_privatekey (
-+    options->streams, SSCG_FILE_TYPE_CA_KEY, cakey, options);
-+  CHECK_OK (ret);
- 
-   /* Public keys come next, in chain order */
- 
-@@ -1217,7 +1054,7 @@ main (int argc, const char **argv)
- 
- 
-   /* Create CA public certificate */
--  struct sscg_stream *stream =
-+  stream =
-     sscg_io_utils_get_stream_by_type (options->streams, SSCG_FILE_TYPE_CA);
-   sret = PEM_write_bio_X509 (stream->bio, cacert->certificate);
-   CHECK_SSL (sret, PEM_write_bio_X509 (CA));
-diff --git a/src/x509.c b/src/x509.c
-index c173f539791fbbc51e52e6b121e587dca43924d4..42315d42d1e03460a8121e1592d8e7fcc0fef1df 100644
---- a/src/x509.c
-+++ b/src/x509.c
-@@ -72,7 +72,7 @@ _sscg_certinfo_destructor (TALLOC_CTX *ctx)
-   struct sscg_cert_info *certinfo =
-     talloc_get_type_abort (ctx, struct sscg_cert_info);
- 
--  sk_X509_EXTENSION_free (certinfo->extensions);
-+  sk_X509_EXTENSION_pop_free (certinfo->extensions, X509_EXTENSION_free);
- 
-   return 0;
- }
-@@ -155,7 +155,7 @@ sscg_x509v3_csr_new (TALLOC_CTX *mem_ctx,
-   talloc_set_destructor ((TALLOC_CTX *)csr, _sscg_csr_destructor);
- 
-   /* We will generate only x509v3 certificates */
--  sslret = X509_REQ_set_version (csr->x509_req, 3);
-+  sslret = X509_REQ_set_version (csr->x509_req, 2);
-   CHECK_SSL (sslret, X509_REQ_set_version);
- 
-   subject = X509_REQ_get_subject_name (csr->x509_req);
-@@ -461,6 +461,8 @@ sscg_sign_x509_csr (TALLOC_CTX *mem_ctx,
-         }
-       sslret = X509_add_ext (cert, ext, -1);
-       CHECK_SSL (sslret, X509_add_ext);
-+
-+      X509_EXTENSION_free (ext);
-     }
- 
-   /* Sign the new certificate */
--- 
-2.24.1
-
diff --git a/SPECS/sscg.spec b/SPECS/sscg.spec
index 235e7fb..52431b8 100644
--- a/SPECS/sscg.spec
+++ b/SPECS/sscg.spec
@@ -8,16 +8,13 @@
 
 
 Name:           sscg
-Version:        2.3.3
-Release:        14%{?dist}
+Version:        3.0.0
+Release:        5%{?dist}
 Summary:        Simple SSL certificate generator
 
-License:        BSD
+License:        GPLv3+ with exceptions
 URL:            https://%{provider_prefix}
-
-# Run ./sscg-strip.sh to produce a tarball with the bundled popt library
-# stripped out to reduce license issues.
-Source0:        https://%{provider_prefix}/releases/download/%{repo}-%{version}/%{repo}-%{version}-stripped.tar.xz
+Source0:        https://%{provider_prefix}/releases/download/%{repo}-%{version}/%{repo}-%{version}.tar.xz
 
 BuildRequires:  gcc
 BuildRequires:  libtalloc-devel
@@ -28,23 +25,11 @@ BuildRequires:  meson
 BuildRequires:  ninja-build
 BuildRequires:  help2man
 
-# Patches
-Patch0001: 0001-Generate-manpage.patch
-Patch0002: 0002-Adjust-defaults-based-on-system-security-level.patch
-Patch0003: 0003-Adjust-hash-defaults-based-on-system-security-level.patch
-Patch0004: 0004-Properly-check-all-return-values.patch
-
-# RHBZ #1717880
-Patch0005: 0005-Add-password-support-for-private-keys.patch
-Patch0006: 0006-Allow-specifying-keyfile-password-by-file.patch
 
-# RHBZ #1720667
-Patch0007: 0007-Add-support-for-client-certificates-and-dhparams.patch
-Patch0008: 0008-Fix-client-cert-issues-found-by-CI-tests.patch
-Patch0009: 0009-Fix-help-message-for-client-key-file.patch
+Patch0001: 0001-Drop-usage-of-ERR_GET_FUNC.patch
+Patch0002: 0002-Correct-certificate-lifetime-calculation.patch
+Patch0003: 0003-Truncate-IP-address-in-SAN.patch
 
-# RHBZ #1784441 and 1784443
-Patch0010: 0010-Better-validation-of-command-line-arguments.patch
 
 %description
 A utility to aid in the creation of more secure "self-signed"
@@ -54,7 +39,6 @@ client machine to trust the service certificate without needing to set
 up a full PKI environment and without exposing the machine to a risk of
 false signatures from the service certificate.
 
-
 %prep
 %autosetup -p1
 
@@ -67,10 +51,7 @@ false signatures from the service certificate.
 %meson_install
 
 %check
-
-%ifnarch %{arm}
-%meson_test
-%endif
+%meson_test -t 10
 
 %files
 %license COPYING
@@ -79,6 +60,15 @@ false signatures from the service certificate.
 %{_mandir}/man8/%{name}.8*
 
 %changelog
+* Thu Jul 14 2022 Stephen Gallagher <sgallagh@redhat.com> - 3.0.0-5
+- Rebase to sscg 3.0.0
+- Resolves: rhbz#2107369
+- Resolves: rhbz#2091525
+
+* Thu Jun 02 2022 Stephen Gallagher <sgallagh@redhat.com> - 2.3.3-15
+- Fix certificate lifetime calculation
+- Resolves: rhbz#2091525
+
 * Tue Jan 21 2020 Stephen Gallagher <sgallagh@redhat.com> - 2.3.3-14
 - Properly handling reading long passphrase files.