Commit 1b0f21f055 for openssl.org

commit 1b0f21f0555cfada3e789c80100df40d1c429800
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date:   Mon Aug 11 11:55:06 2025 +0200

    Implementing store support for EVP_SKEY

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Simo Sorce <simo@redhat.com>
    (Merged from https://github.com/openssl/openssl/pull/28278)

diff --git a/apps/enc.c b/apps/enc.c
index 69ac4bc60c..fa8adc8970 100644
--- a/apps/enc.c
+++ b/apps/enc.c
@@ -75,7 +75,9 @@ typedef enum OPTION_choice {
     OPT_R_ENUM,
     OPT_PROV_ENUM,
     OPT_SKEYOPT,
-    OPT_SKEYMGMT
+    OPT_SKEYMGMT,
+    OPT_SKEYURI,
+    OPT_PASSIN
 } OPTION_CHOICE;

 const OPTIONS enc_options[] = {
@@ -130,6 +132,8 @@ const OPTIONS enc_options[] = {
 #endif
     { "skeyopt", OPT_SKEYOPT, 's', "Key options as opt:value for opaque symmetric key handling" },
     { "skeymgmt", OPT_SKEYMGMT, 's', "Symmetric key management name for opaque symmetric key handling" },
+    { "skeyuri", OPT_SKEYURI, 's', "Symmetric key object URI" },
+    { "storepass", OPT_PASSIN, 's', "Store pass phrase source when skeyuri is used (optional)" },
     { "", OPT_CIPHER, '-', "Any supported cipher" },

     OPT_R_OPTIONS,
@@ -137,6 +141,33 @@ const OPTIONS enc_options[] = {
     { NULL }
 };

+static EVP_SKEY *skey_from_params(const EVP_CIPHER *cipher, const char *skeymgmt,
+                                  STACK_OF(OPENSSL_STRING) *opts)
+{
+    EVP_SKEY *skey = NULL;
+    EVP_SKEYMGMT *mgmt = NULL;
+    OSSL_PARAM *params = NULL;
+
+    mgmt = EVP_SKEYMGMT_fetch(app_get0_libctx(),
+                              skeymgmt != NULL ? skeymgmt : EVP_CIPHER_name(cipher),
+                              app_get0_propq());
+    if (mgmt == NULL)
+        return NULL;
+
+    params = app_params_new_from_opts(opts, EVP_SKEYMGMT_get0_imp_settable_params(mgmt));
+    if (params == NULL) {
+        EVP_SKEYMGMT_free(mgmt);
+        return NULL;
+    }
+
+    skey = EVP_SKEY_import(app_get0_libctx(), EVP_SKEYMGMT_get0_name(mgmt),
+                           app_get0_propq(), OSSL_SKEYMGMT_SELECT_ALL, params);
+    OSSL_PARAM_free(params);
+    EVP_SKEYMGMT_free(mgmt);
+
+    return skey;
+}
+
 int enc_main(int argc, char **argv)
 {
     static char buf[128];
@@ -149,6 +180,7 @@ int enc_main(int argc, char **argv)
     char *hkey = NULL, *hiv = NULL, *hsalt = NULL, *p;
     char *infile = NULL, *outfile = NULL, *prog;
     char *str = NULL, *passarg = NULL, *pass = NULL, *strbuf = NULL;
+    char *storepassarg = NULL;
     const char *ciphername = NULL;
     char mbuf[sizeof(magic) - 1];
     OPTION_CHOICE o;
@@ -176,8 +208,8 @@ int enc_main(int argc, char **argv)
     BIO *bzstd = NULL;
     STACK_OF(OPENSSL_STRING) *skeyopts = NULL;
     const char *skeymgmt = NULL;
+    const char *skeyuri = NULL;
     EVP_SKEY *skey = NULL;
-    EVP_SKEYMGMT *mgmt = NULL;

     /* first check the command name */
     if (strcmp(argv[0], "base64") == 0)
@@ -231,6 +263,9 @@ int enc_main(int argc, char **argv)
         case OPT_PASS:
             passarg = opt_arg();
             break;
+        case OPT_PASSIN:
+            storepassarg = opt_arg();
+            break;
         case OPT_D:
             enc = 0;
             break;
@@ -346,6 +381,9 @@ int enc_main(int argc, char **argv)
         case OPT_SKEYMGMT:
             skeymgmt = opt_arg();
             break;
+        case OPT_SKEYURI:
+            skeyuri = opt_arg();
+            break;
         case OPT_R_CASES:
             if (!opt_rand(o))
                 goto end;
@@ -427,7 +465,8 @@ int enc_main(int argc, char **argv)
         str = pass;
     }

-    if ((str == NULL) && (cipher != NULL) && (hkey == NULL) && (skeyopts == NULL)) {
+    if ((str == NULL) && (cipher != NULL) && (hkey == NULL)
+        && (skeyopts == NULL) && (skeyuri == NULL)) {
         if (1) {
 #ifndef OPENSSL_NO_UI_CONSOLE
             for (;;) {
@@ -666,8 +705,8 @@ int enc_main(int argc, char **argv)
          * At this moment we know whether we trying to use raw bytes as the key
          * or an opaque symmetric key. We do not allow both options simultaneously.
          */
-        if (rawkey_set > 0 && skeyopts != NULL) {
-            BIO_printf(bio_err, "Either a raw key or the 'skeyopt' args must be used.\n");
+        if (rawkey_set > 0 && (skeyopts != NULL || skeyuri != NULL)) {
+            BIO_printf(bio_err, "Either a raw key or the skeyopt/skeyuri args must be used.\n");
             goto end;
         }

@@ -689,31 +728,35 @@ int enc_main(int argc, char **argv)
                     (hiv == NULL && wrap == 1 ? NULL : iv), enc)) {
                 BIO_printf(bio_err, "Error setting cipher %s\n",
                     EVP_CIPHER_get0_name(cipher));
-                ERR_print_errors(bio_err);
                 goto end;
             }
         } else {
-            OSSL_PARAM *params = NULL;
+            char *storepass = NULL;

+            if (!app_passwd(storepassarg, NULL, &storepass, NULL)) {
+                BIO_printf(bio_err,
+                    "Error getting store password from 'storepass' argument\n");
+            }
             mgmt = EVP_SKEYMGMT_fetch(app_get0_libctx(),
                 skeymgmt != NULL ? skeymgmt : EVP_CIPHER_name(cipher),
                 app_get0_propq());
             if (mgmt == NULL)
                 goto end;

-            params = app_params_new_from_opts(skeyopts,
-                EVP_SKEYMGMT_get0_imp_settable_params(mgmt));
-            if (params == NULL)
-                goto end;
-
-            skey = EVP_SKEY_import(app_get0_libctx(), EVP_SKEYMGMT_get0_name(mgmt),
-                app_get0_propq(), OSSL_SKEYMGMT_SELECT_ALL, params);
-            OSSL_PARAM_free(params);
-            if (skey == NULL) {
-                BIO_printf(bio_err, "Error creating opaque key object for skeymgmt %s\n",
-                    skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher));
-                ERR_print_errors(bio_err);
-                goto end;
+            if (skeyuri != NULL) {
+                skey = load_skey(skeyuri, FORMAT_UNDEF, 0, storepass, 0);
+                OPENSSL_free(storepass);
+                if (skey == NULL) {
+                    BIO_printf(bio_err, "Error loading opaque key object from URI %s\n", skeyuri);
+                    goto end;
+                }
+            } else {
+                skey = skey_from_params(cipher, skeymgmt, skeyopts);
+                if (skey == NULL) {
+                    BIO_printf(bio_err, "Error creating opaque key object for skeymgmt %s\n",
+                               skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher));
+                    goto end;
+                }
             }

             if (!EVP_CipherInit_SKEY(ctx, cipher, skey,
@@ -721,7 +764,6 @@ int enc_main(int argc, char **argv)
                     EVP_CIPHER_get_iv_length(cipher), enc, NULL)) {
                 BIO_printf(bio_err, "Error setting an opaque key for cipher %s\n",
                     EVP_CIPHER_get0_name(cipher));
-                ERR_print_errors(bio_err);
                 goto end;
             }
         }
@@ -795,7 +837,6 @@ int enc_main(int argc, char **argv)
 end:
     ERR_print_errors(bio_err);
     sk_OPENSSL_STRING_free(skeyopts);
-    EVP_SKEYMGMT_free(mgmt);
     EVP_SKEY_free(skey);
     OPENSSL_free(strbuf);
     OPENSSL_free(buff);
diff --git a/apps/include/apps.h b/apps/include/apps.h
index ce87477924..b4792a6f07 100644
--- a/apps/include/apps.h
+++ b/apps/include/apps.h
@@ -156,7 +156,10 @@ int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
     EVP_PKEY **ppkey, EVP_PKEY **ppubkey,
     EVP_PKEY **pparams,
     X509 **pcert, STACK_OF(X509) **pcerts,
-    X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls);
+    X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls.
+    EVP_SKEY **pskey);
+EVP_SKEY *load_skey(const char *uri, int format, int maybe_stdin,
+    const char *pass, int quiet);
 X509_STORE *setup_verify(const char *CAfile, int noCAfile,
     const char *CApath, int noCApath,
     const char *CAstore, int noCAstore);
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index 5f6dcae84e..2a36c4215a 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -442,7 +442,7 @@ X509 *load_cert_pass(const char *uri, int format, int maybe_stdin,
         }
     } else {
         (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 0,
-            NULL, NULL, NULL, &cert, NULL, NULL, NULL);
+            NULL, NULL, NULL, &cert, NULL, NULL, NULL, NULL);
     }
     return cert;
 }
