cmdline app: add provider commandline options.

Add a -provider option to allow providers to be loaded. This option can be
specified multiple times.

Add a -provider_path option to allow the path to providers to be specified.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/11167)
diff --git a/apps/ca.c b/apps/ca.c
index f201efe..84e1905 100644
--- a/apps/ca.c
+++ b/apps/ca.c
@@ -148,7 +148,7 @@
     OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID,
     OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS,
     OPT_RAND_SERIAL,
-    OPT_R_ENUM, OPT_SM2ID, OPT_SM2HEXID,
+    OPT_R_ENUM, OPT_SM2ID, OPT_SM2HEXID, OPT_PROV_ENUM,
     /* Do not change the order here; see related case statements below */
     OPT_CRL_REASON, OPT_CRL_HOLD, OPT_CRL_COMPROMISE, OPT_CRL_CA_COMPROMISE
 } OPTION_CHOICE;
@@ -237,6 +237,7 @@
     {"revoke", OPT_REVOKE, '<', "Revoke a cert (given in file)"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"certreq", 0, 0, "Certificate requests to be signed (optional)"},
@@ -361,6 +362,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_KEY:
             key = opt_arg();
             break;
diff --git a/apps/ciphers.c b/apps/ciphers.c
index 280a5ab..9bbc12a 100644
--- a/apps/ciphers.c
+++ b/apps/ciphers.c
@@ -27,7 +27,7 @@
     OPT_PSK,
     OPT_SRP,
     OPT_CIPHERSUITES,
-    OPT_V, OPT_UPPER_V, OPT_S
+    OPT_V, OPT_UPPER_V, OPT_S, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS ciphers_options[] = {
@@ -67,6 +67,7 @@
 #endif
     {"ciphersuites", OPT_CIPHERSUITES, 's',
      "Configure the TLSv1.3 ciphersuites to use"},
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"cipher", 0, 0, "Cipher string to decode (optional)"},
@@ -169,6 +170,10 @@
         case OPT_CIPHERSUITES:
             ciphersuites = opt_arg();
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argv = opt_rest();
diff --git a/apps/cms.c b/apps/cms.c
index 9c92e79..c8c303e 100644
--- a/apps/cms.c
+++ b/apps/cms.c
@@ -83,6 +83,7 @@
     OPT_RR_TO, OPT_AES128_WRAP, OPT_AES192_WRAP, OPT_AES256_WRAP,
     OPT_3DES_WRAP, OPT_WRAP, OPT_ENGINE,
     OPT_R_ENUM,
+    OPT_PROV_ENUM,
     OPT_V_ENUM,
     OPT_CIPHER,
     OPT_ORIGINATOR
@@ -220,6 +221,7 @@
 
     OPT_R_OPTIONS,
     OPT_V_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"cert", 0, 0, "Recipient certs (optional; used only when encrypting)"},
@@ -621,6 +623,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_3DES_WRAP:
 # ifndef OPENSSL_NO_DES
             wrap_cipher = EVP_des_ede3_wrap();
diff --git a/apps/crl.c b/apps/crl.c
index 14410f3..5e0a517 100644
--- a/apps/crl.c
+++ b/apps/crl.c
@@ -24,7 +24,7 @@
     OPT_ISSUER, OPT_LASTUPDATE, OPT_NEXTUPDATE, OPT_FINGERPRINT,
     OPT_CRLNUMBER, OPT_BADSIG, OPT_GENDELTA, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
     OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_VERIFY, OPT_TEXT, OPT_HASH,
-    OPT_HASH_OLD, OPT_NOOUT, OPT_NAMEOPT, OPT_MD
+    OPT_HASH_OLD, OPT_NOOUT, OPT_NAMEOPT, OPT_MD, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS crl_options[] = {
@@ -69,6 +69,7 @@
      "Do not load certificates from the default certificates directory"},
     {"no-CAstore", OPT_NOCASTORE, '-',
      "Do not load certificates from the default certificates store"},
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -193,6 +194,11 @@
         case OPT_MD:
             if (!opt_md(opt_unknown(), &digest))
                 goto opthelp;