@@ -464,7 +464,7 @@ X509_CRL *load_crl(const char *uri, int format, int maybe_stdin,
         }
     } else {
         (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc, 0,
-            NULL, NULL, NULL, NULL, NULL, &crl, NULL);
+            NULL, NULL, NULL, NULL, NULL, &crl, NULL, NULL);
     }
     return crl;
 }
@@ -555,7 +555,7 @@ EVP_PKEY *load_key(const char *uri, int format, int may_stdin,
         desc = "private key";

     (void)load_key_certs_crls(uri, format, may_stdin, pass, desc, 0,
-        &pkey, NULL, NULL, NULL, NULL, NULL, NULL);
+        &pkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

     return pkey;
 }
@@ -570,10 +570,10 @@ EVP_PKEY *load_pubkey(const char *uri, int format, int maybe_stdin,
         desc = "public key";

     (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 1,
-        NULL, &pkey, NULL, NULL, NULL, NULL, NULL);
+        NULL, &pkey, NULL, NULL, NULL, NULL, NULL, NULL);
     if (pkey == NULL)
         (void)load_key_certs_crls(uri, format, maybe_stdin, pass, desc, 0,
-            &pkey, NULL, NULL, NULL, NULL, NULL, NULL);
+            &pkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
     return pkey;
 }

@@ -587,7 +587,7 @@ EVP_PKEY *load_keyparams_suppress(const char *uri, int format, int maybe_stdin,
         desc = "key parameters";
     (void)load_key_certs_crls(uri, format, maybe_stdin, NULL, desc,
         suppress_decode_errors,
-        NULL, NULL, &params, NULL, NULL, NULL, NULL);
+        NULL, NULL, &params, NULL, NULL, NULL, NULL, NULL);
     if (params != NULL && keytype != NULL && !EVP_PKEY_is_a(params, keytype)) {
         ERR_print_errors(bio_err);
         BIO_printf(bio_err,
@@ -605,6 +605,17 @@ EVP_PKEY *load_keyparams(const char *uri, int format, int maybe_stdin,
     return load_keyparams_suppress(uri, format, maybe_stdin, keytype, desc, 0);
 }

+EVP_SKEY *load_skey(const char *uri, int format, int may_stdin,
+                    const char *pass, int quiet)
+{
+    EVP_SKEY *skey = NULL;
+
+    (void)load_key_certs_crls(uri, format, may_stdin, pass, NULL, 0,
+                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, &skey);
+
+    return skey;
+}
+
 void app_bail_out(char *fmt, ...)
 {
     va_list args;
@@ -704,7 +715,7 @@ int load_cert_certs(const char *uri,
     }
     pass_string = get_passwd(pass, desc);
     ret = load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass_string, desc, 0,
-        NULL, NULL, NULL, pcert, pcerts, NULL, NULL);
+        NULL, NULL, NULL, pcert, pcerts, NULL, NULL, NULL);
     clear_free(pass_string);

     if (ret) {
@@ -805,7 +816,7 @@ int load_certs(const char *uri, int maybe_stdin, STACK_OF(X509) **certs,
     if (desc == NULL)
         desc = "certificates";
     return load_key_certs_crls(uri, FORMAT_UNDEF, maybe_stdin, pass, desc, 0,
-        NULL, NULL, NULL, NULL, certs, NULL, NULL);
+        NULL, NULL, NULL, NULL, certs, NULL, NULL, NULL);
 }

 /*
@@ -818,7 +829,7 @@ int load_crls(const char *uri, STACK_OF(X509_CRL) **crls,
     if (desc == NULL)
         desc = "CRLs";
     return load_key_certs_crls(uri, FORMAT_UNDEF, 0, pass, desc, 0,
-        NULL, NULL, NULL, NULL, NULL, NULL, crls);
+        NULL, NULL, NULL, NULL, NULL, NULL, crls, NULL);
 }

 static const char *format2string(int format)
@@ -845,20 +856,21 @@ static const char *format2string(int format)
         SET_EXPECT(val);       \
     }
 /* Provide (error msg) text for some of the credential types to be loaded. */
-#define FAIL_NAME                                                       \
-    (ppkey != NULL ? "private key" : ppubkey != NULL ? "public key"     \
-            : pparams != NULL                        ? "key parameters" \
-            : pcert != NULL                          ? "certificate"    \
-            : pcerts != NULL                         ? "certificates"   \
-            : pcrl != NULL                           ? "CRL"            \
-            : pcrls != NULL                          ? "CRLs"           \
+#define FAIL_NAME                                                             \
+    (ppkey != NULL ? "private key" : ppubkey != NULL ? "public key"           \
+            : pparams != NULL                        ? "key parameters"       \
+            : pcert != NULL                          ? "certificate"          \
+            : pcerts != NULL                         ? "certificates"         \
+            : pcrl != NULL                           ? "CRL"                  \
+            : pcrls != NULL                          ? "CRLs"                 \
+            : pskey != NULL                          ? "symmetric key" : NULL \
                                                      : NULL)
 /*
  * Load those types of credentials for which the result pointer is not NULL.
  * Reads from stdin if 'uri' is NULL and 'maybe_stdin' is nonzero.
  * 'format' parameter may be FORMAT_PEM, FORMAT_ASN1, or 0 for no hint.
  * desc may contain more detail on the credential(s) to be loaded for error msg
- * For non-NULL ppkey, pcert, and pcrl the first suitable value found is loaded.
+ * For non-NULL ppkey, pcert, pcrl, and pskey the first suitable value found is loaded.
  * If pcerts is non-NULL and *pcerts == NULL then a new cert list is allocated.
  * If pcerts is non-NULL then all available certificates are appended to *pcerts
  * except any certificate assigned to *pcert.
@@ -867,18 +879,20 @@ static const char *format2string(int format)
  * except any CRL assigned to *pcrl.
  * On error, any contents of non-NULL credential pointers are freed.
  */
+
 int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
     const char *pass, const char *desc, int quiet,
     EVP_PKEY **ppkey, EVP_PKEY **ppubkey,
     EVP_PKEY **pparams,
     X509 **pcert, STACK_OF(X509) **pcerts,
-    X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls)
+    X509_CRL **pcrl, STACK_OF(X509_CRL) **pcrls,
+    EVP_SKEY **pskey)
 {
     PW_CB_DATA uidata;
     OSSL_STORE_CTX *ctx = NULL;
     OSSL_LIB_CTX *libctx = app_get0_libctx();
     const char *propq = app_get0_propq();
-    int ncerts = 0, ncrls = 0, expect = -1;
+    int ncerts = 0, ncrls = 0, nskeys = 0, expect = -1;
     const char *failed = FAIL_NAME;
     const char *input_type;
     OSSL_PARAM itp[2];
@@ -898,9 +912,10 @@ int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
     SET_EXPECT1(ppubkey, OSSL_STORE_INFO_PUBKEY);
     SET_EXPECT1(pparams, OSSL_STORE_INFO_PARAMS);
     SET_EXPECT1(pcert, OSSL_STORE_INFO_CERT);
+    SET_EXPECT1(pskey, OSSL_STORE_INFO_SKEY);
     /*
      * Up to here, the following holds.
-     * If just one of the ppkey, ppubkey, pparams, and pcert function parameters
+     * If just one of the ppkey, ppubkey, pparams, pcert, and pskey function parameters
      * is nonzero, expect > 0 indicates which type of credential is expected.
      * If expect == 0, more than one of them is nonzero (multiple types expected).
      */
@@ -969,6 +984,7 @@ int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
             BIO_printf(bio_err, "Could not open file or uri for loading");
         goto end;
     }