+            break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/crl2p7.c b/apps/crl2p7.c
index ffbee4c..53bc88f 100644
--- a/apps/crl2p7.c
+++ b/apps/crl2p7.c
@@ -23,7 +23,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_NOCRL, OPT_CERTFILE
+    OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_NOCRL, OPT_CERTFILE,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS crl2pkcs7_options[] = {
@@ -40,6 +41,8 @@
     OPT_SECTION("Output"),
     {"out", OPT_OUT, '>', "Output file"},
     {"outform", OPT_OUTFORM, 'F', "Output format - DER or PEM"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -93,6 +96,10 @@
             if (!sk_OPENSSL_STRING_push(certflst, opt_arg()))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/dgst.c b/apps/dgst.c
index 7a81cb2..dcb4c09 100644
--- a/apps/dgst.c
+++ b/apps/dgst.c
@@ -42,7 +42,7 @@
     OPT_HEX, OPT_BINARY, OPT_DEBUG, OPT_FIPS_FINGERPRINT,
     OPT_HMAC, OPT_MAC, OPT_SIGOPT, OPT_MACOPT,
     OPT_DIGEST,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS dgst_options[] = {
@@ -82,6 +82,7 @@
      "Compute HMAC with the key used in OpenSSL-FIPS fingerprint"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"file", 0, 0, "Files to digest (optional; default is stdin)"},
@@ -208,6 +209,10 @@
                 goto opthelp;
             md = m;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/dhparam.c b/apps/dhparam.c
index acff366..3f694c5 100644
--- a/apps/dhparam.c
+++ b/apps/dhparam.c
@@ -41,7 +41,7 @@
     OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT,
     OPT_ENGINE, OPT_CHECK, OPT_TEXT, OPT_NOOUT,
     OPT_DSAPARAM, OPT_C, OPT_2, OPT_3, OPT_5,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS dhparam_options[] = {
@@ -73,6 +73,7 @@
     {"5", OPT_5, '-', "Generate parameters using 5 as the generator value"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"numbits", 0, 0, "Number of bits if generating parameters (optional)"},
@@ -151,6 +152,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/dsa.c b/apps/dsa.c
index 9704a34..082aa96 100644
--- a/apps/dsa.c
+++ b/apps/dsa.c
@@ -35,7 +35,8 @@
     /* Do not change the order here; see case statements below */
     OPT_PVK_NONE, OPT_PVK_WEAK, OPT_PVK_STRONG,
     OPT_NOOUT, OPT_TEXT, OPT_MODULUS, OPT_PUBIN,
-    OPT_PUBOUT, OPT_CIPHER, OPT_PASSIN, OPT_PASSOUT
+    OPT_PUBOUT, OPT_CIPHER, OPT_PASSIN, OPT_PASSOUT,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS dsa_options[] = {
@@ -66,6 +67,7 @@
     {"pubout", OPT_PUBOUT, '-', "Output public key, not private"},
     {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
 
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -147,6 +149,10 @@
             if (!opt_cipher(opt_unknown(), &enc))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/dsaparam.c b/apps/dsaparam.c
index eebffa0..04e09e7 100644
--- a/apps/dsaparam.c
+++ b/apps/dsaparam.c
@@ -36,7 +36,7 @@
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C,
     OPT_NOOUT, OPT_GENKEY, OPT_ENGINE, OPT_VERBOSE,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS dsaparam_options[] = {
@@ -62,6 +62,7 @@
     {"genkey", OPT_GENKEY, '-', "Generate a DSA key"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"numbits", 0, 0, "Number of bits if generating parameters (optional)"},
@@ -122,6 +123,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_NOOUT:
             noout = 1;
             break;
diff --git a/apps/ec.c b/apps/ec.c
index 3d901bb..6a38799 100644
--- a/apps/ec.c
+++ b/apps/ec.c
@@ -40,7 +40,7 @@
     OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT,
     OPT_NOOUT, OPT_TEXT, OPT_PARAM_OUT, OPT_PUBIN, OPT_PUBOUT,
     OPT_PASSIN, OPT_PASSOUT, OPT_PARAM_ENC, OPT_CONV_FORM, OPT_CIPHER,
-    OPT_NO_PUBLIC, OPT_CHECK
+    OPT_NO_PUBLIC, OPT_CHECK, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS ec_options[] = {
@@ -70,6 +70,8 @@
     {"pubout", OPT_PUBOUT, '-', "Output public key, not private"},
     {"no_public", OPT_NO_PUBLIC, '-', "exclude public key from private key"},
     {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -161,6 +163,10 @@
         case OPT_CHECK:
             check = 1;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/ecparam.c b/apps/ecparam.c
index 307eb1f..ed4f4f1 100644
--- a/apps/ecparam.c
+++ b/apps/ecparam.c
@@ -31,7 +31,7 @@
     OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_TEXT, OPT_C,
     OPT_CHECK, OPT_LIST_CURVES, OPT_NO_SEED, OPT_NOOUT, OPT_NAME,
     OPT_CONV_FORM, OPT_PARAM_ENC, OPT_GENKEY, OPT_ENGINE, OPT_CHECK_NAMED,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS ecparam_options[] = {
@@ -67,6 +67,7 @@
     {"conv_form", OPT_CONV_FORM, 's', "Specifies the point conversion form "},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -169,6 +170,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
diff --git a/apps/enc.c b/apps/enc.c
index 2b23564..74fd6f8 100644
--- a/apps/enc.c
+++ b/apps/enc.c
@@ -45,7 +45,7 @@
     OPT_NOPAD, OPT_SALT, OPT_NOSALT, OPT_DEBUG, OPT_UPPER_P, OPT_UPPER_A,
     OPT_A, OPT_Z, OPT_BUFSIZE, OPT_K, OPT_KFILE, OPT_UPPER_K, OPT_NONE,
     OPT_UPPER_S, OPT_IV, OPT_MD, OPT_ITER, OPT_PBKDF2, OPT_CIPHER,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS enc_options[] = {
@@ -97,6 +97,7 @@
     {"", OPT_CIPHER, '-', "Any supported cipher"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -288,6 +289,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     if (opt_num_rest() != 0) {
diff --git a/apps/gendsa.c b/apps/gendsa.c
index a64ac16..cf24416 100644
--- a/apps/gendsa.c
+++ b/apps/gendsa.c
@@ -31,7 +31,7 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_OUT, OPT_PASSOUT, OPT_ENGINE, OPT_CIPHER, OPT_VERBOSE,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS gendsa_options[] = {
@@ -47,6 +47,7 @@
     {"out", OPT_OUT, '>', "Output the key to the specified file"},
     {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {"", OPT_CIPHER, '-', "Encrypt the output with any supported cipher"},
     {"verbose", OPT_VERBOSE, '-', "Verbose output"},
 
@@ -92,6 +93,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_CIPHER:
             if (!opt_cipher(opt_unknown(), &enc))
                 goto end;
diff --git a/apps/genpkey.c b/apps/genpkey.c
index 3ae508c..ef2979d 100644
--- a/apps/genpkey.c
+++ b/apps/genpkey.c
@@ -24,7 +24,8 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_ENGINE, OPT_OUTFORM, OPT_OUT, OPT_PASS, OPT_PARAMFILE,
-    OPT_ALGORITHM, OPT_PKEYOPT, OPT_GENPARAM, OPT_TEXT, OPT_CIPHER
+    OPT_ALGORITHM, OPT_PKEYOPT, OPT_GENPARAM, OPT_TEXT, OPT_CIPHER,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS genpkey_options[] = {
@@ -46,6 +47,8 @@
     {"text", OPT_TEXT, '-', "Print the in text"},
     {"", OPT_CIPHER, '-', "Cipher to use to encrypt the key"},
 
+    OPT_PROV_OPTIONS,
+
     /* This is deliberately last. */
     {OPT_HELP_STR, 1, 1,
      "Order of options may be important!  See the documentation.\n"},
@@ -131,6 +134,11 @@
                 BIO_printf(bio_err, "%s: cipher mode not supported\n", prog);
                 goto end;
             }
+            break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/genrsa.c b/apps/genrsa.c
index c45fe19..3f76d9b 100644
--- a/apps/genrsa.c
+++ b/apps/genrsa.c
@@ -41,7 +41,7 @@
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_3, OPT_F4, OPT_ENGINE,
     OPT_OUT, OPT_PASSOUT, OPT_CIPHER, OPT_PRIMES, OPT_VERBOSE,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS genrsa_options[] = {
@@ -66,6 +66,7 @@
     {"", OPT_CIPHER, '-', "Encrypt the output with any supported cipher"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"numbits", 0, 0, "Size of key in bits"},
@@ -121,6 +122,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_PASSOUT:
             passoutarg = opt_arg();
             break;
diff --git a/apps/include/opt.h b/apps/include/opt.h
index ac2c236..b4257cc 100644
--- a/apps/include/opt.h
+++ b/apps/include/opt.h
@@ -266,6 +266,24 @@
         case OPT_R_RAND: case OPT_R_WRITERAND
 
 /*
+ * Provider options.
+ */
+# define OPT_PROV_ENUM \
+        OPT_PROV__FIRST=1600, \
+        OPT_PROV_PROVIDER, OPT_PROV_PROVIDER_PATH, \
+        OPT_PROV__LAST
+
+# define OPT_PROV_OPTIONS \
+        OPT_SECTION("Provider"), \
+        { "provider", OPT_PROV_PROVIDER, 's', "Provder to load (can be specified multiple times)" }, \
+        { "provider_path", OPT_PROV_PROVIDER_PATH, 's', "Provider load path" }
+
+# define OPT_PROV_CASES \
+        OPT_PROV__FIRST: case OPT_PROV__LAST: break; \
+        case OPT_PROV_PROVIDER: \
+        case OPT_PROV_PROVIDER_PATH
+
+/*
  * Option parsing.
  */
 extern const char OPT_HELP_STR[];
@@ -348,6 +366,7 @@
 int opt_num_rest(void);
 int opt_verify(int i, X509_VERIFY_PARAM *vpm);
 int opt_rand(int i);
+int opt_provider(int i);
 void opt_help(const OPTIONS * list);
 void opt_print(const OPTIONS * opt, int doingparams, int width);
 int opt_format_error(const char *s, unsigned long flags);
diff --git a/apps/kdf.c b/apps/kdf.c
index 82818f1..0162ac8 100644
--- a/apps/kdf.c
+++ b/apps/kdf.c
@@ -19,7 +19,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_KDFOPT, OPT_BIN, OPT_KEYLEN, OPT_OUT
+    OPT_KDFOPT, OPT_BIN, OPT_KEYLEN, OPT_OUT,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS kdf_options[] = {
@@ -36,6 +37,8 @@
     {"binary", OPT_BIN, '-',
         "Output in binary format (default is hexadecimal)"},
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"kdf_name", 0, 0, "Name of the KDF algorithm"},
     {NULL}
@@ -80,6 +83,10 @@
             if (opts == NULL || !sk_OPENSSL_STRING_push(opts, opt_arg()))
                 goto opthelp;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto err;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/lib/app_provider.c b/apps/lib/app_provider.c
new file mode 100644
index 0000000..ac01e88
--- /dev/null
+++ b/apps/lib/app_provider.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "apps.h"
+#include <openssl/err.h>
+#include <openssl/provider.h>
+
+/*
+ * See comments in opt_verify for explanation of this.
+ */
+enum prov_range { OPT_PROV_ENUM };
+
+static int opt_provider_load(const char *provider)
+{
+    OSSL_PROVIDER *prov;
+
+    prov = OSSL_PROVIDER_load(NULL, provider);
+    if (prov == NULL) {
+        opt_printf_stderr("%s: unable to load provider %s\n",
+                          opt_getprog(), provider);
+        return 0;
+    }
+    return 1;
+}
+
+static int opt_provider_path(const char *path)
+{
+    if (path != NULL && *path == '\0')
+        path = NULL;
+    return OSSL_PROVIDER_set_default_search_path(NULL, path);
+}
+
+int opt_provider(int opt)
+{
+    switch ((enum prov_range)opt) {
+    case OPT_PROV__FIRST:
+    case OPT_PROV__LAST:
+        return 1;
+    case OPT_PROV_PROVIDER:
+        return opt_provider_load(opt_arg());
+    case OPT_PROV_PROVIDER_PATH:
+        return opt_provider_path(opt_arg());
+    }
+    return 0;
+}
diff --git a/apps/lib/build.info b/apps/lib/build.info
index 3f68a2e..a7be58b 100644
--- a/apps/lib/build.info
+++ b/apps/lib/build.info
@@ -9,7 +9,7 @@
 
 # Source for libapps
 $LIBAPPSSRC=apps.c apps_ui.c opt.c fmt.c s_cb.c s_socket.c app_rand.c \
-        columns.c app_params.c names.c
+        columns.c app_params.c names.c app_provider.c
 
 IF[{- !$disabled{apps} -}]
   LIBS{noinst}=../libapps.a
diff --git a/apps/list.c b/apps/list.c
index e8bb853..28fd5c8 100644
--- a/apps/list.c
+++ b/apps/list.c
@@ -617,7 +617,8 @@
     OPT_COMMANDS, OPT_DIGEST_COMMANDS, OPT_MAC_ALGORITHMS, OPT_OPTIONS,
     OPT_DIGEST_ALGORITHMS, OPT_CIPHER_COMMANDS, OPT_CIPHER_ALGORITHMS,
     OPT_PK_ALGORITHMS, OPT_PK_METHOD, OPT_ENGINES, OPT_DISABLED,
-    OPT_KDF_ALGORITHMS, OPT_MISSING_HELP, OPT_OBJECTS
+    OPT_KDF_ALGORITHMS, OPT_MISSING_HELP, OPT_OBJECTS,
+    OPT_PROV_ENUM
 } HELPLIST_CHOICE;
 
 const OPTIONS list_options[] = {
@@ -655,6 +656,8 @@
      "List options for specified command"},
     {"objects", OPT_OBJECTS, '-',
      "List built in objects (OID<->name mappings)"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -741,6 +744,10 @@
         case OPT_VERBOSE:
             verbose = 1;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                return 1;
+            break;
         }
         done = 1;
     }
diff --git a/apps/mac.c b/apps/mac.c
index 6abcfcd..bc236a7 100644
--- a/apps/mac.c
+++ b/apps/mac.c
@@ -21,7 +21,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_MACOPT, OPT_BIN, OPT_IN, OPT_OUT
+    OPT_MACOPT, OPT_BIN, OPT_IN, OPT_OUT,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS mac_options[] = {
@@ -40,6 +41,8 @@
     {"binary", OPT_BIN, '-',
         "Output in binary format (default is hexadecimal)"},
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"mac_name", 0, 0, "MAC algorithm"},
     {NULL}
@@ -89,6 +92,10 @@
             if (opts == NULL || !sk_OPENSSL_STRING_push(opts, opt_arg()))
                 goto opthelp;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto err;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/nseq.c b/apps/nseq.c
index 9d1e095..e9aeea9 100644
--- a/apps/nseq.c
+++ b/apps/nseq.c
@@ -16,7 +16,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_TOSEQ, OPT_IN, OPT_OUT
+    OPT_TOSEQ, OPT_IN, OPT_OUT,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS nseq_options[] = {
@@ -29,6 +30,8 @@
     OPT_SECTION("Output"),
     {"toseq", OPT_TOSEQ, '-', "Output NS Sequence file"},
     {"out", OPT_OUT, '>', "Output file"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -62,6 +65,10 @@
         case OPT_OUT:
             outfile = opt_arg();
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/ocsp.c b/apps/ocsp.c
index 3297b42..3c6b8cb 100644
--- a/apps/ocsp.c
+++ b/apps/ocsp.c
@@ -137,7 +137,7 @@
     OPT_RCID,
     OPT_V_ENUM,
     OPT_MD,
-    OPT_MULTI
+    OPT_MULTI, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS ocsp_options[] = {
@@ -230,6 +230,7 @@
     {"status_age", OPT_STATUS_AGE, 'p', "Maximum status age in seconds"},
 
     OPT_V_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -541,6 +542,10 @@
             multi = atoi(opt_arg());
 # endif
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     if (trailing_md) {
diff --git a/apps/passwd.c b/apps/passwd.c
index 4626eeb..f3faa58 100644
--- a/apps/passwd.c
+++ b/apps/passwd.c
@@ -58,7 +58,7 @@
     OPT_IN,
     OPT_NOVERIFY, OPT_QUIET, OPT_TABLE, OPT_REVERSE, OPT_APR1,
     OPT_1, OPT_5, OPT_6, OPT_CRYPT, OPT_AIXMD5, OPT_SALT, OPT_STDIN,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS passwd_options[] = {
@@ -90,6 +90,7 @@
 #endif
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"password", 0, 0, "Password text to digest (optional)"},
@@ -191,6 +192,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index 091318b..c2448a7 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -58,7 +58,7 @@
     OPT_INKEY, OPT_CERTFILE, OPT_NAME, OPT_CSP, OPT_CANAME,
     OPT_IN, OPT_OUT, OPT_PASSIN, OPT_PASSOUT, OPT_PASSWORD, OPT_CAPATH,
     OPT_CAFILE, OPT_CASTORE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_ENGINE,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS pkcs12_options[] = {
@@ -130,6 +130,7 @@
     {"", OPT_CIPHER, '-', "Any supported cipher"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -307,6 +308,10 @@
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/pkcs7.c b/apps/pkcs7.c
index bd1cb10..9c74d99 100644
--- a/apps/pkcs7.c
+++ b/apps/pkcs7.c
@@ -23,7 +23,8 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, OPT_NOOUT,
-    OPT_TEXT, OPT_PRINT, OPT_PRINT_CERTS, OPT_ENGINE
+    OPT_TEXT, OPT_PRINT, OPT_PRINT_CERTS, OPT_ENGINE,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS pkcs7_options[] = {
@@ -45,6 +46,8 @@
     {"print", OPT_PRINT, '-', "Print out all fields of the PKCS7 structure"},
     {"print_certs", OPT_PRINT_CERTS, '-',
      "Print_certs  print any certs or crl in the input"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -99,6 +102,10 @@
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/pkcs8.c b/apps/pkcs8.c
index 1ccaff0..b058ea1 100644
--- a/apps/pkcs8.c
+++ b/apps/pkcs8.c
@@ -26,7 +26,7 @@
 #endif
     OPT_V2, OPT_V1, OPT_V2PRF, OPT_ITER, OPT_PASSIN, OPT_PASSOUT,
     OPT_TRADITIONAL,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS pkcs8_options[] = {
@@ -63,6 +63,7 @@
 #endif
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -127,6 +128,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_TRADITIONAL:
             traditional = 1;
             break;
diff --git a/apps/pkey.c b/apps/pkey.c
index 54709f6..e90b3a5 100644
--- a/apps/pkey.c
+++ b/apps/pkey.c
@@ -37,7 +37,8 @@
     OPT_INFORM, OPT_OUTFORM, OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE,
     OPT_IN, OPT_OUT, OPT_PUBIN, OPT_PUBOUT, OPT_TEXT_PUB,
     OPT_TEXT, OPT_NOOUT, OPT_MD, OPT_TRADITIONAL, OPT_CHECK, OPT_PUB_CHECK,
-    OPT_EC_PARAM_ENC, OPT_EC_CONV_FORM
+    OPT_EC_PARAM_ENC, OPT_EC_CONV_FORM,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS pkey_options[] = {
@@ -72,6 +73,7 @@
     {"text", OPT_TEXT, '-', "Output in plaintext as well"},
     {"noout", OPT_NOOUT, '-', "Don't output the key"},
 
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -177,6 +179,10 @@
             ec_asn1_flag = i;
             break;
 #endif
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/pkeyparam.c b/apps/pkeyparam.c
index 5521909..441523d 100644
--- a/apps/pkeyparam.c
+++ b/apps/pkeyparam.c
@@ -19,7 +19,8 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_IN, OPT_OUT, OPT_TEXT, OPT_NOOUT,
-    OPT_ENGINE, OPT_CHECK
+    OPT_ENGINE, OPT_CHECK,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS pkeyparam_options[] = {
@@ -37,6 +38,8 @@
     {"out", OPT_OUT, '>', "Output file"},
     {"text", OPT_TEXT, '-', "Print parameters as text"},
     {"noout", OPT_NOOUT, '-', "Don't output encoded parameters"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -81,6 +84,10 @@
         case OPT_CHECK:
             check = 1;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c
index 5bc4365..7f11b16 100644
--- a/apps/pkeyutl.c
+++ b/apps/pkeyutl.c
@@ -44,7 +44,7 @@
     OPT_VERIFY, OPT_VERIFYRECOVER, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT,
     OPT_DERIVE, OPT_SIGFILE, OPT_INKEY, OPT_PEERKEY, OPT_PASSIN,
     OPT_PEERFORM, OPT_KEYFORM, OPT_PKEYOPT, OPT_PKEYOPT_PASSIN, OPT_KDF,
-    OPT_KDFLEN, OPT_R_ENUM,
+    OPT_KDFLEN, OPT_R_ENUM, OPT_PROV_ENUM,
     OPT_RAWIN, OPT_DIGEST
 } OPTION_CHOICE;
 
@@ -92,6 +92,7 @@
     {"kdflen", OPT_KDFLEN, 'p', "KDF algorithm output length"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -165,6 +166,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
diff --git a/apps/prime.c b/apps/prime.c
index 5345e5b..ff25f90 100644
--- a/apps/prime.c
+++ b/apps/prime.c
@@ -15,7 +15,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_HEX, OPT_GENERATE, OPT_BITS, OPT_SAFE, OPT_CHECKS
+    OPT_HEX, OPT_GENERATE, OPT_BITS, OPT_SAFE, OPT_CHECKS,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS prime_options[] = {
@@ -32,6 +33,8 @@
     {"safe", OPT_SAFE, '-',
      "When used with -generate, generate a safe prime"},
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"number", 0, 0, "Number(s) to check for primality if not generating"},
     {NULL}
@@ -72,6 +75,10 @@
             /* ignore parameter and argument */
             opt_arg();
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/rand.c b/apps/rand.c
index 421ebfc..7dc14cb 100644
--- a/apps/rand.c
+++ b/apps/rand.c
@@ -21,7 +21,7 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_OUT, OPT_ENGINE, OPT_BASE64, OPT_HEX,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS rand_options[] = {
@@ -39,6 +39,7 @@
     {"hex", OPT_HEX, '-', "Hex encode output"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"num", 0, 0, "Number of bytes to generate"},
@@ -81,6 +82,10 @@
         case OPT_HEX:
             format = FORMAT_TEXT;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/rehash.c b/apps/rehash.c
index b7e6e7e..de54064 100644
--- a/apps/rehash.c
+++ b/apps/rehash.c
@@ -447,7 +447,8 @@
 
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
-    OPT_COMPAT, OPT_OLD, OPT_N, OPT_VERBOSE
+    OPT_COMPAT, OPT_OLD, OPT_N, OPT_VERBOSE,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS rehash_options[] = {
@@ -463,6 +464,8 @@
     OPT_SECTION("Output"),
     {"v", OPT_VERBOSE, '-', "Verbose output"},
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"directory", 0, 0, "One or more directories to process (optional)"},
     {NULL}
@@ -499,6 +502,10 @@
         case OPT_VERBOSE:
             verbose = 1;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/req.c b/apps/req.c
index 7140705..dcad9dc 100644
--- a/apps/req.c
+++ b/apps/req.c
@@ -91,7 +91,7 @@
     OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509,
     OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS,
     OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_SM2ID, OPT_SM2HEXID,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS req_options[] = {
@@ -160,6 +160,7 @@
     {"modulus", OPT_MODULUS, '-', "RSA modulus"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -331,6 +332,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_NEWKEY:
             keyalg = opt_arg();
             newreq = 1;
diff --git a/apps/rsa.c b/apps/rsa.c
index 7d03a86..d626bbb 100644
--- a/apps/rsa.c
+++ b/apps/rsa.c
@@ -36,7 +36,8 @@
     OPT_RSAPUBKEY_IN, OPT_RSAPUBKEY_OUT,
     /* Do not change the order here; see case statements below */
     OPT_PVK_NONE, OPT_PVK_WEAK, OPT_PVK_STRONG,
-    OPT_NOOUT, OPT_TEXT, OPT_MODULUS, OPT_CHECK, OPT_CIPHER
+    OPT_NOOUT, OPT_TEXT, OPT_MODULUS, OPT_CHECK, OPT_CIPHER,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS rsa_options[] = {
@@ -71,6 +72,8 @@
     {"pvk-weak", OPT_PVK_WEAK, '-', "Enable 'Weak' PVK encoding level"},
     {"pvk-none", OPT_PVK_NONE, '-', "Don't enforce PVK encoding"},
 # endif
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -160,6 +163,10 @@
             if (!opt_cipher(opt_unknown(), &enc))
                 goto opthelp;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/rsautl.c b/apps/rsautl.c
index 7bb9096..b72f527 100644
--- a/apps/rsautl.c
+++ b/apps/rsautl.c
@@ -37,7 +37,7 @@
     OPT_RSA_RAW, OPT_OAEP, OPT_SSL, OPT_PKCS, OPT_X931,
     OPT_SIGN, OPT_VERIFY, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT,
     OPT_PUBIN, OPT_CERTIN, OPT_INKEY, OPT_PASSIN, OPT_KEYFORM,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS rsautl_options[] = {
@@ -72,6 +72,7 @@
     {"hexdump", OPT_HEXDUMP, '-', "Hex dump output"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -169,6 +170,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/s_client.c b/apps/s_client.c
index 7803785..c06f2c8 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -600,7 +600,7 @@
     OPT_DANE_TLSA_RRDATA, OPT_DANE_EE_NO_NAME,
     OPT_ENABLE_PHA,
     OPT_SCTP_LABEL_BUG,
-    OPT_R_ENUM
+    OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS s_client_options[] = {
@@ -817,6 +817,7 @@
     {"chainCAfile", OPT_CHAINCAFILE, '<',
      "CA file for certificate chain (PEM format)"},
     OPT_X_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"host:port", 0, 0, "Where to connect; same as -connect option"},
@@ -1225,6 +1226,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_IGN_EOF:
             c_ign_eof = 1;
             break;
diff --git a/apps/s_server.c b/apps/s_server.c
index 69d9e04..4016270 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -757,7 +757,8 @@
     OPT_R_ENUM,
     OPT_S_ENUM,
     OPT_V_ENUM,
-    OPT_X_ENUM
+    OPT_X_ENUM,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS s_server_options[] = {
@@ -992,6 +993,7 @@
     {"chainCAfile", OPT_CHAINCAFILE, '<',
      "CA file for certificate chain (PEM format)"},
     OPT_X_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -1566,6 +1568,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_SERVERNAME:
             tlsextcbp.servername = opt_arg();
             break;
diff --git a/apps/s_time.c b/apps/s_time.c
index babbdbe..28e82f7 100644
--- a/apps/s_time.c
+++ b/apps/s_time.c
@@ -48,7 +48,8 @@
     OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
     OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
     OPT_NEW, OPT_REUSE, OPT_BUGS, OPT_VERIFY, OPT_TIME, OPT_SSL3,
-    OPT_WWW, OPT_TLS1, OPT_TLS1_1, OPT_TLS1_2, OPT_TLS1_3
+    OPT_WWW, OPT_TLS1, OPT_TLS1_1, OPT_TLS1_2, OPT_TLS1_3,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS s_time_options[] = {
@@ -99,6 +100,7 @@
     {"no-CAstore", OPT_NOCASTORE, '-',
      "Do not load certificates from the default certificates store URI"},
 
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -226,6 +228,10 @@
             min_version = TLS1_3_VERSION;
             max_version = TLS1_3_VERSION;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/smime.c b/apps/smime.c
index 149d315..c7f74d4 100644
--- a/apps/smime.c
+++ b/apps/smime.c
@@ -42,7 +42,7 @@
     OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP, OPT_MD,
     OPT_CIPHER, OPT_INKEY, OPT_KEYFORM, OPT_CERTFILE, OPT_CAFILE,
     OPT_CAPATH, OPT_CASTORE, OPT_NOCAFILE, OPT_NOCAPATH, OPT_NOCASTORE,
-    OPT_R_ENUM,
+    OPT_R_ENUM, OPT_PROV_ENUM,
     OPT_V_ENUM,
     OPT_IN, OPT_INFORM, OPT_OUT,
     OPT_OUTFORM, OPT_CONTENT
@@ -121,6 +121,7 @@
 
     OPT_R_OPTIONS,
     OPT_V_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"cert", 0, 0, "Recipient certs, used when encrypting"},
@@ -244,6 +245,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
diff --git a/apps/speed.c b/apps/speed.c
index 13285d6..9d4ab2c 100644
--- a/apps/speed.c
+++ b/apps/speed.c
@@ -253,7 +253,7 @@
 typedef enum OPTION_choice {
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_ELAPSED, OPT_EVP, OPT_HMAC, OPT_DECRYPT, OPT_ENGINE, OPT_MULTI,
-    OPT_MR, OPT_MB, OPT_MISALIGN, OPT_ASYNCJOBS, OPT_R_ENUM,
+    OPT_MR, OPT_MB, OPT_MISALIGN, OPT_ASYNCJOBS, OPT_R_ENUM, OPT_PROV_ENUM,
     OPT_PRIMES, OPT_SECONDS, OPT_BYTES, OPT_AEAD, OPT_CMAC
 } OPTION_CHOICE;
 
@@ -301,6 +301,7 @@
      "Use specified offset to mis-align buffers"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"algorithm", 0, 0, "Algorithm(s) to test (optional; otherwise tests all)"},
@@ -1706,6 +1707,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_PRIMES:
 #ifndef OPENSSL_NO_DEPRECATED_3_0
             if (!opt_int(opt_arg(), &primes))
diff --git a/apps/spkac.c b/apps/spkac.c
index 75e246a..dbd3d45 100644
--- a/apps/spkac.c
+++ b/apps/spkac.c
@@ -24,7 +24,8 @@
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_NOOUT, OPT_PUBKEY, OPT_VERIFY, OPT_IN, OPT_OUT,
     OPT_ENGINE, OPT_KEY, OPT_CHALLENGE, OPT_PASSIN, OPT_SPKAC,
-    OPT_SPKSECT, OPT_KEYFORM
+    OPT_SPKSECT, OPT_KEYFORM,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS spkac_options[] = {
@@ -49,6 +50,8 @@
     {"noout", OPT_NOOUT, '-', "Don't print SPKAC"},
     {"pubkey", OPT_PUBKEY, '-', "Output public key"},
     {"verify", OPT_VERIFY, '-', "Verify SPKAC signature"},
+
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -116,6 +119,10 @@
         case OPT_ENGINE:
             e = setup_engine(opt_arg(), 0);
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/srp.c b/apps/srp.c
index e7a18d6..646e86f 100644
--- a/apps/srp.c
+++ b/apps/srp.c
@@ -193,7 +193,7 @@
     OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
     OPT_VERBOSE, OPT_CONFIG, OPT_NAME, OPT_SRPVFILE, OPT_ADD,
     OPT_DELETE, OPT_MODIFY, OPT_LIST, OPT_GN, OPT_USERINFO,
-    OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE, OPT_R_ENUM
+    OPT_PASSIN, OPT_PASSOUT, OPT_ENGINE, OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS srp_options[] = {
@@ -222,6 +222,7 @@
     {"passout", OPT_PASSOUT, 's', "Output file pass phrase source"},
 
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
 
     OPT_PARAMETERS(),
     {"user", 0, 0, "Username(s) to process (optional)"},
@@ -297,6 +298,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/storeutl.c b/apps/storeutl.c
index 85fb1ed..c03eb08 100644
--- a/apps/storeutl.c
+++ b/apps/storeutl.c
@@ -27,7 +27,7 @@
     OPT_SEARCHFOR_CERTS, OPT_SEARCHFOR_KEYS, OPT_SEARCHFOR_CRLS,
     OPT_CRITERION_SUBJECT, OPT_CRITERION_ISSUER, OPT_CRITERION_SERIAL,
     OPT_CRITERION_FINGERPRINT, OPT_CRITERION_ALIAS,
-    OPT_MD
+    OPT_MD, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS storeutl_options[] = {
@@ -59,6 +59,8 @@
     {"text", OPT_TEXT, '-', "Print a text form of the objects"},
     {"noout", OPT_NOOUT, '-', "No PEM output, just status"},
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"uri", 0, 0, "URI of the store object"},
     {NULL}
@@ -250,6 +252,10 @@
         case OPT_MD:
             if (!opt_md(opt_unknown(), &digest))
                 goto opthelp;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/ts.c b/apps/ts.c
index c651c8e..3d380be 100644
--- a/apps/ts.c
+++ b/apps/ts.c
@@ -86,7 +86,7 @@
     OPT_IN, OPT_TOKEN_IN, OPT_OUT, OPT_TOKEN_OUT, OPT_TEXT,
     OPT_REPLY, OPT_QUERYFILE, OPT_PASSIN, OPT_INKEY, OPT_SIGNER,
     OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE, OPT_UNTRUSTED,
-    OPT_MD, OPT_V_ENUM, OPT_R_ENUM
+    OPT_MD, OPT_V_ENUM, OPT_R_ENUM, OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS ts_options[] = {
@@ -127,6 +127,7 @@
 
     OPT_R_OPTIONS,
     OPT_V_OPTIONS,
+    OPT_PROV_OPTIONS,
     {NULL}
 };
 
@@ -219,6 +220,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_TSPOLICY:
             policy = opt_arg();
             break;
diff --git a/apps/verify.c b/apps/verify.c
index 5c50421..82ca35e 100644
--- a/apps/verify.c
+++ b/apps/verify.c
@@ -31,7 +31,8 @@
     OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
     OPT_UNTRUSTED, OPT_TRUSTED, OPT_CRLFILE, OPT_CRL_DOWNLOAD, OPT_SHOW_CHAIN,
     OPT_V_ENUM, OPT_NAMEOPT,
-    OPT_VERBOSE, OPT_SM2ID, OPT_SM2HEXID
+    OPT_VERBOSE, OPT_SM2ID, OPT_SM2HEXID,
+    OPT_PROV_ENUM
 } OPTION_CHOICE;
 
 const OPTIONS verify_options[] = {
@@ -73,6 +74,8 @@
      "Specify a hex ID string to verify an SM2 certificate"},
 #endif
 
+    OPT_PROV_OPTIONS,
+
     OPT_PARAMETERS(),
     {"cert", 0, 0, "Certificate(s) to verify (optional; stdin used otherwise)"},
     {NULL}
@@ -209,6 +212,10 @@
                 goto end;
             }
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         }
     }
     argc = opt_num_rest();
diff --git a/apps/x509.c b/apps/x509.c
index a8ec118..3176cf5 100644
--- a/apps/x509.c
+++ b/apps/x509.c
@@ -61,7 +61,7 @@
     OPT_SUBJECT_HASH_OLD,
     OPT_ISSUER_HASH_OLD,
     OPT_BADSIG, OPT_MD, OPT_ENGINE, OPT_NOCERT, OPT_PRESERVE_DATES,
-    OPT_R_ENUM, OPT_EXT
+    OPT_R_ENUM, OPT_PROV_ENUM, OPT_EXT
 } OPTION_CHOICE;
 
 const OPTIONS x509_options[] = {
@@ -144,6 +144,7 @@
      "The CA key, must be PEM format; if not in CAfile"},
     {"extfile", OPT_EXTFILE, '<', "File with X509V3 extensions to add"},
     OPT_R_OPTIONS,
+    OPT_PROV_OPTIONS,
     {"CAform", OPT_CAFORM, 'F', "CA format - default PEM"},
     {"CAkeyform", OPT_CAKEYFORM, 'E', "CA key format - default PEM"},
     {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"},
@@ -270,6 +271,10 @@
             if (!opt_rand(o))
                 goto end;
             break;
+        case OPT_PROV_CASES:
+            if (!opt_provider(o))
+                goto end;
+            break;
         case OPT_EXTENSIONS:
             extsect = opt_arg();
             break;