+
     /* expect == 0 means here multiple types of credentials are to be loaded */
     if (expect > 0 && !OSSL_STORE_expect(ctx, expect)) {
         if (!quiet)
@@ -980,7 +996,8 @@ int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
     /* from here, failed != NULL only if actually an error has been detected */

     while ((ppkey != NULL || ppubkey != NULL || pparams != NULL
-               || pcert != NULL || pcerts != NULL || pcrl != NULL || pcrls != NULL)
+               || pcert != NULL || pcerts != NULL || pcrl != NULL || pcrls != NULL
+               || pskey != NULL)
         && !OSSL_STORE_eof(ctx)) {
         OSSL_STORE_INFO *info = OSSL_STORE_load(ctx);
         int type, ok = 1;
@@ -1047,6 +1064,14 @@ int load_key_certs_crls(const char *uri, int format, int maybe_stdin,
             }
             ncrls += ok;
             break;
+        case OSSL_STORE_INFO_SKEY:
+            if (pskey != NULL) {
+                ok = (*pskey = OSSL_STORE_INFO_get1_SKEY(info)) != NULL;
+                if (ok)
+                    pskey = NULL;
+            }
+            nskeys += ok;
+            break;
         default:
             /* skip any other type; ok stays == 1 */
             break;
@@ -1069,6 +1094,8 @@ end:
             pcerts = NULL;
         if (ncrls > 0)
             pcrls = NULL;
+        if (nskeys > 0)
+            pskey = NULL;
         failed = FAIL_NAME;
         if (failed != NULL && !quiet)
             BIO_printf(bio_err, "Could not find");
diff --git a/apps/storeutl.c b/apps/storeutl.c
index ebd430d89a..387cf54522 100644
--- a/apps/storeutl.c
+++ b/apps/storeutl.c
@@ -33,6 +33,7 @@ typedef enum OPTION_choice {
     OPT_SEARCHFOR_CERTS,
     OPT_SEARCHFOR_KEYS,
     OPT_SEARCHFOR_CRLS,
+    OPT_SEARCHFOR_SKEYS,
     OPT_CRITERION_SUBJECT,
     OPT_CRITERION_ISSUER,
     OPT_CRITERION_SERIAL,
@@ -53,6 +54,7 @@ const OPTIONS storeutl_options[] = {
     { "certs", OPT_SEARCHFOR_CERTS, '-', "Search for certificates only" },
     { "keys", OPT_SEARCHFOR_KEYS, '-', "Search for keys only" },
     { "crls", OPT_SEARCHFOR_CRLS, '-', "Search for CRLs only" },
+    { "skeys", OPT_SEARCHFOR_SKEYS, '-', "Search for symmetric keys only" },
     { "subject", OPT_CRITERION_SUBJECT, 's', "Search by subject" },
     { "issuer", OPT_CRITERION_ISSUER, 's', "Search by issuer and serial, issuer name" },
     { "serial", OPT_CRITERION_SERIAL, 's', "Search by issuer and serial, serial number" },
@@ -124,6 +126,7 @@ int storeutl_main(int argc, char *argv[])
         case OPT_SEARCHFOR_CERTS:
         case OPT_SEARCHFOR_KEYS:
         case OPT_SEARCHFOR_CRLS:
+        case OPT_SEARCHFOR_SKEYS:
             if (expected != 0) {
                 BIO_printf(bio_err, "%s: only one search type can be given.\n",
                     prog);
@@ -137,6 +140,7 @@ int storeutl_main(int argc, char *argv[])
                     { OPT_SEARCHFOR_CERTS, OSSL_STORE_INFO_CERT },
                     { OPT_SEARCHFOR_KEYS, OSSL_STORE_INFO_PKEY },
                     { OPT_SEARCHFOR_CRLS, OSSL_STORE_INFO_CRL },
+                    { OPT_SEARCHFOR_SKEYS, OSSL_STORE_INFO_SKEY },
                 };
                 size_t i;

@@ -476,6 +480,9 @@ static int process(const char *uri, const UI_METHOD *uimeth, PW_CB_DATA *uidata,
             if (!noout)
                 PEM_write_bio_X509_CRL(out, OSSL_STORE_INFO_get0_CRL(info));
             break;
+        case OSSL_STORE_INFO_SKEY:
+            /* Currently there is no universal API allowing to print smth, so no output */
+            break;
         default:
             BIO_printf(bio_err, "!!! Unknown code\n");
             ret++;
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 8bd83215dd..70e06a6a38 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -898,6 +898,7 @@ OSSL_STORE_R_NOT_A_CRL:101:not a crl
 OSSL_STORE_R_NOT_A_NAME:103:not a name
 OSSL_STORE_R_NOT_A_PRIVATE_KEY:102:not a private key
 OSSL_STORE_R_NOT_A_PUBLIC_KEY:122:not a public key
+OSSL_STORE_R_NOT_A_SYMMETRIC_KEY:124:not a symmetric key
 OSSL_STORE_R_NOT_PARAMETERS:104:not parameters
 OSSL_STORE_R_NO_LOADERS_FOUND:123:no loaders found
 OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR:114:passphrase callback error
diff --git a/crypto/store/store_err.c b/crypto/store/store_err.c
index ee35151cc3..afde022997 100644
--- a/crypto/store/store_err.c
+++ b/crypto/store/store_err.c
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2025 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
@@ -38,6 +38,8 @@ static const ERR_STRING_DATA OSSL_STORE_str_reasons[] = {
         "not a private key" },
     { ERR_PACK(ERR_LIB_OSSL_STORE, 0, OSSL_STORE_R_NOT_A_PUBLIC_KEY),
         "not a public key" },
+    { ERR_PACK(ERR_LIB_OSSL_STORE, 0, OSSL_STORE_R_NOT_A_SYMMETRIC_KEY),
+     "not a symmetric key" },
     { ERR_PACK(ERR_LIB_OSSL_STORE, 0, OSSL_STORE_R_NOT_PARAMETERS),
         "not parameters" },
     { ERR_PACK(ERR_LIB_OSSL_STORE, 0, OSSL_STORE_R_NO_LOADERS_FOUND),
diff --git a/crypto/store/store_lib.c b/crypto/store/store_lib.c
index 7c11154138..ea0ab3423b 100644
--- a/crypto/store/store_lib.c
+++ b/crypto/store/store_lib.c
@@ -295,7 +295,7 @@ int OSSL_STORE_expect(OSSL_STORE_CTX *ctx, int expected_type)
     int ret = 1;

     if (ctx == NULL
-        || expected_type < 0 || expected_type > OSSL_STORE_INFO_CRL) {
+        || expected_type < 0 || expected_type > OSSL_STORE_INFO_SKEY) {
         ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_INVALID_ARGUMENT);
         return 0;
     }
@@ -690,6 +690,15 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_CRL(X509_CRL *crl)
     return info;
 }

+OSSL_STORE_INFO *OSSL_STORE_INFO_new_SKEY(EVP_SKEY *skey)
+{
+    OSSL_STORE_INFO *info = OSSL_STORE_INFO_new(OSSL_STORE_INFO_SKEY, skey);
+
+    if (info == NULL)
+        ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_OSSL_STORE_LIB);
+    return info;
+}
+
 /*
  * Functions to try to extract data from an OSSL_STORE_INFO.
  */
@@ -825,6 +834,24 @@ X509_CRL *OSSL_STORE_INFO_get1_CRL(const OSSL_STORE_INFO *info)
     return NULL;
 }

+EVP_SKEY *OSSL_STORE_INFO_get0_SKEY(const OSSL_STORE_INFO *info)
+{
+    if (info->type == OSSL_STORE_INFO_SKEY)
+        return info->_.skey;
+    return NULL;
+}
+
+EVP_SKEY *OSSL_STORE_INFO_get1_SKEY(const OSSL_STORE_INFO *info)
+{
+    if (info->type == OSSL_STORE_INFO_SKEY) {
+        if (!EVP_SKEY_up_ref(info->_.skey))
+            return NULL;
+        return info->_.skey;
+    }
+    ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_NOT_A_SYMMETRIC_KEY);
+    return NULL;
+}
+
 /*
  * Free the OSSL_STORE_INFO
  */
@@ -851,6 +878,9 @@ void OSSL_STORE_INFO_free(OSSL_STORE_INFO *info)
         case OSSL_STORE_INFO_CRL:
             X509_CRL_free(info->_.crl);
             break;
+        case OSSL_STORE_INFO_SKEY:
+            EVP_SKEY_free(info->_.skey);
+            break;
         }
         OPENSSL_free(info);
     }
diff --git a/crypto/store/store_local.h b/crypto/store/store_local.h
index a7ae850d1b..dd7c107a50 100644
--- a/crypto/store/store_local.h
+++ b/crypto/store/store_local.h
@@ -37,6 +37,7 @@ struct ossl_store_info_st {
         EVP_PKEY *pkey; /* when type == OSSL_STORE_INFO_PKEY */
         X509 *x509; /* when type == OSSL_STORE_INFO_CERT */
         X509_CRL *crl; /* when type == OSSL_STORE_INFO_CRL */
+        EVP_SKEY *skey; /* when type == OSSL_STORE_INFO_SKEY */
     } _;
 };
 DEFINE_STACK_OF(OSSL_STORE_INFO)
diff --git a/crypto/store/store_result.c b/crypto/store/store_result.c
index 849e4f33fb..1900b42dcc 100644
--- a/crypto/store/store_result.c
+++ b/crypto/store/store_result.c
@@ -83,6 +83,8 @@ static int try_crl(struct extracted_param_data_st *, OSSL_STORE_INFO **,
     OSSL_LIB_CTX *, const char *);
 static int try_pkcs12(struct extracted_param_data_st *, OSSL_STORE_INFO **,
     OSSL_STORE_CTX *, OSSL_LIB_CTX *, const char *);
+static int try_skey(struct extracted_param_data_st *, OSSL_STORE_INFO **,
+    const OSSL_PROVIDER *, OSSL_LIB_CTX *, const char *);

 int ossl_store_handle_load_result(const OSSL_PARAM params[], void *arg)
 {
@@ -150,6 +152,10 @@ int ossl_store_handle_load_result(const OSSL_PARAM params[], void *arg)
     if (*v == NULL && !try_pkcs12(&helper_data, v, ctx, libctx, propq))
         goto err;
     ERR_pop_to_mark();
+    ERR_set_mark();
+    if (*v == NULL && !try_skey(&helper_data, v, provider, libctx, propq))
+        goto err;
+    ERR_pop_to_mark();

     if (*v == NULL) {
         const char *hint = "";
@@ -665,3 +671,49 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,

     return ok;
 }
+
+static int try_skey(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
+                    const OSSL_PROVIDER *provider, OSSL_LIB_CTX *libctx, const char *propq)
+{
+    EVP_SKEY *skey = NULL;
+    const char *skeymgmt_name = data->data_type == NULL
+                                ? OSSL_SKEY_TYPE_GENERIC : data->data_type;
+    size_t keysize = 0;
+    unsigned char *keybytes = NULL;
+
+    if (data->object_type != OSSL_OBJECT_SKEY)
+        return 0;
+
+    if (data->octet_data != NULL) {
+        keysize  = data->octet_data_size;
+        keybytes = (unsigned char *)data->octet_data;
+        skey = EVP_SKEY_import_raw_key(libctx, skeymgmt_name,
+                                       keybytes, keysize, propq);
+    } else if (data->ref != NULL) {
+        EVP_SKEYMGMT *skeymgmt = evp_skeymgmt_fetch_from_prov((OSSL_PROVIDER *)provider,
+                                                              skeymgmt_name, propq);
+        OSSL_PARAM params[2];
+
+        /*
+         * We got an internal reference from a particular provider so we need the SKEYMGMT
+         * exactly from this provider
+         */
+        if (skeymgmt == NULL)
+            return 0;
+
+        keysize  = data->ref_size;
+        keybytes = (unsigned char *)data->ref;
+        params[0] = OSSL_PARAM_construct_octet_ptr(OSSL_OBJECT_PARAM_REFERENCE,
+                                                   (void **)&keybytes, keysize);
+        params[1] = OSSL_PARAM_construct_end();
+
+        skey = EVP_SKEY_import_SKEYMGMT(libctx, skeymgmt, OSSL_SKEYMGMT_SELECT_ALL, params);
+    }
+
+    if (skey != NULL)
+        *v = OSSL_STORE_INFO_new_SKEY(skey);
+    if (*v == NULL)
+        EVP_SKEY_free(skey);
+
+    return 1;
+}
diff --git a/crypto/store/store_strings.c b/crypto/store/store_strings.c
index 0d525b71c2..e8b17e5188 100644
--- a/crypto/store/store_strings.c
+++ b/crypto/store/store_strings.c
@@ -17,7 +17,8 @@ static const char *const type_strings[] = {
     "Public key", /* OSSL_STORE_INFO_PUBKEY */
     "Pkey", /* OSSL_STORE_INFO_PKEY */
     "Certificate", /* OSSL_STORE_INFO_CERT */
-    "CRL" /* OSSL_STORE_INFO_CRL */
+    "CRL", /* OSSL_STORE_INFO_CRL */
+    "Symmetric key" /* OSSL_STORE_INFO_SKEY */
 };

 const char *OSSL_STORE_INFO_type_string(int type)
diff --git a/doc/man1/openssl-enc.pod.in b/doc/man1/openssl-enc.pod.in
index 429ef9abcf..fd05d777c9 100644
--- a/doc/man1/openssl-enc.pod.in
+++ b/doc/man1/openssl-enc.pod.in
@@ -41,6 +41,8 @@ B<openssl> B<enc>|I<cipher>
 [B<-none>]
 [B<-skeymgmt> I<skeymgmt>]
 [B<-skeyopt> I<opt>:I<value>]
+[B<-skeyuri> I<uri>]
+[B<-storepass> I<arg>]
 {- $OpenSSL::safe::opt_r_synopsis -}
 {- $OpenSSL::safe::opt_provider_synopsis -}

@@ -237,6 +239,18 @@ To obtain an existing opaque symmetric key or generate a new one, key
 options are specified as opt:value. These options can't be used together with
 any options implying raw key directly or indirectly.

+=item B<-skeyuri> I<uri>
+
+The URI identifying the symmetric key object to be used for encryption.  This
+option can't be used together with any options implying raw key directly or
+indirectly. The B<-skeymgmt> option is ignored. If both B<-skeyuri> and
+B<-skeyopt> options are provided, B<-skeyuri> is ignored.
+
+=item B<-storepass> I<arg>
+
+The input URI password source. For more information about the format of I<arg>
+see L<openssl-passphrase-options(1)>.
+
 {- $OpenSSL::safe::opt_r_item -}

 {- $OpenSSL::safe::opt_provider_item -}
@@ -485,7 +499,7 @@ certain parameters. So if, for example, you want to use RC2 with a

 =head1 SEE ALSO

-L<openssl-list(1)>, L<EVP_SKEY(3)>
+L<openssl-list(1)>, L<EVP_SKEY(3)>, L<openssl-passphrase-options(1)>

 =head1 HISTORY

@@ -501,6 +515,8 @@ The B<-skeymgmt> and B<-skeyopt> options were added in OpenSSL 3.5.

 The B<-engine> option was removed in OpenSSL 4.0.

+The B<-skeyuri> and B<-storepass> options were added in OpenSSL 4.0.
+
 =head1 COPYRIGHT

 Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man1/openssl-storeutl.pod.in b/doc/man1/openssl-storeutl.pod.in
index 38dd1df645..c2ae6251e6 100644
--- a/doc/man1/openssl-storeutl.pod.in
+++ b/doc/man1/openssl-storeutl.pod.in
@@ -21,6 +21,7 @@ B<openssl> B<storeutl>
 [B<-certs>]
 [B<-keys>]
 [B<-crls>]
+[B<-skeys>]
 [B<-subject> I<arg>]
 [B<-issuer> I<arg>]
 [B<-serial> I<arg>]
@@ -75,7 +76,9 @@ Fetch objects recursively when possible.

 =item B<-crls>

-Only select the certificates, keys or CRLs from the given URI.
+=item B<-skeys>
+
+Only select the certificates, keys, CRLs or symmetric keys from the given URI.
 However, if this URI would return a set of names (URIs), those are always
 returned.

@@ -138,9 +141,11 @@ This command was added in OpenSSL 1.1.1.

 The B<-engine> option was removed in OpenSSL 4.0.

+The B<-skeys> option was added in OpenSSL 4.0
+
 =head1 COPYRIGHT

-Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2016-2025 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
diff --git a/doc/man3/OSSL_STORE_INFO.pod b/doc/man3/OSSL_STORE_INFO.pod
index b8332855d4..1052749a4b 100644
--- a/doc/man3/OSSL_STORE_INFO.pod
+++ b/doc/man3/OSSL_STORE_INFO.pod
@@ -6,14 +6,16 @@ OSSL_STORE_INFO, OSSL_STORE_INFO_get_type, OSSL_STORE_INFO_get0_NAME,
 OSSL_STORE_INFO_get0_NAME_description,
 OSSL_STORE_INFO_get0_PARAMS, OSSL_STORE_INFO_get0_PUBKEY,
 OSSL_STORE_INFO_get0_PKEY, OSSL_STORE_INFO_get0_CERT, OSSL_STORE_INFO_get0_CRL,
+OSSL_STORE_INFO_get0_SKEY,
 OSSL_STORE_INFO_get1_NAME, OSSL_STORE_INFO_get1_NAME_description,
 OSSL_STORE_INFO_get1_PARAMS, OSSL_STORE_INFO_get1_PUBKEY,
 OSSL_STORE_INFO_get1_PKEY, OSSL_STORE_INFO_get1_CERT, OSSL_STORE_INFO_get1_CRL,
+OSSL_STORE_INFO_get1_SKEY,
 OSSL_STORE_INFO_type_string, OSSL_STORE_INFO_free,
 OSSL_STORE_INFO_new_NAME, OSSL_STORE_INFO_set0_NAME_description,
 OSSL_STORE_INFO_new_PARAMS, OSSL_STORE_INFO_new_PUBKEY,
 OSSL_STORE_INFO_new_PKEY, OSSL_STORE_INFO_new_CERT, OSSL_STORE_INFO_new_CRL,
-OSSL_STORE_INFO_new, OSSL_STORE_INFO_get0_data
+OSSL_STORE_INFO_new, OSSL_STORE_INFO_new_SKEY, OSSL_STORE_INFO_get0_data
 - Functions to manipulate OSSL_STORE_INFO objects

 =head1 SYNOPSIS
@@ -38,6 +40,8 @@ OSSL_STORE_INFO_new, OSSL_STORE_INFO_get0_data
  X509 *OSSL_STORE_INFO_get1_CERT(const OSSL_STORE_INFO *store_info);
  X509_CRL *OSSL_STORE_INFO_get0_CRL(const OSSL_STORE_INFO *store_info);
  X509_CRL *OSSL_STORE_INFO_get1_CRL(const OSSL_STORE_INFO *store_info);
+ EVP_SKEY *OSSL_STORE_INFO_get0_SKEY(const OSSL_STORE_INFO *store_info);
+ EVP_SKEY *OSSL_STORE_INFO_get1_SKEY(const OSSL_STORE_INFO *store_info);

  const char *OSSL_STORE_INFO_type_string(int type);

@@ -50,6 +54,7 @@ OSSL_STORE_INFO_new, OSSL_STORE_INFO_get0_data
  OSSL_STORE_INFO *OSSL_STORE_INFO_new_PKEY(EVP_PKEY *pkey);
  OSSL_STORE_INFO *OSSL_STORE_INFO_new_CERT(X509 *x509);
  OSSL_STORE_INFO *OSSL_STORE_INFO_new_CRL(X509_CRL *crl);
+ OSSL_STORE_INFO *OSSL_STORE_INFO_new_SKEY(EVP_SKEY *skey);

  OSSL_STORE_INFO *OSSL_STORE_INFO_new(int type, void *data);
  void *OSSL_STORE_INFO_get0_data(int type, const OSSL_STORE_INFO *info);
@@ -87,7 +92,7 @@ short string describing it.
 OSSL_STORE_INFO_get0_NAME(), OSSL_STORE_INFO_get0_NAME_description(),
 OSSL_STORE_INFO_get0_PARAMS(), OSSL_STORE_INFO_get0_PUBKEY(),
 OSSL_STORE_INFO_get0_PKEY(), OSSL_STORE_INFO_get0_CERT(),
-OSSL_STORE_INFO_get0_CRL()
+OSSL_STORE_INFO_get0_CRL(), OSSL_STORE_INFO_get0_SKEY()
 all take a B<OSSL_STORE_INFO> and return the object it holds if the
 B<OSSL_STORE_INFO> type (as returned by OSSL_STORE_INFO_get_type())
 matches the function, otherwise NULL.
@@ -95,7 +100,7 @@ matches the function, otherwise NULL.
 OSSL_STORE_INFO_get1_NAME(), OSSL_STORE_INFO_get1_NAME_description(),
 OSSL_STORE_INFO_get1_PARAMS(), OSSL_STORE_INFO_get1_PUBKEY(),
 OSSL_STORE_INFO_get1_PKEY(), OSSL_STORE_INFO_get1_CERT() and
-OSSL_STORE_INFO_get1_CRL()
+OSSL_STORE_INFO_get1_CRL(), OSSL_STORE_INFO_get1_SKEY()
 all take a B<OSSL_STORE_INFO> and return a duplicate the object it
 holds if the B<OSSL_STORE_INFO> type (as returned by
 OSSL_STORE_INFO_get_type()) matches the function, otherwise NULL.
@@ -105,7 +110,8 @@ If the argument is NULL, nothing is done.

 OSSL_STORE_INFO_new_NAME() , OSSL_STORE_INFO_new_PARAMS(),
 , OSSL_STORE_INFO_new_PUBKEY(), OSSL_STORE_INFO_new_PKEY(),
-OSSL_STORE_INFO_new_CERT() and OSSL_STORE_INFO_new_CRL()
+OSSL_STORE_INFO_new_CERT(), OSSL_STORE_INFO_new_CRL() and
+OSSL_STORE_INFO_new_SKEY()
 create a B<OSSL_STORE_INFO> object to hold the given input object.
 On success the input object is consumed.

@@ -181,6 +187,10 @@ An X.509 certificate.

 A X.509 certificate revocation list.

+=item OSSL_STORE_INFO_SKEY
+
+A symmetric key object.
+
 =back

 =head1 RETURN VALUES
@@ -191,21 +201,23 @@ There is no error value.

 OSSL_STORE_INFO_get0_NAME(), OSSL_STORE_INFO_get0_NAME_description(),
 OSSL_STORE_INFO_get0_PARAMS(), OSSL_STORE_INFO_get0_PKEY(),
-OSSL_STORE_INFO_get0_CERT() and OSSL_STORE_INFO_get0_CRL() all return
-a pointer to the OpenSSL object on success, NULL otherwise.
+OSSL_STORE_INFO_get0_CERT(), OSSL_STORE_INFO_get0_CRL() and
+OSSL_STORE_INFO_get0_SKEY() all return a pointer to the OpenSSL object on
+success, NULL otherwise.

 OSSL_STORE_INFO_get1_NAME(), OSSL_STORE_INFO_get1_NAME_description(),
 OSSL_STORE_INFO_get1_PARAMS(), OSSL_STORE_INFO_get1_PKEY(),
-OSSL_STORE_INFO_get1_CERT() and OSSL_STORE_INFO_get1_CRL() all return
-a pointer to a duplicate of the OpenSSL object on success, NULL otherwise.
+OSSL_STORE_INFO_get1_CERT(), OSSL_STORE_INFO_get1_CRL() and
+OSSL_STORE_INFO_get1_SKEY() all return a pointer to a duplicate of the OpenSSL
+object on success, NULL otherwise.

 OSSL_STORE_INFO_type_string() returns a string on success, or NULL on
 failure.

 OSSL_STORE_INFO_new_NAME(), OSSL_STORE_INFO_new_PARAMS(),
-OSSL_STORE_INFO_new_PKEY(), OSSL_STORE_INFO_new_CERT() and
-OSSL_STORE_INFO_new_CRL() return a B<OSSL_STORE_INFO>
-pointer on success, or NULL on failure.
+OSSL_STORE_INFO_new_PKEY(), OSSL_STORE_INFO_new_CERT(),
+OSSL_STORE_INFO_new_CRL() and OSSL_STORE_INFO_new_SKEY() return a
+B<OSSL_STORE_INFO> pointer on success, or NULL on failure.

 OSSL_STORE_INFO_set0_NAME_description() returns 1 on success, or 0 on
 failure.
@@ -220,6 +232,9 @@ The OSSL_STORE API was added in OpenSSL 1.1.1.

 The OSSL_STORE_INFO_PUBKEY object type was added in OpenSSL 3.0.

+OSSL_STORE_INFO_get0_SKEY(), OSSL_STORE_INFO_get1_SKEY() and
+OSSL_STORE_INFO_new_SKEY() were added in OpenSSL 4.0.
+
 =head1 COPYRIGHT

 Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man7/ossl_store-file.pod b/doc/man7/ossl_store-file.pod
index d4d163eabb..c44567270f 100644
--- a/doc/man7/ossl_store-file.pod
+++ b/doc/man7/ossl_store-file.pod
@@ -35,6 +35,13 @@ If the file isn't determined to be formatted as PEM, the content is
 loaded in raw form in its entirety and passed to the available file
 handlers as is, with no PEM name or headers.

+Store 'file' also supports load of symmetric keys from arbitrary files. As
+symmetric keys don't have any structure and encapsulation, the interpretation
+of a file as a symmetric key should be requested explicitly via
+L<OSSL_STORE_expect(3)>.
+
+When loading from a file, symmetric keys are limited to 2048 bytes in length.
+
 Each file handler is expected to handle PEM and non-PEM content as
 appropriate.  Some may refuse non-PEM content for the sake of
 determinism (for example, there are keys out in the wild that are
@@ -57,11 +64,15 @@ See L<passphrase-encoding(7)> for more information.

 =head1 SEE ALSO

-L<ossl_store(7)>, L<passphrase-encoding(7)>
+L<ossl_store(7)>, L<passphrase-encoding(7)>, L<OSSL_STORE_expect(3)>
+
+=head1 HISTORY
+
+Support for reading symmetric keys from files was added in OpenSSL 4.0

 =head1 COPYRIGHT

-Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2018-2025 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
diff --git a/include/openssl/core_object.h b/include/openssl/core_object.h
index df0c79436d..d6d46a914f 100644
--- a/include/openssl/core_object.h
+++ b/include/openssl/core_object.h
@@ -29,6 +29,7 @@ extern "C" {
 #define OSSL_OBJECT_PKEY 2 /* EVP_PKEY * */
 #define OSSL_OBJECT_CERT 3 /* X509 * */
 #define OSSL_OBJECT_CRL 4 /* X509_CRL * */
+#define OSSL_OBJECT_SKEY 5 /* EVP_SKEY * */

 /*
  * The rest of the associated OSSL_PARAM elements is described in core_names.h
diff --git a/include/openssl/store.h b/include/openssl/store.h
index 0658739ea3..5c25d0f852 100644
--- a/include/openssl/store.h
+++ b/include/openssl/store.h
@@ -160,6 +160,7 @@ OSSL_STORE_CTX *OSSL_STORE_attach(BIO *bio, const char *scheme,
 #define OSSL_STORE_INFO_PKEY 4 /* EVP_PKEY * */
 #define OSSL_STORE_INFO_CERT 5 /* X509 * */
 #define OSSL_STORE_INFO_CRL 6 /* X509_CRL * */
+#define OSSL_STORE_INFO_SKEY 7 /* EVP_SKEY * */

 /*
  * Functions to generate OSSL_STORE_INFOs, one function for each type we
@@ -176,6 +177,7 @@ OSSL_STORE_INFO *OSSL_STORE_INFO_new_PUBKEY(EVP_PKEY *pubkey);
 OSSL_STORE_INFO *OSSL_STORE_INFO_new_PKEY(EVP_PKEY *pkey);
 OSSL_STORE_INFO *OSSL_STORE_INFO_new_CERT(X509 *x509);
 OSSL_STORE_INFO *OSSL_STORE_INFO_new_CRL(X509_CRL *crl);
+OSSL_STORE_INFO *OSSL_STORE_INFO_new_SKEY(EVP_SKEY *skey);

 /*
  * Functions to try to extract data from a OSSL_STORE_INFO.
@@ -196,6 +198,8 @@ X509 *OSSL_STORE_INFO_get0_CERT(const OSSL_STORE_INFO *info);
 X509 *OSSL_STORE_INFO_get1_CERT(const OSSL_STORE_INFO *info);
 X509_CRL *OSSL_STORE_INFO_get0_CRL(const OSSL_STORE_INFO *info);
 X509_CRL *OSSL_STORE_INFO_get1_CRL(const OSSL_STORE_INFO *info);
+EVP_SKEY *OSSL_STORE_INFO_get0_SKEY(const OSSL_STORE_INFO *info);
+EVP_SKEY *OSSL_STORE_INFO_get1_SKEY(const OSSL_STORE_INFO *info);

 const char *OSSL_STORE_INFO_type_string(int type);

diff --git a/include/openssl/storeerr.h b/include/openssl/storeerr.h
index a61bee1112..fb58f0630e 100644
--- a/include/openssl/storeerr.h
+++ b/include/openssl/storeerr.h
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2025 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
@@ -32,6 +32,7 @@
 #define OSSL_STORE_R_NOT_A_NAME 103
 #define OSSL_STORE_R_NOT_A_PRIVATE_KEY 102
 #define OSSL_STORE_R_NOT_A_PUBLIC_KEY 122
+#define OSSL_STORE_R_NOT_A_SYMMETRIC_KEY 124
 #define OSSL_STORE_R_NOT_PARAMETERS 104
 #define OSSL_STORE_R_NO_LOADERS_FOUND 123
 #define OSSL_STORE_R_PASSPHRASE_CALLBACK_ERROR 114
diff --git a/providers/implementations/storemgmt/file_store.c b/providers/implementations/storemgmt/file_store.c
index 77351dccc2..8493bb4426 100644
--- a/providers/implementations/storemgmt/file_store.c
+++ b/providers/implementations/storemgmt/file_store.c
@@ -464,6 +464,8 @@ static int file_setup_decoders(struct file_ctx_st *ctx)
                 goto err;
             }
             break;
+        case OSSL_STORE_INFO_SKEY: /* No input structure */
+            break;
         default:
             break;
         }
@@ -502,6 +504,16 @@ static int file_setup_decoders(struct file_ctx_st *ctx)
                 continue;
             }

+            /*
+             * As any sequence of bytes can be a secret key, we allow
+             * reading raw key from a file only when it is explicitly requested.
+             */
+            if ((ctx->expected_type != OSSL_STORE_INFO_SKEY)
+                && OPENSSL_strcasecmp(input_type, "raw") == 0) {
+                ossl_decoder_instance_free(to_obj_inst);
+                continue;
+            }
+
             if (!ossl_decoder_ctx_add_decoder_inst(ctx->_.file.decoderctx,
                     to_obj_inst)) {
                 ossl_decoder_instance_free(to_obj_inst);
diff --git a/providers/implementations/storemgmt/file_store_any2obj.c b/providers/implementations/storemgmt/file_store_any2obj.c
index fc7a98d1e8..67de51d94a 100644
--- a/providers/implementations/storemgmt/file_store_any2obj.c
+++ b/providers/implementations/storemgmt/file_store_any2obj.c
@@ -302,14 +302,66 @@ err:
         OSSL_DISPATCH_END                                                    \
     }

+#define MAX_RAW_KEY_SIZE 2048
+
+static OSSL_FUNC_decoder_decode_fn raw2obj_decode;
+static int raw2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+                          OSSL_CALLBACK *data_cb, void *data_cbarg,
+                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct any2obj_ctx_st *ctx = vctx;
+    BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+    BUF_MEM *mem = NULL;
+    size_t len = 0, max_len = MAX_RAW_KEY_SIZE;
+    int ok = 0;
+
+    if (in == NULL)
+        goto err;
+
+    if ((mem = BUF_MEM_new()) == NULL
+        || BUF_MEM_grow(mem, max_len) == 0) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+        goto err;
+    }
+
+    ok = BIO_read_ex(in, &mem->data[0], max_len, &len);
+    if (ok == 0) {
+        ERR_raise(ERR_LIB_BIO, ERR_R_BIO_LIB);
+        goto err;
+    }
+
+    if (len == 0) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_UNSUPPORTED);
+        goto err;
+    }
+
+    BIO_free(in);
+
+    if (BUF_MEM_grow(mem, len) != len) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+        goto err;
+    }
+
+    /* any2obj_decode_final() frees |mem| for us */
+    return any2obj_decode_final(ctx, OSSL_OBJECT_SKEY, "raw", "SKEY",
+                                mem, data_cb, data_cbarg);
+
+ err:
+    BIO_free(in);
+    BUF_MEM_free(mem);
+    return 0;
+}
+
 MAKE_DECODER(der, OSSL_OBJECT_UNKNOWN);
 MAKE_DECODER(msblob, OSSL_OBJECT_PKEY);
 MAKE_DECODER(pvk, OSSL_OBJECT_PKEY);
+MAKE_DECODER(raw, OSSL_OBJECT_SKEY);

 const OSSL_ALGORITHM ossl_any_to_obj_algorithm[] = {
     { "obj", "input=DER", der_to_obj_decoder_functions },
     { "obj", "input=MSBLOB", msblob_to_obj_decoder_functions },
     { "obj", "input=PVK", pvk_to_obj_decoder_functions },
+    { "obj", "input=RAW", raw_to_obj_decoder_functions },
     {
         NULL,
     }
diff --git a/test/recipes/20-test_enc.t b/test/recipes/20-test_enc.t
index 2b430b74fc..fe09dfdd4c 100644
--- a/test/recipes/20-test_enc.t
+++ b/test/recipes/20-test_enc.t
@@ -41,7 +41,7 @@ my @ciphers =
                      |rc2|rc4|seed)/x} @ciphers
     if disabled("legacy");

-plan tests => 5 + (scalar @ciphers)*2;
+plan tests => 6 + (scalar @ciphers)*2;

  SKIP: {
      skip "Problems getting ciphers...", 1 + scalar(@ciphers)
@@ -90,4 +90,16 @@ plan tests => 5 + (scalar @ciphers)*2;
         && compare_text($test,"salted.clear") == 0,
         "Check that we can still use a salt length of 16 bytes for PKDF2");

+#./util/wrap.pl apps/openssl enc -aes128 -K 30313032303330343035303630373038 -iv 100f0e0d0c0b0a090807060504030201 -in 1.txt -out 2.enc
+#./util/wrap.pl apps/openssl enc -aes128 -skeyuri skeyfile.bin -iv 100f0e0d0c0b0a090807060504030201 -in 1.txt -out 1.enc
+     my $folder = "test/recipes/20-test_enc_data";
+     my $skeyuri = srctop_file($folder, "skeyfile.bin");
+     ok(run(app([$cmd, "enc", "-in", $test, "-aes128", "-K", "30313032303330343035303630373038",
+                 "-iv", "100f0e0d0c0b0a090807060504030201",
+                 "-out", "key_from_cmdline.enc"]))
+        && run(app([$cmd, "enc", "-in", $test, "-aes128", "-skeyuri", $skeyuri,
+                 "-iv", "100f0e0d0c0b0a090807060504030201",
+                 "-out", "key_from_uri.enc" ]))
+        && File::Compare::compare("key_from_cmdline.enc", "key_from_uri.enc") == 0,
+        "Check that key from URI gives an equal result comparing to the explicit one");
 }
diff --git a/test/recipes/20-test_enc_data/skeyfile.bin b/test/recipes/20-test_enc_data/skeyfile.bin
new file mode 100644
index 0000000000..8fd8c47f0f
Binary files /dev/null and b/test/recipes/20-test_enc_data/skeyfile.bin differ
diff --git a/util/libcrypto.num b/util/libcrypto.num
index ee9be3c00e..155ba0f93d 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5805,6 +5805,9 @@ i2d_OSSL_AA_DIST_POINT                  ?	4_0_0	EXIST::FUNCTION:
 OSSL_AA_DIST_POINT_free                 ?	4_0_0	EXIST::FUNCTION:
 OSSL_AA_DIST_POINT_new                  ?	4_0_0	EXIST::FUNCTION:
 OSSL_AA_DIST_POINT_it                   ?	4_0_0	EXIST::FUNCTION:
+OSSL_STORE_INFO_new_SKEY                ?	4_0_0	EXIST::FUNCTION:
+OSSL_STORE_INFO_get0_SKEY               ?	4_0_0	EXIST::FUNCTION:
+OSSL_STORE_INFO_get1_SKEY               ?	4_0_0	EXIST::FUNCTION:
 OPENSSL_posix_to_tm                     ?	4_0_0	EXIST::FUNCTION:
 OPENSSL_tm_to_posix                     ?	4_0_0	EXIST::FUNCTION:
 OPENSSL_timegm                          ?	4_0_0	EXIST::FUNCTION: