[openssl] master update

Richard Levitte levitte at openssl.org
Wed Nov 11 11:43:33 UTC 2020


The branch master has been updated
       via  122e81f0705e74a2019c482e5122bbd9195ea1af (commit)
       via  f49d486075e94a2b6387961c63436d59d5a988f4 (commit)
       via  973a52ce01d9d821040d5a8c780125d3bdeacb40 (commit)
       via  4227e504c894db14d06be7180d0b4e7f6fe4ac2c (commit)
       via  c319b6276bf84da2676b4b70f40f7ce897649f72 (commit)
       via  0b9f90f5043e4cb8559448fb6270b0e7dd74e4b9 (commit)
       via  b9a2afdfe68a5212dc2352a574a6ca98d8cf5140 (commit)
       via  cd861ab73d57ea21dd7342b13f4a733da229e15e (commit)
       via  8a98a507fb9a06254c79af4d9c9104f5f300a032 (commit)
       via  45da4a0fc54bce2c5fd99ce4a3fbe8e5456092ea (commit)
      from  6debc6ab7499745baf7123e69936a9997dbbe6cb (commit)


- Log -----------------------------------------------------------------
commit 122e81f0705e74a2019c482e5122bbd9195ea1af
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 11:45:19 2020 +0200

    test/recipes/30-test_evp_libctx.t: use fips-and-base.cnf
    
    The FIPS provider module doesn't have any encoders, the base provider
    is needed for that.
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit f49d486075e94a2b6387961c63436d59d5a988f4
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 11:44:43 2020 +0200

    test/evp_libctx_test.c: use OSSL_ENCODER instead of i2d_PublicKey()
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit 973a52ce01d9d821040d5a8c780125d3bdeacb40
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 08:59:48 2020 +0200

    test/endecode_test.c: Update to specify output structures
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit 4227e504c894db14d06be7180d0b4e7f6fe4ac2c
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 08:55:39 2020 +0200

    Adapt libcrypto functionality to specify the desired output structure
    
    This also modifies i2d_PublicKey() and i2d_KeyParams() to support
    provided keys.
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit c319b6276bf84da2676b4b70f40f7ce897649f72
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 08:34:47 2020 +0200

    PROV: Re-implement all the keypair encoders
    
    The base functionality to implement the keypair encoders doesn't
    change much, but this results in a more massive amount of
    OSSL_DISPATCH and OSSL_ALGORITHM arrays, to support a fine grained
    selection of implementation based on what parts of the keypair
    structure (combinations of key parameters, public key and private key)
    should be output, the output type ("TEXT", "DER" or "PEM") and the
    outermost output structure ("pkcs8", "SubjectPublicKeyInfo", key
    type specific structures, ...).
    
    We add support for the generic structure name "type-specific", to
    allow selecting that without knowing the exact name of that structure.
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit 0b9f90f5043e4cb8559448fb6270b0e7dd74e4b9
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Oct 28 10:14:53 2020 +0100

    ENCODER: Add tracing
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit b9a2afdfe68a5212dc2352a574a6ca98d8cf5140
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 08:23:43 2020 +0200

    ENCODER: Add output structure support for EVP_PKEY encoding
    
    OSSL_ENCODER_CTX_new_by_EVP_PKEY() takes one more argument to express
    the desired outermost structure for the output.
    
    This also adds OSSL_ENCODER_CTX_prune_encoders(), which is used to
    reduce the stack of encoders found according to criteria formed from
    the combination of desired selection, output type and output
    structure.
    
    squash! ENCODER: Add output structure support for EVP_PKEY encoding
    
    Replace the paragraph talking about OSSL_ENCODER_CTX_prune_encoders() with:
    
    The encoding processor encoder_process() is enhanced with better
    analysis of the stack of encoder implementations.  To avoid having to
    keep an on the side array of information, it uses recursion.
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit cd861ab73d57ea21dd7342b13f4a733da229e15e
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 08:17:41 2020 +0200

    ENCODER: Add support for OSSL_FUNC_encoder_does_selection()
    
    OSSL_FUNC_encoder_does_selection() is a dispatchable encoder implementation
    function that should return 1 if the given |selection| is supported by an
    encoder implementation and 0 if not.  This can be used by libcrypto
    functionality to figure out if an encoder implementation should be
    considered or not.
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit 8a98a507fb9a06254c79af4d9c9104f5f300a032
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 07:57:04 2020 +0200

    ENCODER: Add support for specifying the outermost output structure
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

commit 45da4a0fc54bce2c5fd99ce4a3fbe8e5456092ea
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Oct 17 07:58:32 2020 +0200

    CORE: Add support for specifying the outermost object structure
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/13167)

-----------------------------------------------------------------------

Summary of changes:
 crypto/asn1/build.info                             |   4 +-
 crypto/asn1/i2d_evp.c                              | 124 ++++
 crypto/asn1/i2d_param.c                            |  30 -
 crypto/asn1/i2d_pr.c                               |  51 --
 crypto/asn1/i2d_pu.c                               |  44 --
 crypto/encode_decode/encoder_lib.c                 | 441 ++++++++++---
 crypto/encode_decode/encoder_local.h               |  26 +-
 crypto/encode_decode/encoder_meth.c                |   5 +
 crypto/encode_decode/encoder_pkey.c                | 157 ++---
 crypto/evp/p_lib.c                                 |   2 +-
 crypto/pem/pem_local.h                             |  10 +-
 crypto/pem/pem_pk8.c                               |   4 +-
 crypto/trace.c                                     |   1 +
 crypto/x509/x_pubkey.c                             |   6 +-
 doc/man3/OSSL_ENCODER_CTX.pod                      |  68 +-
 doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod      |   8 +-
 doc/man7/provider-encoder.pod                      |  28 +-
 doc/man7/provider-object.pod                       |   5 +
 include/openssl/core_dispatch.h                    |   9 +-
 include/openssl/core_names.h                       |  20 +-
 include/openssl/encoder.h                          |   9 +-
 include/openssl/trace.h                            |   3 +-
 providers/baseprov.c                               |   8 +-
 providers/defltprov.c                              |   8 +-
 providers/encoders.inc                             | 224 ++++++-
 .../implementations/encode_decode/encode_key2any.c | 721 ++++++++++++++++-----
 .../implementations/include/prov/implementations.h | 104 ++-
 test/endecode_test.c                               | 147 ++---
 test/evp_libctx_test.c                             |  13 +-
 test/evp_pkey_provided_test.c                      |  13 +-
 test/recipes/30-test_evp_libctx.t                  |   2 +-
 util/libcrypto.num                                 |   2 +
 32 files changed, 1594 insertions(+), 703 deletions(-)
 create mode 100644 crypto/asn1/i2d_evp.c
 delete mode 100644 crypto/asn1/i2d_param.c
 delete mode 100644 crypto/asn1/i2d_pr.c
 delete mode 100644 crypto/asn1/i2d_pu.c

diff --git a/crypto/asn1/build.info b/crypto/asn1/build.info
index a66c3084ce..e10d631654 100644
--- a/crypto/asn1/build.info
+++ b/crypto/asn1/build.info
@@ -5,7 +5,7 @@ SOURCE[../../libcrypto]=\
         a_utf8.c a_sign.c a_digest.c a_verify.c a_mbstr.c a_strex.c \
         x_algor.c x_val.c x_sig.c x_bignum.c \
         x_int64.c x_info.c x_spki.c nsseq.c \
-        d2i_pu.c d2i_pr.c i2d_pu.c i2d_pr.c\
+        d2i_pu.c d2i_pr.c i2d_evp.c \
         t_pkey.c t_spki.c t_bitst.c \
         tasn_new.c tasn_fre.c tasn_enc.c tasn_dec.c tasn_utl.c tasn_typ.c \
         tasn_prn.c tasn_scn.c ameth_lib.c \
@@ -14,7 +14,7 @@ SOURCE[../../libcrypto]=\
         asn1_gen.c asn1_par.c asn1_lib.c asn1_err.c a_strnid.c \
         evp_asn1.c asn_pack.c p5_pbe.c p5_pbev2.c p5_scrypt.c p8_pkey.c \
         asn_moid.c asn_mstbl.c asn1_item_list.c \
-        d2i_param.c i2d_param.c
+        d2i_param.c
 IF[{- !$disabled{'rsa'} and !$disabled{'rc4'} -}]
   SOURCE[../../libcrypto]=n_pkey.c
 ENDIF
diff --git a/crypto/asn1/i2d_evp.c b/crypto/asn1/i2d_evp.c
new file mode 100644
index 0000000000..a81ae415fa
--- /dev/null
+++ b/crypto/asn1/i2d_evp.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1995-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
+ */
+
+/* We need to use some deprecated APIs to support the legacy bits */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include <stdio.h>
+#include "internal/cryptlib.h"
+#include <openssl/evp.h>
+#include <openssl/encoder.h>
+#include <openssl/buffer.h>
+#include <openssl/x509.h>
+#include <openssl/rsa.h>         /* For i2d_RSAPublicKey */
+#include <openssl/dsa.h>         /* For i2d_DSAPublicKey */
+#include <openssl/ec.h>          /* For i2o_ECPublicKey */
+#include "crypto/asn1.h"
+#include "crypto/evp.h"
+
+static int i2d_provided(const EVP_PKEY *a, int selection,
+                        const char *output_structures[],
+                        unsigned char **pp)
+{
+    OSSL_ENCODER_CTX *ctx = NULL;
+    int ret;
+
+    for (ret = -1;
+         ret == -1 && *output_structures != NULL;
+         output_structures++) {
+        /*
+         * The i2d_ calls don't take a boundary length for *pp.  However,
+         * OSSL_ENCODER_CTX_get_num_encoders() needs one, so we make one
+         * up.
+         */
+        size_t len = INT_MAX;
+
+        ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, selection, "DER",
+                                               *output_structures,
+                                               NULL, NULL);
+        if (ctx == NULL)
+            return -1;
+        if (OSSL_ENCODER_to_data(ctx, pp, &len))
+            ret = (int)len;
+        OSSL_ENCODER_CTX_free(ctx);
+        ctx = NULL;
+    }
+
+    if (ret == -1)
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_TYPE);
+    return ret;
+}
+
+int i2d_KeyParams(const EVP_PKEY *a, unsigned char **pp)
+{
+    if (evp_pkey_is_provided(a)) {
+        const char *output_structures[] = { "type-specific", NULL };
+
+        return i2d_provided(a, EVP_PKEY_KEY_PARAMETERS, output_structures, pp);
+    }
+    if (a->ameth != NULL && a->ameth->param_encode != NULL)
+        return a->ameth->param_encode(a, pp);
+    ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_TYPE);
+    return -1;
+}
+
+int i2d_KeyParams_bio(BIO *bp, const EVP_PKEY *pkey)
+{
+    return ASN1_i2d_bio_of(EVP_PKEY, i2d_KeyParams, bp, pkey);
+}
+
+int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
+{
+    if (evp_pkey_is_provided(a)) {
+        const char *output_structures[] = { "type-specific", "pkcs8", NULL };
+
+        return i2d_provided(a, EVP_PKEY_KEYPAIR, output_structures, pp);
+    }
+    if (a->ameth != NULL && a->ameth->old_priv_encode != NULL) {
+        return a->ameth->old_priv_encode(a, pp);
+    }
+    if (a->ameth != NULL && a->ameth->priv_encode != NULL) {
+        PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8(a);
+        int ret = 0;
+
+        if (p8 != NULL) {
+            ret = i2d_PKCS8_PRIV_KEY_INFO(p8, pp);
+            PKCS8_PRIV_KEY_INFO_free(p8);
+        }
+        return ret;
+    }
+    ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+    return -1;
+}
+
+int i2d_PublicKey(const EVP_PKEY *a, unsigned char **pp)
+{
+    if (evp_pkey_is_provided(a)) {
+        const char *output_structures[] = { "type-specific", NULL };
+
+        return i2d_provided(a, EVP_PKEY_PUBLIC_KEY, output_structures, pp);
+    }
+    switch (EVP_PKEY_id(a)) {
+#ifndef OPENSSL_NO_RSA
+    case EVP_PKEY_RSA:
+        return i2d_RSAPublicKey(EVP_PKEY_get0_RSA(a), pp);
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        return i2d_DSAPublicKey(EVP_PKEY_get0_DSA(a), pp);
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        return i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(a), pp);
+#endif
+    default:
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        return -1;
+    }
+}
diff --git a/crypto/asn1/i2d_param.c b/crypto/asn1/i2d_param.c
deleted file mode 100644
index 1e1ebc95b2..0000000000
--- a/crypto/asn1/i2d_param.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019 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 <stdio.h>
-#include "internal/cryptlib.h"
-#include <openssl/evp.h>
-#include <openssl/objects.h>
-#include <openssl/asn1.h>
-#include "crypto/asn1.h"
-#include "crypto/evp.h"
-
-int i2d_KeyParams(const EVP_PKEY *a, unsigned char **pp)
-{
-    if (a->ameth != NULL && a->ameth->param_encode != NULL)
-        return a->ameth->param_encode(a, pp);
-    ASN1err(ASN1_F_I2D_KEYPARAMS, ASN1_R_UNSUPPORTED_TYPE);
-    return -1;
-}
-
-int i2d_KeyParams_bio(BIO *bp, const EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of(EVP_PKEY, i2d_KeyParams, bp, pkey);
-}
-
diff --git a/crypto/asn1/i2d_pr.c b/crypto/asn1/i2d_pr.c
deleted file mode 100644
index 7185abef45..0000000000
--- a/crypto/asn1/i2d_pr.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 1995-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 <stdio.h>
-#include <limits.h>
-#include "internal/cryptlib.h"
-#include <openssl/evp.h>
-#include <openssl/encoder.h>
-#include <openssl/buffer.h>
-#include <openssl/x509.h>
-#include "crypto/asn1.h"
-#include "crypto/evp.h"
-
-int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
-{
-    if (a->ameth && a->ameth->old_priv_encode) {
-        return a->ameth->old_priv_encode(a, pp);
-    }
-    if (a->ameth && a->ameth->priv_encode) {
-        PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8(a);
-        int ret = 0;
-        if (p8 != NULL) {
-            ret = i2d_PKCS8_PRIV_KEY_INFO(p8, pp);
-            PKCS8_PRIV_KEY_INFO_free(p8);
-        }
-        return ret;
-    }
-    if (evp_pkey_is_provided(a)) {
-        /* |*pp| is unbounded, so we need an upper limit */
-        size_t length = INT_MAX;
-        int selection = EVP_PKEY_KEYPAIR;
-        int ret = -1;
-        OSSL_ENCODER_CTX *ctx;
-
-        if ((ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, "DER", selection,
-                                                    NULL, NULL)) != NULL
-            && OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0
-            && OSSL_ENCODER_to_data(ctx, pp, &length))
-            ret = (int)length;
-        OSSL_ENCODER_CTX_free(ctx);
-        return ret;
-    }
-    ASN1err(ASN1_F_I2D_PRIVATEKEY, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
-    return -1;
-}
diff --git a/crypto/asn1/i2d_pu.c b/crypto/asn1/i2d_pu.c
deleted file mode 100644
index d0151e5bd6..0000000000
--- a/crypto/asn1/i2d_pu.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 1995-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
- */
-
-/*
- * DSA low level APIs are deprecated for public use, but still ok for
- * internal use.
- */
-#include "internal/deprecated.h"
-
-#include <stdio.h>
-#include "internal/cryptlib.h"
-#include <openssl/bn.h>
-#include <openssl/evp.h>
-#include <openssl/objects.h>
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#include <openssl/ec.h>
-
-int i2d_PublicKey(const EVP_PKEY *a, unsigned char **pp)
-{
-    switch (EVP_PKEY_id(a)) {
-#ifndef OPENSSL_NO_RSA
-    case EVP_PKEY_RSA:
-        return i2d_RSAPublicKey(EVP_PKEY_get0_RSA(a), pp);
-#endif
-#ifndef OPENSSL_NO_DSA
-    case EVP_PKEY_DSA:
-        return i2d_DSAPublicKey(EVP_PKEY_get0_DSA(a), pp);
-#endif
-#ifndef OPENSSL_NO_EC
-    case EVP_PKEY_EC:
-        return i2o_ECPublicKey(EVP_PKEY_get0_EC_KEY(a), pp);
-#endif
-    default:
-        ASN1err(ASN1_F_I2D_PUBLICKEY, ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
-        return -1;
-    }
-}
diff --git a/crypto/encode_decode/encoder_lib.c b/crypto/encode_decode/encoder_lib.c
index 593483313c..d15fb27fde 100644
--- a/crypto/encode_decode/encoder_lib.c
+++ b/crypto/encode_decode/encoder_lib.c
@@ -14,13 +14,41 @@
 #include <openssl/buffer.h>
 #include <openssl/params.h>
 #include <openssl/provider.h>
+#include <openssl/trace.h>
 #include "encoder_local.h"
 
-static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out);
+struct encoder_process_data_st {
+    OSSL_ENCODER_CTX *ctx;
+
+    /* Current BIO */
+    BIO *bio;
+
+    /* Index of the current encoder instance to be processed */
+    int current_encoder_inst_index;
+
+    /* Processing data passed down through recursion */
+    int level;                   /* Recursion level */
+    OSSL_ENCODER_INSTANCE *next_encoder_inst;
+    int count_output_structure;
+
+    /* Processing data passed up through recursion */
+    OSSL_ENCODER_INSTANCE *prev_encoder_inst;
+    unsigned char *running_output;
+    size_t running_output_length;
+};
+
+static int encoder_process(struct encoder_process_data_st *data);
 
 int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
 {
-    return encoder_process(ctx, out);
+    struct encoder_process_data_st data;
+
+    memset(&data, 0, sizeof(data));
+    data.ctx = ctx;
+    data.bio = out;
+    data.current_encoder_inst_index = OSSL_ENCODER_CTX_get_num_encoders(ctx);
+
+    return encoder_process(&data) > 0;
 }
 
 #ifndef OPENSSL_NO_STDIO
@@ -57,7 +85,7 @@ int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata,
     int ret = 0;
 
     if (pdata_len == NULL) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
@@ -97,6 +125,22 @@ int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata,
     return ret;
 }
 
+int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (!ossl_assert(selection != 0)) {
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    ctx->selection = selection;
+    return 1;
+}
+
 int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
                                      const char *output_type)
 {
@@ -109,19 +153,15 @@ int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
     return 1;
 }
 
-int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection)
+int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx,
+                                          const char *output_structure)
 {
-    if (!ossl_assert(ctx != NULL)) {
+    if (!ossl_assert(ctx != NULL) || !ossl_assert(output_structure != NULL)) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
-    if (!ossl_assert(selection != 0)) {
-        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT);
-        return 0;
-    }
-
-    ctx->selection = selection;
+    ctx->output_structure = output_structure;
     return 1;
 }
 
@@ -129,7 +169,7 @@ static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder,
                                                         void *encoderctx)
 {
     OSSL_ENCODER_INSTANCE *encoder_inst = NULL;
-    OSSL_PARAM params[3];
+    OSSL_PARAM params[4];
 
     if (!ossl_assert(encoder != NULL)) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
@@ -155,12 +195,16 @@ static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder,
         OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
                                       (char **)&encoder_inst->output_type, 0);
     params[1] =
+        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_STRUCTURE,
+                                      (char **)&encoder_inst->output_structure,
+                                      0);
+    params[2] =
         OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_INPUT_TYPE,
                                       (char **)&encoder_inst->input_type, 0);
-    params[2] = OSSL_PARAM_construct_end();
+    params[3] = OSSL_PARAM_construct_end();
 
     if (!encoder->get_params(params)
-        || !OSSL_PARAM_modified(&params[1]))
+        || !OSSL_PARAM_modified(&params[0]))
         goto err;
 
     if (!OSSL_ENCODER_up_ref(encoder)) {
@@ -191,6 +235,8 @@ void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst)
 static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx,
                                              OSSL_ENCODER_INSTANCE *ei)
 {
+    int ok;
+
     if (ctx->encoder_insts == NULL
         && (ctx->encoder_insts =
             sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) {
@@ -198,7 +244,18 @@ static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx,
         return 0;
     }
 
-    return (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0);
+    ok = (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0);
+    if (ok) {
+        OSSL_TRACE_BEGIN(ENCODER) {
+            BIO_printf(trc_out,
+                       "(ctx %p) Added encoder instance %p (encoder %p) with:\n",
+                       (void *)ctx, (void *)ei, (void *)ei->encoder);
+            BIO_printf(trc_out,
+                       "    output type: %s, output structure: %s, input type :%s\n",
+                       ei->output_type, ei->output_structure, ei->input_type);
+        } OSSL_TRACE_END(ENCODER);
+    }
+    return ok;
 }
 
 int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder)
@@ -312,120 +369,292 @@ OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst)
     return encoder_inst->output_type;
 }
 
-static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out)
+const char *
+OSSL_ENCODER_INSTANCE_get_output_structure(OSSL_ENCODER_INSTANCE *encoder_inst)
 {
-    size_t i, end;
-    void *latest_output = NULL;
-    size_t latest_output_length = 0;
-    const char *latest_output_type = NULL;
-    const char *last_input_type = NULL;
-    int ok = 0;
-
-    end = OSSL_ENCODER_CTX_get_num_encoders(ctx);
-    for (i = 0; i < end; i++) {
-        OSSL_ENCODER_INSTANCE *encoder_inst =
-            sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i);
-        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
-        void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
-        const char *current_input_type =
-            OSSL_ENCODER_INSTANCE_get_input_type(encoder_inst);
-        const char *current_output_type =
-            OSSL_ENCODER_INSTANCE_get_output_type(encoder_inst);
-        BIO *current_out;
-        BIO *allocated_out = NULL;
-        const void *current_data = NULL;
-        OSSL_PARAM abstract[3];
-        OSSL_PARAM *abstract_p;
-        const OSSL_PARAM *current_abstract = NULL;
-
-        if (latest_output_type == NULL) {
-            /*
-             * This is the first iteration, so we prepare the object to be
-             * encoded
-             */
+    if (encoder_inst == NULL)
+        return NULL;
+    return encoder_inst->output_structure;
+}
 
-            current_data = ctx->construct(encoder_inst, ctx->construct_data);
+static int encoder_process(struct encoder_process_data_st *data)
+{
+    OSSL_ENCODER_INSTANCE *current_encoder_inst = NULL;
+    OSSL_ENCODER *current_encoder = NULL;
+    OSSL_ENCODER_CTX *current_encoder_ctx = NULL;
+    BIO *allocated_out = NULL;
+    const void *original_data = NULL;
+    OSSL_PARAM abstract[10];
+    const OSSL_PARAM *current_abstract = NULL;
+    int i;
+    int ok = -1;  /* -1 signifies that the lookup loop gave nothing */
+    int top = 0;
+
+    if (data->next_encoder_inst == NULL) {
+        /* First iteration, where we prepare for what is to come */
+
+        data->count_output_structure =
+            data->ctx->output_structure == NULL ? -1 : 0;
+        top = 1;
+    }
 
-            /* Assume that the constructor recorded an error */
-            if (current_data == NULL)
-                goto loop_end;
+    for (i = data->current_encoder_inst_index; i-- > 0;) {
+        OSSL_ENCODER *next_encoder = NULL;
+        const char *current_output_type;
+        const char *current_output_structure;
+        struct encoder_process_data_st new_data;
+
+        if (!top)
+            next_encoder =
+                OSSL_ENCODER_INSTANCE_get_encoder(data->next_encoder_inst);
+
+        current_encoder_inst =
+            sk_OSSL_ENCODER_INSTANCE_value(data->ctx->encoder_insts, i);
+        current_encoder =
+            OSSL_ENCODER_INSTANCE_get_encoder(current_encoder_inst);
+        current_encoder_ctx =
+            OSSL_ENCODER_INSTANCE_get_encoder_ctx(current_encoder_inst);
+        current_output_type =
+            OSSL_ENCODER_INSTANCE_get_output_type(current_encoder_inst);
+        current_output_structure =
+            OSSL_ENCODER_INSTANCE_get_output_structure(current_encoder_inst);
+        memset(&new_data, 0, sizeof(new_data));
+        new_data.ctx = data->ctx;
+        new_data.current_encoder_inst_index = i;
+        new_data.next_encoder_inst = current_encoder_inst;
+        new_data.count_output_structure = data->count_output_structure;
+        new_data.level = data->level + 1;
+
+        OSSL_TRACE_BEGIN(ENCODER) {
+            BIO_printf(trc_out,
+                       "[%d] (ctx %p) Considering encoder instance %p (encoder %p)\n",
+                       data->level, (void *)data->ctx,
+                       (void *)current_encoder_inst, (void *)current_encoder);
+        } OSSL_TRACE_END(ENCODER);
+
+        /*
+         * If this is the top call, we check if the output type of the current
+         * encoder matches the desired output type.
+         * If this isn't the top call, i.e. this is deeper in the recursion,
+         * we instead check if the output type of the current encoder matches
+         * the name of the next encoder (the one found by the parent call).
+         */
+        if (top) {
+            if (data->ctx->output_type != NULL
+                && strcasecmp(current_output_type,
+                              data->ctx->output_type) != 0) {
+                OSSL_TRACE_BEGIN(ENCODER) {
+                    BIO_printf(trc_out,
+                               "[%d]    Skipping because current encoder output type (%s) != desired output type (%s)\n",
+                               data->level,
+                               current_output_type, data->ctx->output_type);
+                } OSSL_TRACE_END(ENCODER);
+                continue;
+            }
         } else {
+            if (!OSSL_ENCODER_is_a(next_encoder, current_output_type)) {
+                OSSL_TRACE_BEGIN(ENCODER) {
+                    BIO_printf(trc_out,
+                               "[%d]    Skipping because current encoder output type (%s) != name of encoder %p\n",
+                               data->level,
+                               current_output_type, (void *)next_encoder);
+                } OSSL_TRACE_END(ENCODER);
+                continue;
+            }
+        }
+
+        /*
+         * If the caller and the current encoder specify an output structure,
+         * Check if they match.  If they do, count the match, otherwise skip
+         * the current encoder.
+         */
+        if (data->ctx->output_structure != NULL
+            && current_output_structure != NULL) {
+            if (strcasecmp(data->ctx->output_structure,
+                           current_output_structure) != 0) {
+                OSSL_TRACE_BEGIN(ENCODER) {
+                    BIO_printf(trc_out,
+                               "[%d]    Skipping because current encoder output structure (%s) != ctx output structure (%s)\n",
+                               data->level,
+                               current_output_structure,
+                               data->ctx->output_structure);
+                } OSSL_TRACE_END(ENCODER);
+                continue;
+            }
+
+            data->count_output_structure++;
+        }
+
+        /*
+         * Recurse to process the encoder implementations before the current
+         * one.
+         */
+        ok = encoder_process(&new_data);
+
+        data->prev_encoder_inst = new_data.prev_encoder_inst;
+        data->running_output = new_data.running_output;
+        data->running_output_length = new_data.running_output_length;
+
+        /*
+         * ok == -1     means that the recursion call above gave no further
+         *              encoders, and that the one we're currently at should
+         *              be tried.
+         * ok == 0      means that something failed in the recursion call
+         *              above, making the result unsuitable for a chain.
+         *              In this case, we simply continue to try finding a
+         *              suitable encoder at this recursion level.
+         * ok == 1      means that the recursion call was successful, and we
+         *              try to use the result at this recursion level.
+         */
+        if (ok != 0)
+            break;
+
+        OSSL_TRACE_BEGIN(ENCODER) {
+            BIO_printf(trc_out,
+                       "[%d]    Skipping because recusion level %d failed\n",
+                       data->level, new_data.level);
+        } OSSL_TRACE_END(ENCODER);
+    }
+
+    /*
+     * If |i < 0|, we didn't find any useful encoder in this recursion, so
+     * we do the rest of the process only if |i >= 0|.
+     */
+    if (i < 0) {
+        ok = -1;
+
+        OSSL_TRACE_BEGIN(ENCODER) {
+            BIO_printf(trc_out,
+                       "[%d] (ctx %p) No suitable encoder found\n",
+                       data->level, (void *)data->ctx);
+        } OSSL_TRACE_END(ENCODER);
+    } else {
+        /* Preparations */
+
+        switch (ok) {
+        case 0:
+            break;
+        case -1:
             /*
-             * Check that the latest output type matches the currently
-             * considered encoder
+             * We have reached the beginning of the encoder instance sequence,
+             * so we prepare the object to be encoded.
              */
-            if (!OSSL_ENCODER_is_a(encoder, latest_output_type))
-                continue;
 
             /*
-             * If there is a latest output type, there should be a latest output
+             * |data->count_output_structure| is one of these values:
+             *
+             * -1       There is no desired output structure
+             *  0       There is a desired output structure, and it wasn't
+             *          matched by any of the encoder instances that were
+             *          considered
+             * >0       There is a desired output structure, and at least one
+             *          of the encoder instances matched it
              */
-            if (!ossl_assert(latest_output != NULL)) {
+            if (data->count_output_structure == 0)
+                return 0;
+
+            original_data =
+                data->ctx->construct(current_encoder_inst,
+                                     data->ctx->construct_data);
+
+            /* Assume that the constructor recorded an error */
+            if (original_data != NULL)
+                ok = 1;
+            else
+                ok = 0;
+            break;
+        case 1:
+            if (!ossl_assert(data->running_output != NULL)) {
                 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
-                goto loop_end;
+                ok = 0;
+                break;
             }
 
-            /*
-             * Create an object abstraction from the latest output, which was
-             * stolen from the previous round.
-             */
-            abstract_p = abstract;
-            if (last_input_type != NULL)
+            {
+                /*
+                 * Create an object abstraction from the latest output, which
+                 * was stolen from the previous round.
+                 */
+
+                OSSL_PARAM *abstract_p = abstract;
+                const char *prev_input_type =
+                    OSSL_ENCODER_INSTANCE_get_input_type(data->prev_encoder_inst);
+                const char *prev_output_structure =
+                    OSSL_ENCODER_INSTANCE_get_output_structure(data->prev_encoder_inst);
+
+                if (prev_input_type != NULL)
+                    *abstract_p++ =
+                        OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                                         (char *)prev_input_type, 0);
+                if (prev_output_structure != NULL)
+                    *abstract_p++ =
+                        OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+                                                         (char *)prev_output_structure,
+                                                         0);
                 *abstract_p++ =
-                    OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
-                                                     (char *)last_input_type, 0);
-            *abstract_p++ =
-                OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
-                                                  latest_output,
-                                                  latest_output_length);
-            *abstract_p = OSSL_PARAM_construct_end();
-            current_abstract = abstract;
+                    OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+                                                      data->running_output,
+                                                      data->running_output_length);
+                *abstract_p = OSSL_PARAM_construct_end();
+                current_abstract = abstract;
+            }
+            break;
         }
 
-        /*
-         * If the desired output type matches the output type of the currently
-         * considered encoder, we're setting up final output.  Otherwise, set
-         * up an intermediary memory output.
-         */
-        if (strcasecmp(ctx->output_type, current_output_type) == 0)
-            current_out = out;
-        else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) == NULL)
-            goto loop_end;     /* Assume BIO_new() recorded an error */
+        /* Calling the encoder implementation */
 
-        ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out,
-                             current_data, current_abstract, ctx->selection,
-                             ossl_pw_passphrase_callback_enc, &ctx->pwdata);
+        if (ok) {
+            BIO *current_out = NULL;
 
-        if (current_input_type != NULL)
-            last_input_type = current_input_type;
+            /*
+             * If we're at the last encoder instance to use, we're setting up
+             * final output.  Otherwise, set up an intermediary memory output.
+             */
+            if (top)
+                current_out = data->bio;
+            else if ((current_out = allocated_out = BIO_new(BIO_s_mem()))
+                     == NULL)
+                ok = 0;     /* Assume BIO_new() recorded an error */
+
+            if (ok) {
+                ok = current_encoder->encode(current_encoder_ctx,
+                                             (OSSL_CORE_BIO *)current_out,
+                                             original_data, current_abstract,
+                                             data->ctx->selection,
+                                             ossl_pw_passphrase_callback_enc,
+                                             &data->ctx->pwdata);
+                OSSL_TRACE_BEGIN(ENCODER) {
+                    BIO_printf(trc_out,
+                               "[%d] (ctx %p) Running encoder instance %p => %d\n",
+                               data->level, (void *)data->ctx,
+                               (void *)current_encoder_inst, ok);
+                } OSSL_TRACE_END(ENCODER);
+            }
 
-        if (!ok)
-            goto loop_end;
+            data->prev_encoder_inst = current_encoder_inst;
+        }
+    }
 
-        OPENSSL_free(latest_output);
+    /* Cleanup and collecting the result */
 
-        /*
-         * Steal the output from the BIO_s_mem, if we did allocate one.
-         * That'll be the data for an object abstraction in the next round.
-         */
-        if (allocated_out != NULL) {
-            BUF_MEM *buf;
-
-            BIO_get_mem_ptr(allocated_out, &buf);
-            latest_output = buf->data;
-            latest_output_length = buf->length;
-            memset(buf, 0, sizeof(*buf));
-            BIO_free(allocated_out);
-        }
+    OPENSSL_free(data->running_output);
+    data->running_output = NULL;
 
-     loop_end:
-        if (current_data != NULL)
-            ctx->cleanup(ctx->construct_data);
+    /*
+     * Steal the output from the BIO_s_mem, if we did allocate one.
+     * That'll be the data for an object abstraction in the next round.
+     */
+    if (allocated_out != NULL) {
+        BUF_MEM *buf;
 
-        if (ok)
-            break;
+        BIO_get_mem_ptr(allocated_out, &buf);
+        data->running_output = (unsigned char *)buf->data;
+        data->running_output_length = buf->length;
+        memset(buf, 0, sizeof(*buf));
     }
 
-    OPENSSL_free(latest_output);
+    BIO_free(allocated_out);
+    if (original_data != NULL)
+        data->ctx->cleanup(data->ctx->construct_data);
     return ok;
 }
diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h
index 18cddf50fb..2ff1853b9f 100644
--- a/crypto/encode_decode/encoder_local.h
+++ b/crypto/encode_decode/encoder_local.h
@@ -33,6 +33,7 @@ struct ossl_encoder_st {
     OSSL_FUNC_encoder_gettable_params_fn *gettable_params;
     OSSL_FUNC_encoder_set_ctx_params_fn *set_ctx_params;
     OSSL_FUNC_encoder_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_FUNC_encoder_does_selection_fn *does_selection;
     OSSL_FUNC_encoder_encode_fn *encode;
     OSSL_FUNC_encoder_import_object_fn *import_object;
     OSSL_FUNC_encoder_free_object_fn *free_object;
@@ -52,10 +53,11 @@ struct ossl_decoder_st {
 };
 
 struct ossl_encoder_instance_st {
-    OSSL_ENCODER *encoder;       /* Never NULL */
-    void *encoderctx;            /* Never NULL */
-    const char *input_type;      /* May be NULL */
-    const char *output_type;     /* Never NULL */
+    OSSL_ENCODER *encoder;        /* Never NULL */
+    void *encoderctx;             /* Never NULL */
+    const char *input_type;       /* May be NULL */
+    const char *output_type;      /* Never NULL */
+    const char *output_structure; /* May be NULL */
 };
 
 DEFINE_STACK_OF(OSSL_ENCODER_INSTANCE)
@@ -63,11 +65,6 @@ DEFINE_STACK_OF(OSSL_ENCODER_INSTANCE)
 void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst);
 
 struct ossl_encoder_ctx_st {
-    /*
-     * The desired output type.  The encoder implementation have a gettable
-     * "output-type" parameter that this will match against.
-     */
-    const char *output_type;
     /*
      * Select what parts of an object will be encoded.  This selection is
      * bit encoded, and the bits correspond to selection bits available with
@@ -75,6 +72,17 @@ struct ossl_encoder_ctx_st {
      * the OSSL_KEYMGMT_SELECT_ macros are used for this.
      */
     int selection;
+    /*
+     * The desired output type.  The encoder implementation must have a
+     * gettable "output-type" parameter that this will match against.
+     */
+    const char *output_type;
+    /*
+     * The desired output structure, if that's relevant for the type of
+     * object being encoded.  It may be used for selection of the starting
+     * encoder implementations in a chain.
+     */
+    const char *output_structure;
 
     /*
      * Decoders that are components of any current decoding path.
diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c
index adff759bd4..99c4a119d3 100644
--- a/crypto/encode_decode/encoder_meth.c
+++ b/crypto/encode_decode/encoder_meth.c
@@ -200,6 +200,11 @@ static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
                 encoder->settable_ctx_params =
                     OSSL_FUNC_encoder_settable_ctx_params(fns);
             break;
+        case OSSL_FUNC_ENCODER_DOES_SELECTION:
+            if (encoder->does_selection == NULL)
+                encoder->does_selection =
+                    OSSL_FUNC_encoder_does_selection(fns);
+            break;
         case OSSL_FUNC_ENCODER_ENCODE:
             if (encoder->encode == NULL)
                 encoder->encode = OSSL_FUNC_encoder_encode(fns);
diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c
index b6f4cf777a..594543b19e 100644
--- a/crypto/encode_decode/encoder_pkey.c
+++ b/crypto/encode_decode/encoder_pkey.c
@@ -13,7 +13,9 @@
 #include <openssl/params.h>
 #include <openssl/encoder.h>
 #include <openssl/core_names.h>
+#include <openssl/provider.h>
 #include <openssl/safestack.h>
+#include <openssl/trace.h>
 #include "internal/provider.h"
 #include "internal/property.h"
 #include "crypto/evp.h"
@@ -70,40 +72,42 @@ int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
  */
 
 struct collected_encoder_st {
+    STACK_OF(OPENSSL_CSTRING) *names;
+    const char *output_structure;
     const char *output_type;
-    STACK_OF(OSSL_ENCODER) *encoders;
+
+    OSSL_ENCODER_CTX *ctx;
+
     int error_occured;
 };
 
 static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
 {
     struct collected_encoder_st *data = arg;
-    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
-    const char *output_type = NULL;
+    size_t i, end_i;
 
     if (data->error_occured)
         return;
 
-    /*
-     * Ask for the output type.  If the encoder doesn't answer to that,
-     * we refuse it.
-     */
-    params[0] =
-        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
-                                      (char **)&output_type, 0);
-    if (!encoder->get_params(params)
-        || !OSSL_PARAM_modified(&params[0])
-        || output_type == NULL
-        || strcasecmp(output_type, data->output_type) != 0)
+    data->error_occured = 1;     /* Assume the worst */
+
+    if (data->names == NULL)
         return;
 
-    data->error_occured = 1;         /* Assume the worst */
+    end_i = sk_OPENSSL_CSTRING_num(data->names);
+    for (i = 0; i < end_i; i++) {
+        const char *name = sk_OPENSSL_CSTRING_value(data->names, i);
+        const OSSL_PROVIDER *prov = OSSL_ENCODER_provider(encoder);
+        void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
 
-    if (!OSSL_ENCODER_up_ref(encoder) /* ref++ */)
-        return;
-    if (sk_OSSL_ENCODER_push(data->encoders, encoder) <= 0) {
-        OSSL_ENCODER_free(encoder);  /* ref-- */
-        return;
+        if (!OSSL_ENCODER_is_a(encoder, name)
+            || (encoder->does_selection != NULL
+                && !encoder->does_selection(provctx, data->ctx->selection)))
+            continue;
+
+        /* Only add each encoder implementation once */
+        if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
+            break;
     }
 
     data->error_occured = 0;         /* All is good now */
@@ -218,11 +222,8 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
     }
 
     if (pkey->keymgmt != NULL) {
-        OSSL_ENCODER *found = NULL;
-        const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(pkey->keymgmt);
         struct collected_encoder_st encoder_data;
         struct collected_names_st keymgmt_data;
-        int i;
 
         if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL) {
             ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
@@ -230,87 +231,28 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
         }
 
         /*
-         * Select the encoder in two steps.  First, collect all encoders
-         * that have the correct output type, as well as all keymgmt names.
+         * Select the first encoder implementations in two steps.
+         * First, collect the keymgmt names, then the encoders that match.
          */
-        encoder_data.output_type = ctx->output_type;
-        encoder_data.encoders = sk_OSSL_ENCODER_new_null();
-        encoder_data.error_occured = 0;
         keymgmt_data.names = sk_OPENSSL_CSTRING_new_null();
         keymgmt_data.error_occured = 0;
-        if (encoder_data.encoders == NULL || keymgmt_data.names == NULL) {
-            sk_OSSL_ENCODER_free(encoder_data.encoders);
-            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
-            return 0;
-        }
-        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
         EVP_KEYMGMT_names_do_all(pkey->keymgmt, collect_name, &keymgmt_data);
-
-        /*-
-         * Now we look for the most desirable encoder for our |pkey|.
-         *
-         * Encoders offer two functions:
-         *
-         * - one ('encode') that encodes a given provider-native object that
-         *   it knows intimately, so it must be from the same provider.
-         * - one ('import_object') that imports the parameters of an object
-         *   of the same type from a different provider, which is used to
-         *   create a temporary object that 'encode' can handle.
-         *
-         * It is, of course, more desirable to be able to use 'encode'
-         * directly without having to go through an export/import maneuver,
-         * but the latter allows us to have generic encoders.
-         *
-         * Of course, if |libctx| is different from |pkey|'s library context,
-         * we're going to have to do an export/import maneuvre no matter what.
-         */
-        for (i = 0; i < sk_OSSL_ENCODER_num(encoder_data.encoders); i++) {
-            OSSL_ENCODER *encoder =
-                sk_OSSL_ENCODER_value(encoder_data.encoders, i);
-            int j;
-
-            /* Check that any of the |keymgmt| names match */
-            for (j = 0; j < sk_OPENSSL_CSTRING_num(keymgmt_data.names); j++) {
-                const char *name =
-                    sk_OPENSSL_CSTRING_value(keymgmt_data.names, j);
-
-                if (OSSL_ENCODER_is_a(encoder, name))
-                    break;
-            }
-
-            if (j == sk_OPENSSL_CSTRING_num(keymgmt_data.names))
-                continue;
-
-            /* We found one!  Process it */
-            if (OSSL_ENCODER_provider(encoder) == desired_prov) {
-                /*
-                 * We found one in the same provider as the keymgmt.  Choose
-                 * it and stop looking.
-                 */
-                found = encoder;
-                break;
-            }
-            if (found == NULL && encoder->import_object != NULL) {
-                /*
-                 * We found one that's good enough.  Choose it for now, but
-                 * keep looking.
-                 */
-                found = encoder;
-            }
-        }
-
-        if (found != NULL) {
-            (void)OSSL_ENCODER_CTX_add_encoder(ctx, found);
-        } else {
-            if (encoder_data.error_occured)
-                ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
-            else
-                ERR_raise(ERR_LIB_OSSL_ENCODER,
-                          OSSL_ENCODER_R_ENCODER_NOT_FOUND);
+        if (keymgmt_data.error_occured) {
+            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
+            goto err;
         }
 
+        encoder_data.names = keymgmt_data.names;
+        encoder_data.output_type = ctx->output_type;
+        encoder_data.output_structure = ctx->output_structure;
+        encoder_data.error_occured = 0;
+        encoder_data.ctx = ctx;
+        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
         sk_OPENSSL_CSTRING_free(keymgmt_data.names);
-        sk_OSSL_ENCODER_pop_free(encoder_data.encoders, OSSL_ENCODER_free);
+        if (encoder_data.error_occured) {
+            ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
     }
 
     if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
@@ -335,8 +277,9 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
 }
 
 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                   const char *output_type,
                                                    int selection,
+                                                   const char *output_type,
+                                                   const char *output_struct,
                                                    OSSL_LIB_CTX *libctx,
                                                    const char *propquery)
 {
@@ -346,12 +289,28 @@ OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
+
+    OSSL_TRACE_BEGIN(ENCODER) {
+        BIO_printf(trc_out,
+                   "(ctx %p) Looking for %s encoders with selection %d\n",
+                   (void *)ctx, EVP_PKEY_get0_first_alg_name(pkey), selection);
+        BIO_printf(trc_out, "    output type: %s, output structure: %s\n",
+                   output_type, output_struct);
+    } OSSL_TRACE_END(ENCODER);
+
     if (OSSL_ENCODER_CTX_set_output_type(ctx, output_type)
+        && (output_struct == NULL
+            || OSSL_ENCODER_CTX_set_output_structure(ctx, output_struct))
         && OSSL_ENCODER_CTX_set_selection(ctx, selection)
         && ossl_encoder_ctx_setup_for_EVP_PKEY(ctx, pkey, selection,
                                                libctx, propquery)
-        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery))
+        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery)) {
+        OSSL_TRACE_BEGIN(ENCODER) {
+            BIO_printf(trc_out, "(ctx %p) Got %d encoders\n",
+                       (void *)ctx, OSSL_ENCODER_CTX_get_num_encoders(ctx));
+        } OSSL_TRACE_END(ENCODER);
         return ctx;
+    }
 
     OSSL_ENCODER_CTX_free(ctx);
     return NULL;
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 4eeb95e413..4b096ac17d 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -1186,7 +1186,7 @@ static int print_pkey(const EVP_PKEY *pkey, BIO *out, int indent,
     if (!print_set_indent(&out, &pop_f_prefix, &saved_indent, indent))
         return 0;
 
-    ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, "TEXT", selection,
+    ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection, "TEXT", NULL,
                                            libctx, propquery);
     if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0)
         ret = OSSL_ENCODER_to_bio(ctx, out);
diff --git a/crypto/pem/pem_local.h b/crypto/pem/pem_local.h
index f9575d4988..10761b03d3 100644
--- a/crypto/pem/pem_local.h
+++ b/crypto/pem/pem_local.h
@@ -32,12 +32,20 @@
 # define PEM_SELECTION_PrivateKey       EVP_PKEY_KEYPAIR
 # define PEM_SELECTION_Parameters       EVP_PKEY_KEY_PARAMETERS
 
+/*
+ * Properties, named according to the ASN.1 names used throughout libcrypto.
+ */
+# define PEM_STRUCTURE_PUBKEY "SubjectPublicKeyInfo"
+# define PEM_STRUCTURE_PrivateKey "pkcs8"
+# define PEM_STRUCTURE_Parameters "type-specific"
+
 /* Alternative IMPLEMENT macros for provided encoders */
 
 # define IMPLEMENT_PEM_provided_write_body_vars(type, asn1)             \
     int ret = 0;                                                        \
     OSSL_ENCODER_CTX *ctx =                                             \
-        OSSL_ENCODER_CTX_new_by_##type(x, "PEM", PEM_SELECTION_##asn1,  \
+        OSSL_ENCODER_CTX_new_by_##type(x, PEM_SELECTION_##asn1,         \
+                                       "PEM", PEM_STRUCTURE_##asn1,     \
                                        NULL, NULL);                     \
                                                                         \
     if (OSSL_ENCODER_CTX_get_num_encoders(ctx) == 0) {                  \
diff --git a/crypto/pem/pem_pk8.c b/crypto/pem/pem_pk8.c
index 2abf687cbd..797c9881d8 100644
--- a/crypto/pem/pem_pk8.c
+++ b/crypto/pem/pem_pk8.c
@@ -74,8 +74,8 @@ static int do_pk8pkey(BIO *bp, const EVP_PKEY *x, int isder, int nid,
     int ret = 0;
     const char *outtype = isder ? "DER" : "PEM";
     OSSL_ENCODER_CTX *ctx =
-        OSSL_ENCODER_CTX_new_by_EVP_PKEY(x, outtype, OSSL_KEYMGMT_SELECT_ALL,
-                                         libctx, propq);
+        OSSL_ENCODER_CTX_new_by_EVP_PKEY(x, OSSL_KEYMGMT_SELECT_ALL,
+                                         outtype, "pkcs8", libctx, propq);
 
     if (ctx == NULL)
         return 0;
diff --git a/crypto/trace.c b/crypto/trace.c
index 46a1800753..ba9b8dd742 100644
--- a/crypto/trace.c
+++ b/crypto/trace.c
@@ -137,6 +137,7 @@ static const struct trace_category_st trace_categories[] = {
     TRACE_CATEGORY_(BN_CTX),
     TRACE_CATEGORY_(STORE),
     TRACE_CATEGORY_(DECODER),
+    TRACE_CATEGORY_(ENCODER),
 };
 
 const char *OSSL_trace_get_category_name(int num)
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index b7dd04838c..b24ed8ff46 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -104,7 +104,8 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
         unsigned char *der = NULL;
         size_t derlen = 0;
         OSSL_ENCODER_CTX *ectx =
-            OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, "DER", EVP_PKEY_PUBLIC_KEY,
+            OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, EVP_PKEY_PUBLIC_KEY,
+                                             "DER", "SubjectPublicKeyInfo",
                                              libctx, NULL);
 
         if (OSSL_ENCODER_to_data(ectx, &der, &derlen)) {
@@ -309,7 +310,8 @@ int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
         const OSSL_PROVIDER *pkprov = EVP_KEYMGMT_provider(a->keymgmt);
         OSSL_LIB_CTX *libctx = ossl_provider_libctx(pkprov);
         OSSL_ENCODER_CTX *ctx =
-            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, "DER", EVP_PKEY_PUBLIC_KEY,
+            OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, EVP_PKEY_PUBLIC_KEY,
+                                             "DER", "SubjectPublicKeyInfo",
                                              libctx, NULL);
         BIO *out = BIO_new(BIO_s_mem());
         BUF_MEM *buf = NULL;
diff --git a/doc/man3/OSSL_ENCODER_CTX.pod b/doc/man3/OSSL_ENCODER_CTX.pod
index e8771fc087..d4ea01060f 100644
--- a/doc/man3/OSSL_ENCODER_CTX.pod
+++ b/doc/man3/OSSL_ENCODER_CTX.pod
@@ -7,8 +7,9 @@ OSSL_ENCODER_CTX_new,
 OSSL_ENCODER_settable_ctx_params,
 OSSL_ENCODER_CTX_set_params,
 OSSL_ENCODER_CTX_free,
-OSSL_ENCODER_CTX_set_output_type,
 OSSL_ENCODER_CTX_set_selection,
+OSSL_ENCODER_CTX_set_output_type,
+OSSL_ENCODER_CTX_set_output_structure,
 OSSL_ENCODER_CTX_add_encoder,
 OSSL_ENCODER_CTX_add_extra,
 OSSL_ENCODER_CTX_get_num_encoders,
@@ -17,6 +18,7 @@ OSSL_ENCODER_INSTANCE_get_encoder,
 OSSL_ENCODER_INSTANCE_get_encoder_ctx,
 OSSL_ENCODER_INSTANCE_get_input_type,
 OSSL_ENCODER_INSTANCE_get_output_type,
+OSSL_ENCODER_INSTANCE_get_output_structure,
 OSSL_ENCODER_CONSTRUCT,
 OSSL_ENCODER_CLEANUP,
 OSSL_ENCODER_CTX_set_construct,
@@ -36,9 +38,11 @@ OSSL_ENCODER_CTX_set_cleanup
                                  const OSSL_PARAM params[]);
  void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx);
 
+ int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
  int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
                                       const char *output_type);
- int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
+ int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx,
+                                           const char *output_structure);
 
  int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder);
  int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
@@ -54,6 +58,8 @@ OSSL_ENCODER_CTX_set_cleanup
  OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst);
  const char *
  OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+ const char *
+ OSSL_ENCODER_INSTANCE_get_output_structure(OSSL_ENCODER_INSTANCE *encoder_inst);
 
  typedef const void *OSSL_ENCODER_CONSTRUCT(OSSL_ENCODER_INSTANCE *encoder_inst,
                                             void *construct_data);
@@ -110,8 +116,13 @@ encoder chains.
 OSSL_ENCODER_CTX_set_output_type() sets the ending output type.  This must
 be specified, and determines if a complete encoder chain is available.
 
-OSSL_ENCODER_CTX_num_encoders() gets the number of encoders currently added
-to the context I<ctx>.
+OSSL_ENCODER_CTX_set_output_structure() sets the desired output structure.
+This may be used to determines what encoder implementations may be used.
+Depending on the type of object being encoded, the output structure may
+not be relevant.
+
+OSSL_ENCODER_CTX_get_num_encoders() gets the number of encoders currently
+added to the context I<ctx>.
 
 OSSL_ENCODER_CTX_set_construct() sets the constructor I<construct>.
 
@@ -144,18 +155,28 @@ or NULL to indicate that an error has occured.
 
 These utility functions may be used by a constructor:
 
-OSSL_ENCODER_INSTANCE_encoder() can be used to get the encoder method from a
-encoder instance I<encoder_inst>.
+OSSL_ENCODER_INSTANCE_get_encoder() can be used to get the encoder
+implementation of the encoder instance I<encoder_inst>.
+
+OSSL_ENCODER_INSTANCE_get_encoder_ctx() can be used to get the encoder
+implementation's provider context of the encoder instance I<encoder_inst>.
 
-OSSL_ENCODER_INSTANCE_encoder_ctx() can be used to get the encoder method's
-provider context from a encoder instance I<encoder_inst>.
+OSSL_ENCODER_INSTANCE_get_input_type() can be used to get the input type for
+the encoder implementation of the encoder instance I<encoder_inst>.
+This may be NULL.
 
-OSSL_ENCODER_INSTANCE_input_type() can be used to get the input type for
-encoder method from a encoder instance I<encoder_inst>.  This may be NULL.
+OSSL_ENCODER_INSTANCE_get_output_type() can be used to get the output type
+for the encoder implementation of the encoder instance I<encoder_inst>.
+This will never be NULL.
 
-OSSL_ENCODER_INSTANCE_output_type() can be used to get the output type for
-encoder method from a encoder instance I<encoder_inst>.  This will never be
-NULL.
+OSSL_ENCODER_INSTANCE_get_output_type() can be used to get the output type
+for the encoder implementation of the encoder instance I<encoder_inst>.
+This will never be NULL.
+
+OSSL_ENCODER_INSTANCE_get_output_structure() can be used to get the output
+structure for the encoder implementation of the encoder instance
+I<encoder_inst>.
+This may be NULL.
 
 =head1 RETURN VALUES
 
@@ -169,25 +190,28 @@ OSSL_ENCODER_CTX_set_params() returns 1 if all recognised parameters were
 valid, or 0 if one of them was invalid or caused some other failure in the
 implementation.
 
-OSSL_DECODER_CTX_add_decoder(), OSSL_DECODER_CTX_add_extra(),
-OSSL_DECODER_CTX_set_construct(), OSSL_DECODER_CTX_set_construct_data() and
-OSSL_DECODER_CTX_set_cleanup() return 1 on success, or 0 on failure.
+OSSL_ENCODER_CTX_add_encoder(), OSSL_ENCODER_CTX_add_extra(),
+OSSL_ENCODER_CTX_set_construct(), OSSL_ENCODER_CTX_set_construct_data() and
+OSSL_ENCODER_CTX_set_cleanup() return 1 on success, or 0 on failure.
 
-OSSL_DECODER_CTX_num_decoders() returns the current number of decoders.  It
-returns 0 if I<ctx> is NULL.
+OSSL_ENCODER_CTX_get_num_encoders() returns the current number of encoders.
+It returns 0 if I<ctx> is NULL.
 
-OSSL_DECODER_INSTANCE_decoder() returns an B<OSSL_DECODER> pointer on
+OSSL_ENCODER_INSTANCE_get_encoder() returns an B<OSSL_ENCODER> pointer on
 success, or NULL on failure.
 
-OSSL_DECODER_INSTANCE_decoder_ctx() returns a provider context pointer on
+OSSL_ENCODER_INSTANCE_get_encoder_ctx() returns a provider context pointer on
 success, or NULL on failure.
 
-OSSL_ENCODER_INSTANCE_input_type() returns a string with the name of the
+OSSL_ENCODER_INSTANCE_get_output_type() returns a string with the name of the
 input type, if relevant.  NULL is a valid returned value.
 
-OSSL_ENCODER_INSTANCE_output_type() returns a string with the name of the
+OSSL_ENCODER_INSTANCE_get_output_type() returns a string with the name of the
 output type.
 
+OSSL_ENCODER_INSTANCE_get_output_structure() returns a string with the name
+of the output structure.
+
 =head1 SEE ALSO
 
 L<provider(7)>, L<OSSL_ENCODER(3)>
diff --git a/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
index f90d13d551..97ffaa56cd 100644
--- a/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
+++ b/doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
@@ -15,8 +15,9 @@ OSSL_ENCODER_CTX_set_passphrase_ui
  #include <openssl/encoder.h>
 
  OSSL_ENCODER_CTX *
- OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                  const char *output_type, int selection,
+ OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, int selection,
+                                  const char *output_type,
+                                  const char *output_structure,
                                   OSSL_LIB_CTX *libctx, const char *propquery);
 
  int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
@@ -48,7 +49,8 @@ L</Selections>.
 Internally, OSSL_ENCODER_CTX_new_by_EVP_PKEY() uses the names from the
 L<EVP_KEYMGMT(3)> implementation associated with I<pkey> to build a list of
 applicable encoder implementations that are used to process the I<pkey> into
-the encoding named by I<output_type>.  All these implementations are
+the encoding named by I<output_type>, with the outermost structure named by
+I<output_structure> if that's relevant.  All these implementations are
 implicitly fetched using I<libctx> and I<propquery>.
 
 If no suitable encoder implementation is found,
diff --git a/doc/man7/provider-encoder.pod b/doc/man7/provider-encoder.pod
index 8048458b94..2fcbd6499a 100644
--- a/doc/man7/provider-encoder.pod
+++ b/doc/man7/provider-encoder.pod
@@ -24,6 +24,9 @@ provider-encoder - The OSSL_ENCODER library E<lt>-E<gt> provider functions
  int OSSL_FUNC_encoder_set_ctx_params(void *ctx, const OSSL_PARAM params[]);
  const OSSL_PARAM *OSSL_FUNC_encoder_settable_ctx_params(void *provctx)
 
+ /* Functions to check selection support */
+ int OSSL_FUNC_encoder_does_selection(void *provctx, int selection);
+
  /* Functions to encode object data */
  int OSSL_FUNC_encoder_encode(void *ctx, OSSL_CORE_BIO *out,
                               const void *obj_raw,
@@ -111,6 +114,8 @@ macros in L<openssl-core_dispatch.h(7)>, as follows:
  OSSL_FUNC_encoder_set_ctx_params      OSSL_FUNC_ENCODER_SET_CTX_PARAMS
  OSSL_FUNC_encoder_settable_ctx_params OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS
 
+ OSSL_FUNC_encoder_does_selection      OSSL_FUNC_ENCODER_DOES_SELECTION
+
  OSSL_FUNC_encoder_encode              OSSL_FUNC_ENCODER_ENCODE_DATA
 
  OSSL_FUNC_encoder_import_object       OSSL_FUNC_ENCODER_IMPORT_OBJECT
@@ -171,13 +176,16 @@ be encoded, with a set of bits I<selection> that are passed in an B<int>.
 This set of bits depend entirely on what kind of provider-side object is
 passed.  For example, those bits are assumed to be the same as those used
 with L<provider-keymgmt(7)> (see L<provider-keymgmt(7)/Key Objects>) when
-the object is an asymmetric key.
+the object is an asymmetric keypair.
 
 ENCODER implementations are free to regard the I<selection> as a set of
 hints, but must do so with care.  In the end, the output must make sense,
 and if there's a corresponding decoder, the resulting decoded object must
 match the original object that was encoded.
 
+OSSL_FUNC_encoder_does_selection() should tell if a particular implementation
+supports any of the combinations given by I<selection>.
+
 =head2 Context functions
 
 OSSL_FUNC_encoder_newctx() returns a context to be used with the rest of
@@ -215,8 +223,6 @@ OSSL_FUNC_encoder_import_object().
 
 =head2 Encoding functions
 
-=for comment There will be a "Decoding functions" title as well
-
 OSSL_FUNC_encoder_encode() should take an provider-native object (in
 I<obj_raw>) or an object abstraction (in I<obj_abstract>), and should output
 the object in encoded form to the B<OSSL_CORE_BIO>.  The I<selection> bits,
@@ -251,6 +257,19 @@ This parameter is I<mandatory>.
 in a set of properties, it would be possible to determine the output type
 from the C<output> property.
 
+=item "output-structure" (B<OSSL_ENCODER_PARAM_OUTPUT_STRUCTURE>) <UTF8 string>
+
+This is used to specify the outermost output structure for an ENCODER
+implementation.
+
+For example, an output of type "DER" for a key pair could be structured
+using PKCS#8, or a key type specific structure, such as PKCS#1 for RSA
+keys.
+
+=for comment If we had functionality to get the value of a specific property
+in a set of properties, it would be possible to determine the output
+structure from the C<structure> property.
+
 =back
 
 =head2 Encoder operation parameters
@@ -308,6 +327,9 @@ parameters was invalid or caused an error, for which 0 is returned.
 OSSL_FUNC_encoder_settable_ctx_params() returns a pointer to an array of
 constant B<OSSL_PARAM> elements.
 
+OSSL_FUNC_encoder_does_selection() returns 1 if the encoder implementation
+supports any of the I<selection> bits, otherwise 0.
+
 OSSL_FUNC_encoder_encode() return 1 on success, or 0 on failure.
 
 =head1 SEE ALSO
diff --git a/doc/man7/provider-object.pod b/doc/man7/provider-object.pod
index 63332b4f9b..2380dd4b17 100644
--- a/doc/man7/provider-object.pod
+++ b/doc/man7/provider-object.pod
@@ -153,6 +153,11 @@ data.
 is either missing or has the value OSSL_OBJECT_UNKNOWN), libcrypto
 interprets the object data type as the input type for a decoder.
 
+=item "data-structure" (B<OSSL_OBJECT_PARAM_DATA_STRUCTURE>) <utf8 string>
+
+The outermost structure of the object content.  Legitimate values depend on
+the object type.
+
 =item "desc" (B<OSSL_OBJECT_PARAM_DESC>) <utf8 string>
 
 A human readable text that describes extra details on the object.
diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h
index cc8e6712ed..d4d581df57 100644
--- a/include/openssl/core_dispatch.h
+++ b/include/openssl/core_dispatch.h
@@ -756,7 +756,8 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, kem_settable_ctx_params, (void *provctx)
 # define OSSL_FUNC_ENCODER_GETTABLE_PARAMS             4
 # define OSSL_FUNC_ENCODER_SET_CTX_PARAMS              5
 # define OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS         6
-# define OSSL_FUNC_ENCODER_ENCODE                     10
+# define OSSL_FUNC_ENCODER_DOES_SELECTION             10
+# define OSSL_FUNC_ENCODER_ENCODE                     11
 # define OSSL_FUNC_ENCODER_IMPORT_OBJECT              20
 # define OSSL_FUNC_ENCODER_FREE_OBJECT                21
 OSSL_CORE_MAKE_FUNC(void *, encoder_newctx, (void *provctx))
@@ -769,10 +770,8 @@ OSSL_CORE_MAKE_FUNC(int, encoder_set_ctx_params,
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, encoder_settable_ctx_params,
                     (void *provctx))
 
-/*
- * TODO(3.0) investigate if this should be two functions, one that takes a
- * raw object and one that takes an object abstraction.
- */
+OSSL_CORE_MAKE_FUNC(int, encoder_does_selection,
+                    (void *provctx, int selection))
 OSSL_CORE_MAKE_FUNC(int, encoder_encode,
                     (void *ctx, OSSL_CORE_BIO *out,
                      const void *obj_raw, const OSSL_PARAM obj_abstract[],
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 11a4168cc1..f0b0cd0163 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -42,11 +42,12 @@ extern "C" {
  * This set of parameter names is explained in detail in provider-object(7)
  * (doc/man7/provider-object.pod)
  */
-#define OSSL_OBJECT_PARAM_TYPE       "type"      /* INTEGER */
-#define OSSL_OBJECT_PARAM_DATA_TYPE  "data-type" /* UTF8_STRING */
-#define OSSL_OBJECT_PARAM_REFERENCE  "reference" /* OCTET_STRING */
-#define OSSL_OBJECT_PARAM_DATA       "data" /* OCTET_STRING or UTF8_STRING */
-#define OSSL_OBJECT_PARAM_DESC       "desc"      /* UTF8_STRING */
+#define OSSL_OBJECT_PARAM_TYPE              "type"      /* INTEGER */
+#define OSSL_OBJECT_PARAM_DATA_TYPE         "data-type" /* UTF8_STRING */
+#define OSSL_OBJECT_PARAM_DATA_STRUCTURE    "data-structure" /* UTF8_STRING */
+#define OSSL_OBJECT_PARAM_REFERENCE         "reference" /* OCTET_STRING */
+#define OSSL_OBJECT_PARAM_DATA              "data" /* OCTET_STRING or UTF8_STRING */
+#define OSSL_OBJECT_PARAM_DESC              "desc"      /* UTF8_STRING */
 
 /*
  * Algorithm parameters
@@ -452,10 +453,11 @@ extern "C" {
 /*
  * Encoder / decoder parameters
  */
-#define OSSL_ENCODER_PARAM_CIPHER       OSSL_ALG_PARAM_CIPHER
-#define OSSL_ENCODER_PARAM_PROPERTIES   OSSL_ALG_PARAM_PROPERTIES
-#define OSSL_ENCODER_PARAM_INPUT_TYPE   "input-type"
-#define OSSL_ENCODER_PARAM_OUTPUT_TYPE  "output-type"
+#define OSSL_ENCODER_PARAM_CIPHER           OSSL_ALG_PARAM_CIPHER
+#define OSSL_ENCODER_PARAM_PROPERTIES       OSSL_ALG_PARAM_PROPERTIES
+#define OSSL_ENCODER_PARAM_INPUT_TYPE       "input-type"
+#define OSSL_ENCODER_PARAM_OUTPUT_TYPE      "output-type"
+#define OSSL_ENCODER_PARAM_OUTPUT_STRUCTURE "output-structure"
 
 #define OSSL_DECODER_PARAM_PROPERTIES       OSSL_ALG_PARAM_PROPERTIES
 #define OSSL_DECODER_PARAM_INPUT_TYPE       "input-type"
diff --git a/include/openssl/encoder.h b/include/openssl/encoder.h
index 669f688b2d..f50d16517e 100644
--- a/include/openssl/encoder.h
+++ b/include/openssl/encoder.h
@@ -65,9 +65,11 @@ int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
 int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
                                 const char *cipher_name,
                                 const char *propquery);
+int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
 int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
                                      const char *output_type);
-int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection);
+int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx,
+                                          const char *output_structure);
 
 /* Utilities to add encoders */
 int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder);
@@ -84,6 +86,8 @@ const char *
 OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst);
 const char *
 OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst);
+const char *
+OSSL_ENCODER_INSTANCE_get_output_structure(OSSL_ENCODER_INSTANCE *encoder_inst);
 
 typedef const void *OSSL_ENCODER_CONSTRUCT(OSSL_ENCODER_INSTANCE *encoder_inst,
                                            void *construct_data);
@@ -110,8 +114,9 @@ int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata,
  * This is more useful than calling OSSL_ENCODER_CTX_new().
  */
 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                   const char *output_type,
                                                    int selection,
+                                                   const char *output_type,
+                                                   const char *output_struct,
                                                    OSSL_LIB_CTX *libctx,
                                                    const char *propquery);
 
diff --git a/include/openssl/trace.h b/include/openssl/trace.h
index d3e1e95df2..a0894ee2a4 100644
--- a/include/openssl/trace.h
+++ b/include/openssl/trace.h
@@ -54,7 +54,8 @@ extern "C" {
 # define OSSL_TRACE_CATEGORY_CMP                13
 # define OSSL_TRACE_CATEGORY_STORE              14
 # define OSSL_TRACE_CATEGORY_DECODER            15
-# define OSSL_TRACE_CATEGORY_NUM                16
+# define OSSL_TRACE_CATEGORY_ENCODER            16
+# define OSSL_TRACE_CATEGORY_NUM                17
 
 /* Returns the trace category number for the given |name| */
 int OSSL_trace_get_category_num(const char *name);
diff --git a/providers/baseprov.c b/providers/baseprov.c
index 18d664aa29..c63f1fb257 100644
--- a/providers/baseprov.c
+++ b/providers/baseprov.c
@@ -69,15 +69,11 @@ static int base_get_params(void *provctx, OSSL_PARAM params[])
 }
 
 static const OSSL_ALGORITHM base_encoder[] = {
-#define ENCODER(name, _fips, _output, func_table)                           \
-    { name,                                                                 \
-      "provider=base,fips=" _fips ",output=" _output,                       \
-      (func_table) }
-
+#define ENCODER_PROVIDER "base"
 #include "encoders.inc"
     { NULL, NULL, NULL }
+#undef ENCODER_PROVIDER
 };
-#undef ENCODER
 
 static const OSSL_ALGORITHM base_decoder[] = {
 #define DECODER_PROVIDER "base"
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 8ce6c92b3e..b309ba798c 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -442,15 +442,11 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
 };
 
 static const OSSL_ALGORITHM deflt_encoder[] = {
-#define ENCODER(name, _fips, _output, func_table)                           \
-    { name,                                                                 \
-      "provider=default,fips=" _fips ",output=" _output,                    \
-      (func_table) }
-
+#define ENCODER_PROVIDER "default"
 #include "encoders.inc"
     { NULL, NULL, NULL }
+#undef ENCODER_PROVIDER
 };
-#undef ENCODER
 
 static const OSSL_ALGORITHM deflt_decoder[] = {
 #define DECODER_PROVIDER "default"
diff --git a/providers/encoders.inc b/providers/encoders.inc
index 1ae0d4fb4c..9330bf426a 100644
--- a/providers/encoders.inc
+++ b/providers/encoders.inc
@@ -7,51 +7,211 @@
  * https://www.openssl.org/source/license.html
  */
 
-#ifndef ENCODER
-# error Macro ENCODER undefined
+#ifndef ENCODER_PROVIDER
+# error Macro ENCODER_PROVIDER undefined
 #endif
 
-    ENCODER("RSA", "yes", "text", ossl_rsa_to_text_encoder_functions),
-    ENCODER("RSA", "yes", "der", ossl_rsa_to_der_encoder_functions),
-    ENCODER("RSA", "yes", "pem", ossl_rsa_to_pem_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "text", ossl_rsapss_to_text_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "der", ossl_rsapss_to_der_encoder_functions),
-    ENCODER("RSA-PSS", "yes", "pem", ossl_rsapss_to_pem_encoder_functions),
+#define ENCODER_STRUCTURE_type_specific_keypair         "type-specific"
+#define ENCODER_STRUCTURE_type_specific_params          "type-specific"
+#define ENCODER_STRUCTURE_type_specific                 "type-specific"
+#define ENCODER_STRUCTURE_type_specific_no_pub          "type-specific"
+#define ENCODER_STRUCTURE_PKCS8                         "pkcs8"
+#define ENCODER_STRUCTURE_SubjectPublicKeyInfo          "SubjectPublicKeyInfo"
+#define ENCODER_STRUCTURE_DH                            "dh"
+#define ENCODER_STRUCTURE_DHX                           "dhx"
+#define ENCODER_STRUCTURE_DSA                           "dsa"
+#define ENCODER_STRUCTURE_EC                            "ec"
+#define ENCODER_STRUCTURE_RSA                           "rsa"
+#define ENCODER_STRUCTURE_PKCS1                         "pkcs1"
+#define ENCODER_STRUCTURE_PKCS3                         "pkcs3"
+#define ENCODER_STRUCTURE_X9_42                         "X9.42"
+#define ENCODER_STRUCTURE_X9_62                         "X9.62"
+
+/* Arguments are prefixed with '_' to avoid build breaks on certain platforms */
+#define ENCODER_TEXT(_name, _sym, _fips)                                \
+    { _name,                                                            \
+      "provider=" ENCODER_PROVIDER ",fips=" #_fips ",output=text",      \
+      (ossl_##_sym##_to_text_encoder_functions) }
+#define ENCODER(_name, _sym, _fips, _output, _structure)                \
+    { _name,                                                            \
+      "provider=" ENCODER_PROVIDER ",fips=" #_fips ",output=" #_output  \
+      ",structure=" ENCODER_STRUCTURE_##_structure,                     \
+      (ossl_##_sym##_to_##_structure##_##_output##_encoder_functions) }
+
+/*
+ * Entries for human text "encoders"
+ */
+ENCODER_TEXT("RSA", rsa, yes),
+ENCODER_TEXT("RSA-PSS", rsapss, yes),
+#ifndef OPENSSL_NO_DH
+ENCODER_TEXT("DH", dh, yes),
+ENCODER_TEXT("DHX", dhx, yes),
+#endif
+#ifndef OPENSSL_NO_DSA
+ENCODER_TEXT("DSA", dsa, yes),
+#endif
+#ifndef OPENSSL_NO_EC
+ENCODER_TEXT("EC", ec, yes),
+ENCODER_TEXT("ED25519", ed25519, yes),
+ENCODER_TEXT("ED448", ed448, yes),
+ENCODER_TEXT("X25519", x25519, yes),
+ENCODER_TEXT("X448", x448, yes),
+#endif
+
+/*
+ * Entries for key type specific output formats.  The structure name on these
+ * is the same as the key type name.  This allows us to say something like:
+ *
+ * To replace i2d_{TYPE}PrivateKey(), i2d_{TYPE}PublicKey() and
+ * i2d_{TYPE}Params(), use OSSL_ENCODER functions with an OSSL_ENCODER_CTX
+ * created like this:
+ *
+ * OSSL_ENCODER_CTX *ctx =
+ *     OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection, "DER", "type-specific",
+ *                                      NULL, NULL);
+ *
+ * To replace PEM_write_bio_{TYPE}PrivateKey(), PEM_write_bio_{TYPE}PublicKey()
+ * and PEM_write_bio_{TYPE}Params(), use OSSL_ENCODER functions with an
+ * OSSL_ENCODER_CTX created like this:
+ *
+ * OSSL_ENCODER_CTX *ctx =
+ *     OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection, "PEM", "type-specific",
+ *                                      NULL, NULL);
+ *
+ * We only implement those for which there are current i2d_ and PEM_write_bio
+ * implementations.
+ */
+
+/* The RSA encoders only support private key and public key output */
+ENCODER("RSA", rsa, yes, der, type_specific_keypair),
+ENCODER("RSA", rsa, yes, pem, type_specific_keypair),
+#ifndef OPENSSL_NO_DH
+/* DH and X9.42 DH only support key parameters output. */
+ENCODER("DH", dh, yes, der, type_specific_params),
+ENCODER("DH", dh, yes, pem, type_specific_params),
+ENCODER("DHX", dhx, yes, der, type_specific_params),
+ENCODER("DHX", dhx, yes, pem, type_specific_params),
+#endif
+#ifndef OPENSSL_NO_DSA
+ENCODER("DSA", dsa, yes, der, type_specific),
+ENCODER("DSA", dsa, yes, pem, type_specific),
+#endif
+#ifndef OPENSSL_NO_EC
+/* EC only supports keypair and parameters output. */
+ENCODER("EC", ec, yes, der, type_specific_no_pub),
+ENCODER("EC", ec, yes, pem, type_specific_no_pub),
+#endif
+
+/*
+ * Entries for PKCS#8 and SubjectPublicKeyInfo.
+ * The "der" ones are added convenience for any user that wants to use
+ * OSSL_ENCODER directly.
+ * The "pem" ones also support PEM_write_bio_PrivateKey() and
+ * PEM_write_bio_PUBKEY().
+ */
+ENCODER("RSA", rsa, yes, der, PKCS8),
+ENCODER("RSA", rsa, yes, pem, PKCS8),
+ENCODER("RSA", rsa, yes, der, SubjectPublicKeyInfo),
+ENCODER("RSA", rsa, yes, pem, SubjectPublicKeyInfo),
+
+ENCODER("RSA-PSS", rsapss, yes, der, PKCS8),
+ENCODER("RSA-PSS", rsapss, yes, pem, PKCS8),
+ENCODER("RSA-PSS", rsapss, yes, der, SubjectPublicKeyInfo),
+ENCODER("RSA-PSS", rsapss, yes, pem, SubjectPublicKeyInfo),
 
 #ifndef OPENSSL_NO_DH
-    ENCODER("DH", "yes", "text", ossl_dh_to_text_encoder_functions),
-    ENCODER("DH", "yes", "der", ossl_dh_to_der_encoder_functions),
-    ENCODER("DH", "yes", "pem", ossl_dh_to_pem_encoder_functions),
+ENCODER("DH", dh, yes, der, PKCS8),
+ENCODER("DH", dh, yes, pem, PKCS8),
+ENCODER("DH", dh, yes, der, SubjectPublicKeyInfo),
+ENCODER("DH", dh, yes, pem, SubjectPublicKeyInfo),
 
-    ENCODER("DHX", "yes", "text", ossl_dhx_to_text_encoder_functions),
-    ENCODER("DHX", "yes", "der", ossl_dhx_to_der_encoder_functions),
-    ENCODER("DHX", "yes", "pem", ossl_dhx_to_pem_encoder_functions),
+ENCODER("DHX", dhx, yes, der, PKCS8),
+ENCODER("DHX", dhx, yes, pem, PKCS8),
+ENCODER("DHX", dhx, yes, der, SubjectPublicKeyInfo),
+ENCODER("DHX", dhx, yes, pem, SubjectPublicKeyInfo),
 #endif
 
 #ifndef OPENSSL_NO_DSA
-    ENCODER("DSA", "yes", "text", ossl_dsa_to_text_encoder_functions),
-    ENCODER("DSA", "yes", "der", ossl_dsa_to_der_encoder_functions),
-    ENCODER("DSA", "yes", "pem", ossl_dsa_to_pem_encoder_functions),
+ENCODER("DSA", dsa, yes, der, PKCS8),
+ENCODER("DSA", dsa, yes, pem, PKCS8),
+ENCODER("DSA", dsa, yes, der, SubjectPublicKeyInfo),
+ENCODER("DSA", dsa, yes, pem, SubjectPublicKeyInfo),
 #endif
 
 #ifndef OPENSSL_NO_EC
-    ENCODER("X25519", "yes", "text", ossl_x25519_to_text_encoder_functions),
-    ENCODER("X25519", "yes", "der", ossl_x25519_to_der_encoder_functions),
-    ENCODER("X25519", "yes", "pem", ossl_x25519_to_pem_encoder_functions),
+ENCODER("EC", ec, yes, der, PKCS8),
+ENCODER("EC", ec, yes, pem, PKCS8),
+ENCODER("EC", ec, yes, der, SubjectPublicKeyInfo),
+ENCODER("EC", ec, yes, pem, SubjectPublicKeyInfo),
 
-    ENCODER("X448", "yes", "text", ossl_x448_to_text_encoder_functions),
-    ENCODER("X448", "yes", "der", ossl_x448_to_der_encoder_functions),
-    ENCODER("X448", "yes", "pem", ossl_x448_to_pem_encoder_functions),
+ENCODER("X25519", x25519, yes, der, PKCS8),
+ENCODER("X25519", x25519, yes, pem, PKCS8),
+ENCODER("X25519", x25519, yes, der, SubjectPublicKeyInfo),
+ENCODER("X25519", x25519, yes, pem, SubjectPublicKeyInfo),
 
-    ENCODER("ED25519", "yes", "text", ossl_ed25519_to_text_encoder_functions),
-    ENCODER("ED25519", "yes", "der", ossl_ed25519_to_der_encoder_functions),
-    ENCODER("ED25519", "yes", "pem", ossl_ed25519_to_pem_encoder_functions),
+ENCODER("X448", x448, yes, der, PKCS8),
+ENCODER("X448", x448, yes, pem, PKCS8),
+ENCODER("X448", x448, yes, der, SubjectPublicKeyInfo),
+ENCODER("X448", x448, yes, pem, SubjectPublicKeyInfo),
 
-    ENCODER("ED448", "yes", "text", ossl_ed448_to_text_encoder_functions),
-    ENCODER("ED448", "yes", "der", ossl_ed448_to_der_encoder_functions),
-    ENCODER("ED448", "yes", "pem", ossl_ed448_to_pem_encoder_functions),
+ENCODER("ED25519", ed25519, yes, der, PKCS8),
+ENCODER("ED25519", ed25519, yes, pem, PKCS8),
+ENCODER("ED25519", ed25519, yes, der, SubjectPublicKeyInfo),
+ENCODER("ED25519", ed25519, yes, pem, SubjectPublicKeyInfo),
 
-    ENCODER("EC", "yes", "text", ossl_ec_to_text_encoder_functions),
-    ENCODER("EC", "yes", "der", ossl_ec_to_der_encoder_functions),
-    ENCODER("EC", "yes", "pem", ossl_ec_to_pem_encoder_functions),
+ENCODER("ED448", ed448, yes, der, PKCS8),
+ENCODER("ED448", ed448, yes, pem, PKCS8),
+ENCODER("ED448", ed448, yes, der, SubjectPublicKeyInfo),
+ENCODER("ED448", ed448, yes, pem, SubjectPublicKeyInfo),
+#endif
+
+/*
+ * Entries for key type specific output formats.  These are exactly the
+ * same as the type specific above, except that they use the key type
+ * name as structure name instead of "type-specific", in the call on
+ * OSSL_ENCODER_CTX_new_by_EVP_PKEY().
+ */
+
+/* The RSA encoders only support private key and public key output */
+ENCODER("RSA", rsa, yes, der, RSA),
+ENCODER("RSA", rsa, yes, pem, RSA),
+#ifndef OPENSSL_NO_DH
+/* DH and X9.42 DH only support key parameters output. */
+ENCODER("DH", dh, yes, der, DH),
+ENCODER("DH", dh, yes, pem, DH),
+ENCODER("DHX", dhx, yes, der, DHX),
+ENCODER("DHX", dhx, yes, pem, DHX),
+#endif
+#ifndef OPENSSL_NO_DSA
+ENCODER("DSA", dsa, yes, der, DSA),
+ENCODER("DSA", dsa, yes, pem, DSA),
+#endif
+#ifndef OPENSSL_NO_EC
+ENCODER("EC", ec, yes, der, EC),
+ENCODER("EC", ec, yes, pem, EC),
+#endif
+
+/*
+ * Additional entries with structure names being the standard name.
+ * This is entirely for the convenience of the user that wants to use
+ * OSSL_ENCODER directly with names they may fancy.  These do not impact
+ * on libcrypto functionality in any way.
+ */
+/* PKCS#1 is a well known for plain RSA keys, so we add that too */
+ENCODER("RSA", rsa, yes, der, PKCS1),
+ENCODER("RSA", rsa, yes, pem, PKCS1),
+ENCODER("RSA-PSS", rsapss, yes, der, PKCS1),
+ENCODER("RSA-PSS", rsapss, yes, pem, PKCS1),
+#ifndef OPENSSL_NO_DH
+/* PKCS#3 defines the format for DH parameters */
+ENCODER("DH", dh, yes, der, PKCS3),
+ENCODER("DH", dh, yes, pem, PKCS3),
+/* X9.42 defines the format for DHX parameters */
+ENCODER("DHX", dhx, yes, der, X9_42),
+ENCODER("DHX", dhx, yes, pem, X9_42),
+#endif
+#ifndef OPENSSL_NO_EC
+/* RFC 5915 defines the format for EC keys and parameters */
+ENCODER("EC", ec, yes, der, X9_62),
+ENCODER("EC", ec, yes, pem, X9_62),
 #endif
diff --git a/providers/implementations/encode_decode/encode_key2any.c b/providers/implementations/encode_decode/encode_key2any.c
index ca8f24fed2..8668588a7d 100644
--- a/providers/implementations/encode_decode/encode_key2any.c
+++ b/providers/implementations/encode_decode/encode_key2any.c
@@ -50,7 +50,8 @@ struct key2any_ctx_st {
 typedef int check_key_type_fn(const void *key, int nid);
 typedef int key_to_paramstring_fn(const void *key, int nid,
                                   void **str, int *strtype);
-typedef int key_to_der_fn(BIO *out, const void *key, int key_nid,
+typedef int key_to_der_fn(BIO *out, const void *key,
+                          int key_nid, const char *pemname,
                           key_to_paramstring_fn *p2s, i2d_of_void *k2d,
                           struct key2any_ctx_st *ctx);
 typedef int write_bio_of_void_fn(BIO *bp, const void *x);
@@ -136,9 +137,18 @@ static X509_PUBKEY *key_to_pubkey(const void *key, int key_nid,
     return xpk;
 }
 
-static int key_to_der_pkcs8_bio(BIO *out, const void *key, int key_nid,
-                                key_to_paramstring_fn *p2s, i2d_of_void *k2d,
-                                struct key2any_ctx_st *ctx)
+/*
+ * key_to_pkcs8_* produce encoded output with the key data pkcs8
+ * in a structure.  For private keys, that structure is PKCS#8, and for
+ * public keys, it's X.509 SubjectPublicKeyInfo.  Parameters don't have
+ * any defined envelopment of that kind.
+ */
+static int key_to_pkcs8_der_priv_bio(BIO *out, const void *key,
+                                     int key_nid,
+                                     ossl_unused const char *pemname,
+                                     key_to_paramstring_fn *p2s,
+                                     i2d_of_void *k2d,
+                                     struct key2any_ctx_st *ctx)
 {
     int ret = 0;
     void *str = NULL;
@@ -167,9 +177,12 @@ static int key_to_der_pkcs8_bio(BIO *out, const void *key, int key_nid,
     return ret;
 }
 
-static int key_to_pem_pkcs8_bio(BIO *out, const void *key, int key_nid,
-                                key_to_paramstring_fn *p2s, i2d_of_void *k2d,
-                                struct key2any_ctx_st *ctx)
+static int key_to_pkcs8_pem_priv_bio(BIO *out, const void *key,
+                                     int key_nid,
+                                     ossl_unused const char *pemname,
+                                     key_to_paramstring_fn *p2s,
+                                     i2d_of_void *k2d,
+                                     struct key2any_ctx_st *ctx)
 {
     int ret = 0;
     void *str = NULL;
@@ -198,9 +211,12 @@ static int key_to_pem_pkcs8_bio(BIO *out, const void *key, int key_nid,
     return ret;
 }
 
-static int key_to_der_pubkey_bio(BIO *out, const void *key, int key_nid,
-                                 key_to_paramstring_fn *p2s, i2d_of_void *k2d,
-                                 struct key2any_ctx_st *ctx)
+static int key_to_spki_der_pub_bio(BIO *out, const void *key,
+                                   int key_nid,
+                                   ossl_unused const char *pemname,
+                                   key_to_paramstring_fn *p2s,
+                                   i2d_of_void *k2d,
+                                   struct key2any_ctx_st *ctx)
 {
     int ret = 0;
     void *str = NULL;
@@ -220,9 +236,12 @@ static int key_to_der_pubkey_bio(BIO *out, const void *key, int key_nid,
     return ret;
 }
 
-static int key_to_pem_pubkey_bio(BIO *out, const void *key, int key_nid,
-                                 key_to_paramstring_fn *p2s, i2d_of_void *k2d,
-                                 struct key2any_ctx_st *ctx)
+static int key_to_spki_pem_pub_bio(BIO *out, const void *key,
+                                   int key_nid,
+                                   ossl_unused const char *pemname,
+                                   key_to_paramstring_fn *p2s,
+                                   i2d_of_void *k2d,
+                                   struct key2any_ctx_st *ctx)
 {
     int ret = 0;
     void *str = NULL;
@@ -242,6 +261,85 @@ static int key_to_pem_pubkey_bio(BIO *out, const void *key, int key_nid,
     return ret;
 }
 
+/*
+ * key_to_type_specific_* produce encoded output with type specific key data,
+ * no envelopment; the same kind of output as the type specific i2d_ and
+ * PEM_write_ functions, which is often a simple SEQUENCE of INTEGER.
+ *
+ * OpenSSL tries to discourage production of new keys in this form, because
+ * of the ambiguity when trying to recognise them, but can't deny that PKCS#1
+ * et al still are live standards.
+ *
+ * Note that these functions completely ignore p2s, and rather rely entirely
+ * on k2d to do the complete work.
+ */
+static int key_to_type_specific_der_bio(BIO *out, const void *key,
+                                        int key_nid,
+                                        ossl_unused const char *pemname,
+                                        key_to_paramstring_fn *p2s,
+                                        i2d_of_void *k2d,
+                                        struct key2any_ctx_st *ctx)
+{
+    unsigned char *der = NULL;
+    int derlen;
+    int ret;
+
+    if ((derlen = k2d(key, &der)) <= 0) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    ret = BIO_write(out, der, derlen);
+    OPENSSL_free(der);
+    return ret > 0;
+}
+#define key_to_type_specific_der_priv_bio key_to_type_specific_der_bio
+#define key_to_type_specific_der_pub_bio key_to_type_specific_der_bio
+#define key_to_type_specific_der_param_bio key_to_type_specific_der_bio
+
+static int key_to_type_specific_pem_bio_cb(BIO *out, const void *key,
+                                           int key_nid, const char *pemname,
+                                           key_to_paramstring_fn *p2s,
+                                           i2d_of_void *k2d,
+                                           struct key2any_ctx_st *ctx,
+                                           pem_password_cb *cb, void *cbarg)
+{
+    return
+        PEM_ASN1_write_bio(k2d, pemname, out, key, ctx->cipher,
+                           NULL, 0, ossl_pw_pem_password, &ctx->pwdata) > 0;
+}
+
+static int key_to_type_specific_pem_priv_bio(BIO *out, const void *key,
+                                             int key_nid, const char *pemname,
+                                             key_to_paramstring_fn *p2s,
+                                             i2d_of_void *k2d,
+                                             struct key2any_ctx_st *ctx)
+{
+    return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+                                           p2s, k2d, ctx,
+                                           ossl_pw_pem_password, &ctx->pwdata);
+}
+
+static int key_to_type_specific_pem_pub_bio(BIO *out, const void *key,
+                                            int key_nid, const char *pemname,
+                                            key_to_paramstring_fn *p2s,
+                                            i2d_of_void *k2d,
+                                            struct key2any_ctx_st *ctx)
+{
+    return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+                                           p2s, k2d, ctx, NULL, NULL);
+}
+
+static int key_to_type_specific_pem_param_bio(BIO *out, const void *key,
+                                              int key_nid, const char *pemname,
+                                              key_to_paramstring_fn *p2s,
+                                              i2d_of_void *k2d,
+                                              struct key2any_ctx_st *ctx)
+{
+    return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+                                           p2s, k2d, ctx, NULL, NULL);
+}
+
 #define der_output_type         "DER"
 #define pem_output_type         "PEM"
 
@@ -275,7 +373,7 @@ static int prepare_dh_params(const void *dh, int nid,
     return 1;
 }
 
-static int dh_pub_to_der(const void *dh, unsigned char **pder)
+static int dh_spki_pub_to_der(const void *dh, unsigned char **pder)
 {
     const BIGNUM *bn = NULL;
     ASN1_INTEGER *pub_key = NULL;
@@ -296,7 +394,7 @@ static int dh_pub_to_der(const void *dh, unsigned char **pder)
     return ret;
 }
 
-static int dh_priv_to_der(const void *dh, unsigned char **pder)
+static int dh_pkcs8_priv_to_der(const void *dh, unsigned char **pder)
 {
     const BIGNUM *bn = NULL;
     ASN1_INTEGER *priv_key = NULL;
@@ -317,31 +415,24 @@ static int dh_priv_to_der(const void *dh, unsigned char **pder)
     return ret;
 }
 
-static int dh_params_to_der_bio(BIO *out, const void *key)
+static int dh_type_specific_params_to_der(const void *dh, unsigned char **pder)
 {
-    int type =
-        DH_test_flags(key, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
-
-    if (type == EVP_PKEY_DH)
-        return i2d_DHparams_bio(out, key);
-    return i2d_DHxparams_bio(out, key);
+    if (DH_test_flags(dh, DH_FLAG_TYPE_DHX))
+        return i2d_DHxparams(dh, pder);
+    return i2d_DHparams(dh, pder);
 }
 
-static int dh_params_to_pem_bio(BIO *out, const void *key)
-{
-    int type =
-        DH_test_flags(key, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
-
-    if (type == EVP_PKEY_DH)
-        return PEM_write_bio_DHparams(out, key);
-
-    return PEM_write_bio_DHxparams(out, key);
-}
+/*
+ * DH doesn't have i2d_DHPrivateKey or i2d_DHPublicKey, so we can't make
+ * corresponding functions here.
+ */
+# define dh_type_specific_priv_to_der   NULL
+# define dh_type_specific_pub_to_der    NULL
 
-static int dh_check_key_type(const void *key, int expected_type)
+static int dh_check_key_type(const void *dh, int expected_type)
 {
     int type =
-        DH_test_flags(key, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
+        DH_test_flags(dh, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
 
     return type == expected_type;
 }
@@ -350,6 +441,8 @@ static int dh_check_key_type(const void *key, int expected_type)
 # define dhx_evp_type           EVP_PKEY_DHX
 # define dh_input_type          "DH"
 # define dhx_input_type         "DHX"
+# define dh_pem_type            "DH"
+# define dhx_pem_type           "X9.42 DH"
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -407,7 +500,7 @@ static int prepare_dsa_params(const void *dsa, int nid,
         :  prepare_some_dsa_params(dsa, nid, pstr, pstrtype);
 }
 
-static int dsa_pub_to_der(const void *dsa, unsigned char **pder)
+static int dsa_spki_pub_to_der(const void *dsa, unsigned char **pder)
 {
     const BIGNUM *bn = NULL;
     ASN1_INTEGER *pub_key = NULL;
@@ -428,7 +521,7 @@ static int dsa_pub_to_der(const void *dsa, unsigned char **pder)
     return ret;
 }
 
-static int dsa_priv_to_der(const void *dsa, unsigned char **pder)
+static int dsa_pkcs8_priv_to_der(const void *dsa, unsigned char **pder)
 {
     const BIGNUM *bn = NULL;
     ASN1_INTEGER *priv_key = NULL;
@@ -449,19 +542,14 @@ static int dsa_priv_to_der(const void *dsa, unsigned char **pder)
     return ret;
 }
 
-static int dsa_params_to_der_bio(BIO *out, const void *key)
-{
-    return i2d_DSAparams_bio(out, key);
-}
-
-static int dsa_params_to_pem_bio(BIO *out, const void *key)
-{
-    return PEM_write_bio_DSAparams(out, key);
-}
+# define dsa_type_specific_priv_to_der   (i2d_of_void *)i2d_DSAPrivateKey
+# define dsa_type_specific_pub_to_der    (i2d_of_void *)i2d_DSAPublicKey
+# define dsa_type_specific_params_to_der (i2d_of_void *)i2d_DSAparams
 
 # define dsa_check_key_type     NULL
 # define dsa_evp_type           EVP_PKEY_DSA
 # define dsa_input_type         "DSA"
+# define dsa_pem_type           "DSA"
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -489,6 +577,11 @@ static int prepare_ec_explicit_params(const void *eckey,
     return 1;
 }
 
+/*
+ * This implements EcpkParameters, where the CHOICE is based on whether there
+ * is a curve name (curve nid) to be found or not.  See RFC 3279 for details.
+ * TODO: shouldn't we use i2d_ECPKParameters()?
+ */
 static int prepare_ec_params(const void *eckey, int nid,
                              void **pstr, int *pstrtype)
 {
@@ -507,6 +600,7 @@ static int prepare_ec_params(const void *eckey, int nid,
 
     if (curve_nid != NID_undef
         && (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE)) {
+        /* The CHOICE came to namedCurve */
         if (OBJ_length(params) == 0) {
             /* Some curves might not have an associated OID */
             ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_OID);
@@ -517,26 +611,17 @@ static int prepare_ec_params(const void *eckey, int nid,
         *pstrtype = V_ASN1_OBJECT;
         return 1;
     } else {
+        /* The CHOICE came to ecParameters */
         return prepare_ec_explicit_params(eckey, pstr, pstrtype);
     }
 }
 
-static int ec_params_to_der_bio(BIO *out, const void *eckey)
-{
-    return i2d_ECPKParameters_bio(out, EC_KEY_get0_group(eckey));
-}
-
-static int ec_params_to_pem_bio(BIO *out, const void *eckey)
-{
-    return PEM_write_bio_ECPKParameters(out, EC_KEY_get0_group(eckey));
-}
-
-static int ec_pub_to_der(const void *eckey, unsigned char **pder)
+static int ec_spki_pub_to_der(const void *eckey, unsigned char **pder)
 {
     return i2o_ECPublicKey(eckey, pder);
 }
 
-static int ec_priv_to_der(const void *veckey, unsigned char **pder)
+static int ec_pkcs8_priv_to_der(const void *veckey, unsigned char **pder)
 {
     EC_KEY *eckey = (EC_KEY *)veckey;
     unsigned int old_flags;
@@ -556,9 +641,14 @@ static int ec_priv_to_der(const void *veckey, unsigned char **pder)
     return ret; /* return the length of the der encoded data */
 }
 
+# define ec_type_specific_params_to_der (i2d_of_void *)i2d_ECParameters
+# define ec_type_specific_pub_to_der    (i2d_of_void *)i2o_ECPublicKey
+# define ec_type_specific_priv_to_der   (i2d_of_void *)i2d_ECPrivateKey
+
 # define ec_check_key_type      NULL
 # define ec_evp_type            EVP_PKEY_EC
 # define ec_input_type          "EC"
+# define ec_pem_type            "EC"
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -566,7 +656,7 @@ static int ec_priv_to_der(const void *veckey, unsigned char **pder)
 #ifndef OPENSSL_NO_EC
 # define prepare_ecx_params NULL
 
-static int ecx_pub_to_der(const void *vecxkey, unsigned char **pder)
+static int ecx_spki_pub_to_der(const void *vecxkey, unsigned char **pder)
 {
     const ECX_KEY *ecxkey = vecxkey;
     unsigned char *keyblob;
@@ -586,7 +676,7 @@ static int ecx_pub_to_der(const void *vecxkey, unsigned char **pder)
     return ecxkey->keylen;
 }
 
-static int ecx_priv_to_der(const void *vecxkey, unsigned char **pder)
+static int ecx_pkcs8_priv_to_der(const void *vecxkey, unsigned char **pder)
 {
     const ECX_KEY *ecxkey = vecxkey;
     ASN1_OCTET_STRING oct;
@@ -610,8 +700,11 @@ static int ecx_priv_to_der(const void *vecxkey, unsigned char **pder)
     return keybloblen;
 }
 
-# define ecx_params_to_der_bio  NULL
-# define ecx_params_to_pem_bio  NULL
+/*
+ * ED25519, ED448, X25519 and X448 only has PKCS#8 / SubjectPublicKeyInfo
+ * representation, so we don't define ecx_type_specific_[priv,pub,params]_to_der.
+ */
+
 # define ecx_check_key_type     NULL
 
 # define ed25519_evp_type       EVP_PKEY_ED25519
@@ -622,6 +715,10 @@ static int ecx_priv_to_der(const void *vecxkey, unsigned char **pder)
 # define ed448_input_type       "ED448"
 # define x25519_input_type      "X25519"
 # define x448_input_type        "X448"
+# define ed25519_pem_type       "ED25519"
+# define ed448_pem_type         "ED448"
+# define x25519_pem_type        "X25519"
+# define x448_pem_type          "X448"
 #endif
 
 /* ---------------------------------------------------------------------- */
@@ -701,10 +798,15 @@ static int prepare_rsa_params(const void *rsa, int nid,
     return 0;
 }
 
-#define rsa_params_to_der_bio   NULL
-#define rsa_params_to_pem_bio   NULL
-#define rsa_priv_to_der         (i2d_of_void *)i2d_RSAPrivateKey
-#define rsa_pub_to_der          (i2d_of_void *)i2d_RSAPublicKey
+/*
+ * RSA is extremely simple, as PKCS#1 is used for the PKCS#8 |privateKey|
+ * field as well as the SubjectPublicKeyInfo |subjectPublicKey| field.
+ */
+#define rsa_pkcs8_priv_to_der           rsa_type_specific_priv_to_der
+#define rsa_spki_pub_to_der             rsa_type_specific_pub_to_der
+#define rsa_type_specific_priv_to_der   (i2d_of_void *)i2d_RSAPrivateKey
+#define rsa_type_specific_pub_to_der    (i2d_of_void *)i2d_RSAPublicKey
+#define rsa_type_specific_params_to_der NULL
 
 static int rsa_check_key_type(const void *rsa, int expected_type)
 {
@@ -723,12 +825,13 @@ static int rsa_check_key_type(const void *rsa, int expected_type)
 #define rsapss_evp_type         EVP_PKEY_RSA_PSS
 #define rsa_input_type          "RSA"
 #define rsapss_input_type       "RSA-PSS"
+#define rsa_pem_type            "RSA"
+#define rsapss_pem_type         "RSA-PSS"
 
 /* ---------------------------------------------------------------------- */
 
 static OSSL_FUNC_decoder_newctx_fn key2any_newctx;
 static OSSL_FUNC_decoder_freectx_fn key2any_freectx;
-static OSSL_FUNC_decoder_gettable_params_fn key2any_gettable_params;
 
 static void *key2any_newctx(void *provctx)
 {
@@ -749,18 +852,27 @@ static void key2any_freectx(void *vctx)
     OPENSSL_free(ctx);
 }
 
-static const OSSL_PARAM *key2any_gettable_params(void *provctx)
+static const OSSL_PARAM *key2any_gettable_params(void *provctx, int structure)
 {
     static const OSSL_PARAM gettables[] = {
+        { OSSL_ENCODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        { OSSL_ENCODER_PARAM_OUTPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    static const OSSL_PARAM gettables_w_structure[] = {
+        { OSSL_ENCODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
         { OSSL_ENCODER_PARAM_OUTPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        { OSSL_ENCODER_PARAM_OUTPUT_STRUCTURE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
         OSSL_PARAM_END,
     };
 
-    return gettables;
+    return structure ? gettables_w_structure : gettables;
 }
 
 static int key2any_get_params(OSSL_PARAM params[], const char *input_type,
-                              const char *output_type)
+                              const char *output_type,
+                              const char *output_struct)
 {
     OSSL_PARAM *p;
 
@@ -772,6 +884,12 @@ static int key2any_get_params(OSSL_PARAM params[], const char *input_type,
     if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, output_type))
         return 0;
 
+    if (output_struct != NULL) {
+        p = OSSL_PARAM_locate(params, OSSL_ENCODER_PARAM_OUTPUT_STRUCTURE);
+        if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, output_struct))
+            return 0;
+    }
+
     return 1;
 }
 
@@ -805,6 +923,7 @@ static int key2any_set_ctx_params(void *vctx, const OSSL_PARAM params[])
             return 0;
 
         EVP_CIPHER_free(ctx->cipher);
+        ctx->cipher = NULL;
         ctx->cipher_intent = ciphername != NULL;
         if (ciphername != NULL
             && ((ctx->cipher =
@@ -814,11 +933,44 @@ static int key2any_set_ctx_params(void *vctx, const OSSL_PARAM params[])
     return 1;
 }
 
+static int key2any_check_selection(int selection, int selection_mask)
+{
+    /*
+     * The selections are kinda sorta "levels", i.e. each selection given
+     * here is assumed to include those following.
+     */
+    int checks[] = {
+        OSSL_KEYMGMT_SELECT_PRIVATE_KEY,
+        OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+        OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+    };
+    size_t i;
+
+    /* The decoder implementations made here support guessing */
+    if (selection == 0)
+        return 1;
+
+    for (i = 0; i < OSSL_NELEM(checks); i++) {
+        int check1 = (selection & checks[i]) != 0;
+        int check2 = (selection_mask & checks[i]) != 0;
+
+        /*
+         * If the caller asked for the currently checked bit(s), return
+         * whether the decoder description says it's supported.
+         */
+        if (check1)
+            return check2;
+    }
+
+    /* This should be dead code, but just to be safe... */
+    return 0;
+}
+
 static int key2any_encode(struct key2any_ctx_st *ctx, OSSL_CORE_BIO *cout,
-                          const void *key, int type,
+                          const void *key, int type, const char *pemname,
                           check_key_type_fn *checker,
                           key_to_der_fn *writer,
-                          OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg,
+                          OSSL_PASSPHRASE_CALLBACK *pwcb, void *pwcbarg,
                           key_to_paramstring_fn *key2paramstring,
                           i2d_of_void *key2der)
 {
@@ -826,13 +978,15 @@ static int key2any_encode(struct key2any_ctx_st *ctx, OSSL_CORE_BIO *cout,
 
     if (key == NULL) {
         ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
-    } else if (checker == NULL || checker(key, type)) {
+    } else if (writer != NULL
+               && (checker == NULL || checker(key, type))) {
         BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
 
         if (out != NULL
-            && writer != NULL
-            && ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg))
-            ret = writer(out, key, type, key2paramstring, key2der, ctx);
+            && (pwcb == NULL
+                || ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, pwcb, pwcbarg)))
+            ret =
+                writer(out, key, type, pemname, key2paramstring, key2der, ctx);
 
         BIO_free(out);
     } else {
@@ -841,136 +995,395 @@ static int key2any_encode(struct key2any_ctx_st *ctx, OSSL_CORE_BIO *cout,
     return ret;
 }
 
-static int key2any_encode_params(struct key2any_ctx_st *ctx,
-                                 OSSL_CORE_BIO *cout,
-                                 const void *key, int type,
-                                 check_key_type_fn *checker,
-                                 write_bio_of_void_fn *writer)
-{
-    int ret = 0;
+#define DO_PRIVATE_KEY_selection_mask OSSL_KEYMGMT_SELECT_PRIVATE_KEY
+#define DO_PRIVATE_KEY(impl, type, kind, output)                            \
+    if ((selection & DO_PRIVATE_KEY_selection_mask) != 0)                   \
+        return key2any_encode(ctx, cout, key, impl##_evp_type,              \
+                              impl##_pem_type " PRIVATE KEY",               \
+                              type##_check_key_type,                        \
+                              key_to_##kind##_##output##_priv_bio,          \
+                              cb, cbarg, prepare_##type##_params,           \
+                              type##_##kind##_priv_to_der);
+
+#define DO_PUBLIC_KEY_selection_mask OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+#define DO_PUBLIC_KEY(impl, type, kind, output)                             \
+    if ((selection & DO_PUBLIC_KEY_selection_mask) != 0)                    \
+        return key2any_encode(ctx, cout, key, impl##_evp_type,              \
+                              impl##_pem_type " PUBLIC KEY",                \
+                              type##_check_key_type,                        \
+                              key_to_##kind##_##output##_pub_bio,           \
+                              cb, cbarg, prepare_##type##_params,           \
+                              type##_##kind##_pub_to_der);
+
+#define DO_PARAMETERS_selection_mask OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+#define DO_PARAMETERS(impl, type, kind, output)                             \
+    if ((selection & DO_PARAMETERS_selection_mask) != 0)                    \
+        return key2any_encode(ctx, cout, key, impl##_evp_type,              \
+                              impl##_pem_type " PARAMETERS",                \
+                              type##_check_key_type,                        \
+                              key_to_##kind##_##output##_param_bio,         \
+                              NULL, NULL, NULL,                             \
+                              type##_##kind##_params_to_der);
+
+/*-
+ * Implement the kinds of output structure that can be produced.  They are
+ * referred to by name, and for each name, the following macros are defined
+ * (braces not included):
+ *
+ * {kind}_output_structure
+ *
+ *      A string that names the output structure. This is used as a selection
+ *      criterion for each implementation.  It may be NULL, which means that
+ *      there is only one possible output structure for the implemented output
+ *      type.
+ *
+ * DO_{kind}_selection_mask
+ *
+ *      A mask of selection bits that must not be zero.  This is used as a
+ *      selection criterion for each implementation.
+ *      This mask must never be zero.
+ *
+ * DO_{kind}
+ *
+ *      The performing macro.  It must use the DO_ macros defined above,
+ *      always in this order:
+ *
+ *      - DO_PRIVATE_KEY
+ *      - DO_PUBLIC_KEY
+ *      - DO_PARAMETERS
+ *
+ *      Any of those may be omitted, but the relative order must still be
+ *      the same.
+ */
 
-    if (key == NULL) {
-        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
-    } else if (checker == NULL || checker(key, type)) {
-        BIO *out = bio_new_from_core_bio(ctx->provctx, cout);
+/* PKCS#8 is a structure for private keys only */
+#define PKCS8_output_structure "pkcs8"
+#define DO_PKCS8_selection_mask DO_PRIVATE_KEY_selection_mask
+#define DO_PKCS8(impl, type, output)                                        \
+    DO_PRIVATE_KEY(impl, type, pkcs8, output)
 
-        if (out != NULL && writer != NULL)
-            ret = writer(out, key);
+/* SubjectPublicKeyInfo is a structure for public keys only */
+#define SubjectPublicKeyInfo_output_structure "SubjectPublicKeyInfo"
+#define DO_SubjectPublicKeyInfo_selection_mask DO_PUBLIC_KEY_selection_mask
+#define DO_SubjectPublicKeyInfo(impl, type, output)                         \
+    DO_PUBLIC_KEY(impl, type, spki, output)
 
-        BIO_free(out);
-    } else {
-        ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
-    }
+/*
+ * "type-specific" is a uniform name for key type specific output for private
+ * and public keys as well as key parameters.  This is used internally in
+ * libcrypto so it doesn't have to have special knowledge about select key
+ * types, but also when no better name has been found.  If there are more
+ * expressive DO_ names above, those are preferred.
+ *
+ * Three forms exist:
+ *
+ * - type_specific_keypair              Only supports private and public key
+ * - type_specific_params               Only supports parameters
+ * - type_specific                      Supports all parts of an EVP_PKEY
+ * - type_specific_no_pub               Supports all parts of an EVP_PKEY
+ *                                      except public key
+ */
+#define type_specific_params_output_structure "type-specific"
+#define DO_type_specific_params_selection_mask DO_PARAMETERS_selection_mask
+#define DO_type_specific_params(impl, type, output)                         \
+    DO_PARAMETERS(impl, type, type_specific, output)
+#define type_specific_keypair_output_structure "type-specific"
+#define DO_type_specific_keypair_selection_mask                             \
+    ( DO_PRIVATE_KEY_selection_mask | DO_PUBLIC_KEY_selection_mask )
+#define DO_type_specific_keypair(impl, type, output)                        \
+    DO_PRIVATE_KEY(impl, type, type_specific, output)                       \
+    DO_PUBLIC_KEY(impl, type, type_specific, output)
+#define type_specific_output_structure "type-specific"
+#define DO_type_specific_selection_mask                                     \
+    ( DO_type_specific_keypair_selection_mask                               \
+      | DO_type_specific_params_selection_mask )
+#define DO_type_specific(impl, type, output)                                \
+    DO_type_specific_keypair(impl, type, output)                            \
+    DO_type_specific_params(impl, type, output)
+#define type_specific_no_pub_output_structure "type-specific"
+#define DO_type_specific_no_pub_selection_mask \
+    ( DO_PRIVATE_KEY_selection_mask |  DO_PARAMETERS_selection_mask)
+#define DO_type_specific_no_pub(impl, type, output)                         \
+    DO_PRIVATE_KEY(impl, type, type_specific, output)                       \
+    DO_type_specific_params(impl, type, output)
 
-    return ret;
-}
+/*
+ * Type specific aliases for the cases where we need to refer to them by
+ * type name.
+ * This only covers key types that are represented with i2d_{TYPE}PrivateKey,
+ * i2d_{TYPE}PublicKey and i2d_{TYPE}params / i2d_{TYPE}Parameters.
+ */
+#define RSA_output_structure "rsa"
+#define DO_RSA_selection_mask DO_type_specific_keypair_selection_mask
+#define DO_RSA(impl, type, output) DO_type_specific_keypair(impl, type, output)
+
+#define DH_output_structure "dh"
+#define DO_DH_selection_mask DO_type_specific_params_selection_mask
+#define DO_DH(impl, type, output) DO_type_specific_params(impl, type, output)
+
+#define DHX_output_structure "dhx"
+#define DO_DHX_selection_mask DO_type_specific_params_selection_mask
+#define DO_DHX(impl, type, output) DO_type_specific_params(impl, type, output)
+
+#define DSA_output_structure "dsa"
+#define DO_DSA_selection_mask DO_type_specific_selection_mask
+#define DO_DSA(impl, type, output) DO_type_specific(impl, type, output)
+
+#define EC_output_structure "ec"
+#define DO_EC_selection_mask DO_type_specific_selection_mask
+#define DO_EC(impl, type, output) DO_type_specific(impl, type, output)
+
+/* PKCS#1 defines a structure for RSA private and public keys */
+#define PKCS1_output_structure "pkcs1"
+#define DO_PKCS1_selection_mask DO_RSA_selection_mask
+#define DO_PKCS1(impl, type, output) DO_RSA(impl, type, output)
+
+/* PKCS#3 defines a structure for DH parameters */
+#define PKCS3_output_structure "pkcs3"
+#define DO_PKCS3_selection_mask DO_DH_selection_mask
+#define DO_PKCS3(impl, type, output) DO_DH(impl, type, output)
+/* X9.42 defines a structure for DHx parameters */
+#define X9_42_output_structure "X9.42"
+#define DO_X9_42_selection_mask DO_DHX_selection_mask
+#define DO_X9_42(impl, type, output) DO_DHX(impl, type, output)
+
+/* X9.62 defines a structure for EC keys and parameters */
+#define X9_62_output_structure "X9.62"
+#define DO_X9_62_selection_mask DO_EC_selection_mask
+#define DO_X9_62(impl, type, output) DO_EC(impl, type, output)
 
-#define MAKE_ENCODER(impl, type, evp_type, output)                          \
+/*
+ * MAKE_ENCODER is the single driver for creating OSSL_DISPATCH tables.
+ * It takes the following arguments:
+ *
+ * impl         This is the key type name that's being implemented.
+ * type         This is the type name for the set of functions that implement
+ *              the key type.  For example, ed25519, ed448, x25519 and x448
+ *              are all implemented with the exact same set of functions.
+ * evp_type     The corresponding EVP_PKEY_xxx type macro for each key.
+ *              Necessary because we currently use EVP_PKEY with legacy
+ *              native keys internally.  This will need to be refactored
+ *              when that legacy support goes away.
+ * kind         What kind of support to implement.  These translate into
+ *              the DO_##kind macros above.
+ * output       The output type to implement.  may be der or pem.
+ *
+ * The resulting OSSL_DISPATCH array gets the following name (expressed in
+ * C preprocessor terms) from those arguments:
+ *
+ * ossl_##impl##_to_##kind##_##output##_encoder_functions
+ */
+#define MAKE_ENCODER(impl, type, evp_type, kind, output)                    \
+    static OSSL_FUNC_encoder_gettable_params_fn                             \
+    impl##_to_##kind##_##output##_gettable_params;                          \
     static OSSL_FUNC_encoder_get_params_fn                                  \
-    impl##2##output##_get_params;                                           \
+    impl##_to_##kind##_##output##_get_params;                               \
     static OSSL_FUNC_encoder_import_object_fn                               \
-    impl##2##output##_import_object;                                        \
+    impl##_to_##kind##_##output##_import_object;                            \
     static OSSL_FUNC_encoder_free_object_fn                                 \
-    impl##2##output##_free_object;                                          \
-    static OSSL_FUNC_encoder_encode_fn impl##2##output##_encode;            \
+    impl##_to_##kind##_##output##_free_object;                              \
+    static OSSL_FUNC_encoder_encode_fn                                      \
+    impl##_to_##kind##_##output##_encode;                                   \
                                                                             \
-    static int impl##2##output##_get_params(OSSL_PARAM params[])            \
+    static const OSSL_PARAM *                                               \
+    impl##_to_##kind##_##output##_gettable_params(void *provctx)            \
+    {                                                                       \
+        return key2any_gettable_params(provctx,                             \
+                                       kind##_output_structure != NULL);    \
+    }                                                                       \
+    static int                                                              \
+    impl##_to_##kind##_##output##_get_params(OSSL_PARAM params[])           \
     {                                                                       \
         return key2any_get_params(params, impl##_input_type,                \
-                                  output##_output_type);                    \
+                                  output##_output_type,                     \
+                                  kind##_output_structure);                 \
     }                                                                       \
     static void *                                                           \
-    impl##2##output##_import_object(void *vctx, int selection,              \
-                                    const OSSL_PARAM params[])              \
+    impl##_to_##kind##_##output##_import_object(void *vctx, int selection,  \
+                                                const OSSL_PARAM params[])  \
     {                                                                       \
         struct key2any_ctx_st *ctx = vctx;                                  \
+                                                                            \
         return ossl_prov_import_key(ossl_##impl##_keymgmt_functions,        \
                                     ctx->provctx, selection, params);       \
     }                                                                       \
-    static void impl##2##output##_free_object(void *key)                    \
+    static void impl##_to_##kind##_##output##_free_object(void *key)        \
     {                                                                       \
         ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key);           \
     }                                                                       \
+    static int impl##_to_##kind##_##output##_does_selection(void *ctx,      \
+                                                            int selection)  \
+    {                                                                       \
+        return key2any_check_selection(selection,                           \
+                                       DO_##kind##_selection_mask);         \
+    }                                                                       \
     static int                                                              \
-    impl##2##output##_encode(void *ctx, OSSL_CORE_BIO *cout,                \
-                             const void *key,                               \
-                             const OSSL_PARAM key_abstract[],               \
-                             int selection,                                 \
-                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)     \
+    impl##_to_##kind##_##output##_encode(void *ctx, OSSL_CORE_BIO *cout,    \
+                                         const void *key,                   \
+                                         const OSSL_PARAM key_abstract[],   \
+                                         int selection,                     \
+                                         OSSL_PASSPHRASE_CALLBACK *cb,      \
+                                         void *cbarg)                       \
     {                                                                       \
         /* We don't deal with abstract objects */                           \
         if (key_abstract != NULL) {                                         \
             ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);         \
             return 0;                                                       \
         }                                                                   \
-        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)             \
-            return key2any_encode(ctx, cout, key, impl##_evp_type,          \
-                                  type##_check_key_type,                    \
-                                  key_to_##output##_pkcs8_bio,              \
-                                  cb, cbarg,                                \
-                                  prepare_##type##_params,                  \
-                                  type##_priv_to_der);                      \
-        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)              \
-            return key2any_encode(ctx, cout, key, impl##_evp_type,          \
-                                  type##_check_key_type,                    \
-                                  key_to_##output##_pubkey_bio,             \
-                                  cb, cbarg,                                \
-                                  prepare_##type##_params,                  \
-                                  type##_pub_to_der);                       \
-        if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)          \
-            return key2any_encode_params(ctx, cout, key,                    \
-                                         impl##_evp_type,                   \
-                                         type##_check_key_type,             \
-                                         type##_params_to_##output##_bio);  \
+        DO_##kind(impl, type, output)                                       \
                                                                             \
         ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);             \
         return 0;                                                           \
     }                                                                       \
-    const OSSL_DISPATCH ossl_##impl##_to_##output##_encoder_functions[] = { \
+    const OSSL_DISPATCH                                                     \
+    ossl_##impl##_to_##kind##_##output##_encoder_functions[] = {            \
         { OSSL_FUNC_ENCODER_NEWCTX,                                         \
           (void (*)(void))key2any_newctx },                                 \
         { OSSL_FUNC_ENCODER_FREECTX,                                        \
           (void (*)(void))key2any_freectx },                                \
         { OSSL_FUNC_ENCODER_GETTABLE_PARAMS,                                \
-          (void (*)(void))key2any_gettable_params },                        \
+          (void (*)(void))impl##_to_##kind##_##output##_gettable_params },  \
         { OSSL_FUNC_ENCODER_GET_PARAMS,                                     \
-          (void (*)(void))impl##2##output##_get_params },                   \
+          (void (*)(void))impl##_to_##kind##_##output##_get_params },       \
         { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS,                            \
           (void (*)(void))key2any_settable_ctx_params },                    \
         { OSSL_FUNC_ENCODER_SET_CTX_PARAMS,                                 \
           (void (*)(void))key2any_set_ctx_params },                         \
+        { OSSL_FUNC_ENCODER_DOES_SELECTION,                                 \
+          (void (*)(void))impl##_to_##kind##_##output##_does_selection },   \
         { OSSL_FUNC_ENCODER_IMPORT_OBJECT,                                  \
-          (void (*)(void))impl##2##output##_import_object },                \
+          (void (*)(void))impl##_to_##kind##_##output##_import_object },    \
         { OSSL_FUNC_ENCODER_FREE_OBJECT,                                    \
-          (void (*)(void))impl##2##output##_free_object },                  \
+          (void (*)(void))impl##_to_##kind##_##output##_free_object },      \
         { OSSL_FUNC_ENCODER_ENCODE,                                         \
-          (void (*)(void))impl##2##output##_encode },                       \
+          (void (*)(void))impl##_to_##kind##_##output##_encode },           \
         { 0, NULL }                                                         \
     }
 
+/*
+ * Replacements for i2d_{TYPE}PrivateKey, i2d_{TYPE}PublicKey,
+ * i2d_{TYPE}params, as they exist.
+ */
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, type_specific_keypair, der);
 #ifndef OPENSSL_NO_DH
-MAKE_ENCODER(dh, dh, EVP_PKEY_DH, der);
-MAKE_ENCODER(dh, dh, EVP_PKEY_DH, pem);
-MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, der);
-MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, pem);
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, type_specific_params, der);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, type_specific_params, der);
 #endif
 #ifndef OPENSSL_NO_DSA
-MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, der);
-MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, pem);
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, type_specific, der);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, type_specific_no_pub, der);
+#endif
+
+/*
+ * Replacements for PEM_write_bio_{TYPE}PrivateKey,
+ * PEM_write_bio_{TYPE}PublicKey, PEM_write_bio_{TYPE}params, as they exist.
+ */
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, type_specific_keypair, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, type_specific_params, pem);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, type_specific_params, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, type_specific, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, type_specific_no_pub, pem);
+#endif
+
+/*
+ * PKCS#8 and SubjectPublicKeyInfo support.  This may duplicate some of the
+ * implementations specified above, but are more specific.
+ * The SubjectPublicKeyInfo implementations also replace the
+ * PEM_write_bio_{TYPE}_PUBKEY functions.
+ * For PEM, these are expected to be used by PEM_write_bio_PrivateKey(),
+ * PEM_write_bio_PUBKEY() and PEM_write_bio_Parameters().
+ */
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, PKCS8, der);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, PKCS8, pem);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, PKCS8, der);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, PKCS8, pem);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, SubjectPublicKeyInfo, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, PKCS8, der);
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, PKCS8, pem);
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, PKCS8, der);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, PKCS8, pem);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, SubjectPublicKeyInfo, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, PKCS8, der);
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, PKCS8, pem);
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, SubjectPublicKeyInfo, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, PKCS8, der);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, PKCS8, pem);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, PKCS8, der);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, PKCS8, pem);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, PKCS8, der);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, PKCS8, pem);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, PKCS8, der);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, PKCS8, pem);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, PKCS8, der);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, PKCS8, pem);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, SubjectPublicKeyInfo, pem);
+#endif
+
+/*
+ * Support for key type specific output formats.  Not all key types have
+ * this, we only aim to duplicate what is available in 1.1.1 as
+ * i2d_TYPEPrivateKey(), i2d_TYPEPublicKey() and i2d_TYPEparams().
+ * For example, there are no publicly available i2d_ function for
+ * ED25519, ED448, X25519 or X448, and they therefore only have PKCS#8
+ * and SubjectPublicKeyInfo implementations as implemented above.
+ */
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, RSA, der);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, RSA, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, DH, der);
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, DH, pem);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, DHX, der);
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, DHX, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, DSA, der);
+MAKE_ENCODER(dsa, dsa, EVP_PKEY_DSA, DSA, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, EC, der);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, EC, pem);
+#endif
+
+/* Convenience structure names */
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, PKCS1, der);
+MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, PKCS1, pem);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, PKCS1, der);
+MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA_PSS, PKCS1, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, PKCS3, der); /* parameters only */
+MAKE_ENCODER(dh, dh, EVP_PKEY_DH, PKCS3, pem); /* parameters only */
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, X9_42, der); /* parameters only */
+MAKE_ENCODER(dhx, dh, EVP_PKEY_DHX, X9_42, pem); /* parameters only */
 #endif
 #ifndef OPENSSL_NO_EC
-MAKE_ENCODER(ec, ec, EVP_PKEY_EC, der);
-MAKE_ENCODER(ec, ec, EVP_PKEY_EC, pem);
-MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, der);
-MAKE_ENCODER(ed25519, ecx, EVP_PKEY_ED25519, pem);
-MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, der);
-MAKE_ENCODER(ed448, ecx, EVP_PKEY_ED448, pem);
-MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, der);
-MAKE_ENCODER(x25519, ecx, EVP_PKEY_X25519, pem);
-MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, der);
-MAKE_ENCODER(x448, ecx, EVP_PKEY_ED448, pem);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, X9_62, der);
+MAKE_ENCODER(ec, ec, EVP_PKEY_EC, X9_62, pem);
 #endif
-MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, der);
-MAKE_ENCODER(rsa, rsa, EVP_PKEY_RSA, pem);
-MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA, der);
-MAKE_ENCODER(rsapss, rsa, EVP_PKEY_RSA, pem);
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 23b0f51291..ffc23f60d8 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -321,45 +321,99 @@ extern const OSSL_DISPATCH sm2_asym_cipher_functions[];
 extern const OSSL_DISPATCH ossl_rsa_asym_kem_functions[];
 
 /* Encoders */
-extern const OSSL_DISPATCH ossl_rsa_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_rsa_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_RSA_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_RSA_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_pem_encoder_functions[];
 extern const OSSL_DISPATCH ossl_rsa_to_text_encoder_functions[];
-
-extern const OSSL_DISPATCH ossl_rsapss_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_rsapss_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_pem_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_pem_encoder_functions[];
 extern const OSSL_DISPATCH ossl_rsapss_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_dh_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_dh_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_DH_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_DH_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS3_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS3_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_pem_encoder_functions[];
 extern const OSSL_DISPATCH ossl_dh_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_dhx_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_dhx_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_DHX_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_DHX_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_X9_42_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_X9_42_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_pem_encoder_functions[];
 extern const OSSL_DISPATCH ossl_dhx_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_dsa_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_dsa_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_DSA_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_DSA_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_type_specific_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_type_specific_der_encoder_functions[];
 extern const OSSL_DISPATCH ossl_dsa_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_x25519_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_x25519_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ossl_x25519_to_text_encoder_functions[];
-
-extern const OSSL_DISPATCH ossl_x448_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_x448_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ossl_x448_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_EC_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_EC_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_X9_62_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_X9_62_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_ed25519_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_ed25519_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_OSSL_current_der_encoder_functions[];
 extern const OSSL_DISPATCH ossl_ed25519_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_ed448_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_ed448_to_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_OSSL_current_der_encoder_functions[];
 extern const OSSL_DISPATCH ossl_ed448_to_text_encoder_functions[];
 
-extern const OSSL_DISPATCH ossl_ec_to_der_encoder_functions[];
-extern const OSSL_DISPATCH ossl_ec_to_pem_encoder_functions[];
-extern const OSSL_DISPATCH ossl_ec_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_x448_to_PKCS8_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_PKCS8_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_text_encoder_functions[];
 
 /* Decoders */
 extern const OSSL_DISPATCH ossl_PKCS8_der_to_dh_decoder_functions[];
diff --git a/test/endecode_test.c b/test/endecode_test.c
index 57ba6774e4..4a86e71b76 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -89,8 +89,9 @@ static EVP_PKEY *make_key(const char *type, EVP_PKEY *template,
  * (TEST_strn2_eq(), for example).
  */
 
-typedef int (encoder)(void **encoded, long *encoded_len, void *object,
-                      const char *output_type, int selection,
+typedef int (encoder)(void **encoded, long *encoded_len,
+                      void *object, int selection,
+                      const char *output_type, const char *output_structure,
                       const char *pass, const char *pcipher);
 typedef int (decoder)(void **object, void *encoded, long encoded_len,
                       const char *keytype, const char *input_type,
@@ -103,7 +104,8 @@ typedef void (dumper)(const char *label, const void *data, size_t data_len);
 #define FLAG_DECODE_WITH_TYPE   0x0001
 
 static int test_encode_decode(const char *type, EVP_PKEY *pkey,
-                              const char *output_type, int selection,
+                              int selection, const char *output_type,
+                              const char *output_structure,
                               const char *pass, const char *pcipher,
                               encoder *encode_cb, decoder *decode_cb,
                               tester *test_cb, checker *check_cb,
@@ -121,14 +123,14 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
      * encoding |pkey2| as well.  That last encoding is for checking and
      * dumping purposes.
      */
-    if (!TEST_true(encode_cb(&encoded, &encoded_len, pkey, output_type,
-                             selection, pass, pcipher))
+    if (!TEST_true(encode_cb(&encoded, &encoded_len, pkey, selection,
+                             output_type, output_structure, pass, pcipher))
         || !TEST_true(check_cb(type, encoded, encoded_len))
         || !TEST_true(decode_cb((void **)&pkey2, encoded, encoded_len,
                                 (flags & FLAG_DECODE_WITH_TYPE ? type : NULL),
                                 output_type, selection, pass))
-        || !TEST_true(encode_cb(&encoded2, &encoded2_len, pkey2, output_type,
-                                selection, pass, pcipher)))
+        || !TEST_true(encode_cb(&encoded2, &encoded2_len, pkey2, selection,
+                                output_type, output_structure, pass, pcipher)))
         goto end;
 
     if (selection == OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) {
@@ -163,11 +165,12 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
     return ok;
 }
 
-/* Encoding and desencoding methods */
+/* Encoding and decoding methods */
 
 static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
-                                void *object,
-                                const char *output_type, int selection,
+                                void *object, int selection,
+                                const char *output_type,
+                                const char *output_structure,
                                 const char *pass, const char *pcipher)
 {
     EVP_PKEY *pkey = object;
@@ -177,9 +180,11 @@ static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
     const unsigned char *upass = (const unsigned char *)pass;
     int ok = 0;
 
-    if (!TEST_ptr(ectx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, output_type,
-                                                          selection,
+    if (!TEST_ptr(ectx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection,
+                                                          output_type,
+                                                          output_structure,
                                                           NULL, NULL))
+        || !TEST_int_gt(OSSL_ENCODER_CTX_get_num_encoders(ectx), 0)
         || (pass != NULL
             && !TEST_true(OSSL_ENCODER_CTX_set_passphrase(ectx, upass,
                                                           strlen(pass))))
@@ -275,8 +280,9 @@ static int decode_EVP_PKEY_prov(void **object, void *encoded, long encoded_len,
 }
 
 static int encode_EVP_PKEY_legacy_PEM(void **encoded, long *encoded_len,
-                                      void *object,
-                                      const char *output_type, int selection,
+                                      void *object, ossl_unused int selection,
+                                      ossl_unused const char *output_type,
+                                      ossl_unused const char *output_structure,
                                       const char *pass, const char *pcipher)
 {
     EVP_PKEY *pkey = object;
@@ -314,9 +320,9 @@ static int encode_EVP_PKEY_legacy_PEM(void **encoded, long *encoded_len,
 
 #ifndef OPENSSL_NO_DSA
 static int encode_EVP_PKEY_MSBLOB(void **encoded, long *encoded_len,
-                                  void *object,
+                                  void *object, int selection,
                                   ossl_unused const char *output_type,
-                                  ossl_unused int selection,
+                                  ossl_unused const char *output_structure,
                                   ossl_unused const char *pass,
                                   ossl_unused const char *pcipher)
 {
@@ -325,38 +331,18 @@ static int encode_EVP_PKEY_MSBLOB(void **encoded, long *encoded_len,
     BUF_MEM *mem_buf = NULL;
     int ok = 0;
 
-    if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
-        || !TEST_int_ge(i2b_PrivateKey_bio(mem_ser, pkey), 0)
-        || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
-        || !TEST_ptr(*encoded = mem_buf->data)
-        || !TEST_long_gt(*encoded_len = mem_buf->length, 0))
+    if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem())))
         goto end;
 
-    /* Detach the encoded output */
-    mem_buf->data = NULL;
-    mem_buf->length = 0;
-    ok = 1;
- end:
-    BIO_free(mem_ser);
-    return ok;
-}
-
-static int encode_public_EVP_PKEY_MSBLOB(void **encoded,
-                                         long *encoded_len,
-                                         void *object,
-                                         ossl_unused const char *output_type,
-                                         ossl_unused int selection,
-                                         ossl_unused const char *pass,
-                                         ossl_unused const char *pcipher)
-{
-    EVP_PKEY *pkey = object;
-    BIO *mem_ser = NULL;
-    BUF_MEM *mem_buf = NULL;
-    int ok = 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        if (!TEST_int_ge(i2b_PrivateKey_bio(mem_ser, pkey), 0))
+            goto end;
+    } else {
+        if (!TEST_int_ge(i2b_PublicKey_bio(mem_ser, pkey), 0))
+            goto end;
+    }
 
-    if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
-        || !TEST_int_ge(i2b_PublicKey_bio(mem_ser, pkey), 0)
-        || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
+    if (!TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
         || !TEST_ptr(*encoded = mem_buf->data)
         || !TEST_long_gt(*encoded_len = mem_buf->length, 0))
         goto end;
@@ -379,9 +365,9 @@ static int pass_pw(char *buf, int size, int rwflag, void *userdata)
 }
 
 static int encode_EVP_PKEY_PVK(void **encoded, long *encoded_len,
-                               void *object,
+                               void *object, int selection,
                                ossl_unused const char *output_type,
-                               ossl_unused int selection,
+                               ossl_unused const char *output_structure,
                                const char *pass,
                                ossl_unused const char *pcipher)
 {
@@ -391,7 +377,9 @@ static int encode_EVP_PKEY_PVK(void **encoded, long *encoded_len,
     int enc = (pass != NULL);
     int ok = 0;
 
-    if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
+    if (!TEST_true(ossl_assert((selection
+                                & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0))
+        || !TEST_ptr(mem_ser = BIO_new(BIO_s_mem()))
         || !TEST_int_ge(i2b_PVK_bio(mem_ser, pkey, enc,
                                     pass_pw, (void *)pass), 0)
         || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0)
@@ -486,9 +474,9 @@ static int check_unprotected_PKCS8_DER(const char *type,
 static int test_unprotected_via_DER(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "DER", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "DER", "pkcs8", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_mem, check_unprotected_PKCS8_DER,
                               dump_der, 0);
@@ -506,10 +494,9 @@ static int check_unprotected_PKCS8_PEM(const char *type,
 
 static int test_unprotected_via_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+    return test_encode_decode(type, key, OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "PEM", "pkcs8", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_text, check_unprotected_PKCS8_PEM,
                               dump_pem, 0);
@@ -554,9 +541,8 @@ static int check_params_PEM(const char *type,
 
 static int test_params_via_DER(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key,
-                              "DER", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+    return test_encode_decode(type, key, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              "DER", "type-specific", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_mem, check_params_DER,
                               dump_der, FLAG_DECODE_WITH_TYPE);
@@ -564,9 +550,8 @@ static int test_params_via_DER(const char *type, EVP_PKEY *key)
 
 static int test_params_via_PEM(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+    return test_encode_decode(type, key, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                              "PEM", "type-specific", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_text, check_params_PEM,
                               dump_pem, 0);
@@ -587,9 +572,9 @@ static int check_unprotected_legacy_PEM(const char *type,
 static int test_unprotected_via_legacy_PEM(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "PEM", "type-specific", NULL, NULL,
                               encode_EVP_PKEY_legacy_PEM, decode_EVP_PKEY_prov,
                               test_text, check_unprotected_legacy_PEM,
                               dump_pem, 0);
@@ -609,9 +594,9 @@ static int check_MSBLOB(const char *type, const void *data, size_t data_len)
 static int test_unprotected_via_MSBLOB(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "MSBLOB", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "MSBLOB", NULL, NULL, NULL,
                               encode_EVP_PKEY_MSBLOB, decode_EVP_PKEY_prov,
                               test_mem, check_MSBLOB,
                               dump_der, 0);
@@ -630,9 +615,9 @@ static int check_PVK(const char *type, const void *data, size_t data_len)
 static int test_unprotected_via_PVK(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PVK", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "PVK", NULL, NULL, NULL,
                               encode_EVP_PKEY_PVK, decode_EVP_PKEY_prov,
                               test_mem, check_PVK,
                               dump_der, 0);
@@ -657,9 +642,9 @@ static int check_protected_PKCS8_DER(const char *type,
 static int test_protected_via_DER(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "DER", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              pass, pass_cipher,
+                              "DER", "pkcs8", pass, pass_cipher,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_mem, check_protected_PKCS8_DER,
                               dump_der, 0);
@@ -678,9 +663,9 @@ static int check_protected_PKCS8_PEM(const char *type,
 static int test_protected_via_PEM(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              pass, pass_cipher,
+                              "PEM", "pkcs8", pass, pass_cipher,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_text, check_protected_PKCS8_PEM,
                               dump_pem, 0);
@@ -702,9 +687,9 @@ static int check_protected_legacy_PEM(const char *type,
 static int test_protected_via_legacy_PEM(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              pass, pass_cipher,
+                              "PEM", "type-specific", pass, pass_cipher,
                               encode_EVP_PKEY_legacy_PEM, decode_EVP_PKEY_prov,
                               test_text, check_protected_legacy_PEM,
                               dump_pem, 0);
@@ -714,9 +699,9 @@ static int test_protected_via_legacy_PEM(const char *type, EVP_PKEY *key)
 static int test_protected_via_PVK(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PVK", OSSL_KEYMGMT_SELECT_KEYPAIR
+                              OSSL_KEYMGMT_SELECT_KEYPAIR
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              pass, NULL,
+                              "PVK", NULL, pass, NULL,
                               encode_EVP_PKEY_PVK, decode_EVP_PKEY_prov,
                               test_mem, check_PVK, dump_der, 0);
 }
@@ -735,9 +720,9 @@ static int check_public_DER(const char *type, const void *data, size_t data_len)
 static int test_public_via_DER(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "DER", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                              OSSL_KEYMGMT_SELECT_PUBLIC_KEY
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "DER", "SubjectPublicKeyInfo", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_mem, check_public_DER, dump_der, 0);
 }
@@ -755,9 +740,9 @@ static int check_public_PEM(const char *type, const void *data, size_t data_len)
 static int test_public_via_PEM(const char *type, EVP_PKEY *key)
 {
     return test_encode_decode(type, key,
-                              "PEM", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+                              OSSL_KEYMGMT_SELECT_PUBLIC_KEY
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
+                              "PEM", "SubjectPublicKeyInfo", NULL, NULL,
                               encode_EVP_PKEY_prov, decode_EVP_PKEY_prov,
                               test_text, check_public_PEM, dump_pem, 0);
 }
@@ -776,12 +761,10 @@ static int check_public_MSBLOB(const char *type,
 
 static int test_public_via_MSBLOB(const char *type, EVP_PKEY *key)
 {
-    return test_encode_decode(type, key,
-                              "MSBLOB", OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+    return test_encode_decode(type, key, OSSL_KEYMGMT_SELECT_PUBLIC_KEY
                               | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
-                              NULL, NULL,
-                              encode_public_EVP_PKEY_MSBLOB,
-                              decode_EVP_PKEY_prov,
+                              "MSBLOB", NULL, NULL, NULL,
+                              encode_EVP_PKEY_MSBLOB, decode_EVP_PKEY_prov,
                               test_mem, check_public_MSBLOB, dump_der, 0);
 }
 #endif
diff --git a/test/evp_libctx_test.c b/test/evp_libctx_test.c
index e8d41ab2a7..a1540dd4b9 100644
--- a/test/evp_libctx_test.c
+++ b/test/evp_libctx_test.c
@@ -25,8 +25,10 @@
 #include <openssl/dsa.h>
 #include <openssl/dh.h>
 #include <openssl/safestack.h>
+#include <openssl/core_dispatch.h>
 #include <openssl/core_names.h>
 #include <openssl/x509.h>
+#include <openssl/encoder.h>
 #include "testutil.h"
 #include "internal/nelem.h"
 #include "crypto/bn_dh.h"   /* _bignum_ffdhe2048_p */
@@ -448,19 +450,26 @@ static int rsa_keygen(int bits, EVP_PKEY **pub, EVP_PKEY **priv)
     EVP_PKEY_CTX *keygen_ctx = NULL;
     unsigned char *pub_der = NULL;
     const unsigned char *pp = NULL;
-    long len = 0;
+    size_t len = 0;
+    OSSL_ENCODER_CTX *ectx = NULL;
 
     if (!TEST_ptr(keygen_ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", NULL))
         || !TEST_int_gt(EVP_PKEY_keygen_init(keygen_ctx), 0)
         || !TEST_true(EVP_PKEY_CTX_set_rsa_keygen_bits(keygen_ctx, bits))
         || !TEST_int_gt(EVP_PKEY_keygen(keygen_ctx, priv), 0)
-        || !TEST_int_gt(len = i2d_PublicKey(*priv, &pub_der), 0))
+        || !TEST_ptr(ectx =
+                     OSSL_ENCODER_CTX_new_by_EVP_PKEY(*priv,
+                                                      EVP_PKEY_PUBLIC_KEY,
+                                                      "DER", "type-specific",
+                                                      libctx, NULL))
+        || !TEST_true(OSSL_ENCODER_to_data(ectx, &pub_der, &len)))
         goto err;
     pp = pub_der;
     if (!TEST_ptr(d2i_PublicKey(EVP_PKEY_RSA, pub, &pp, len)))
         goto err;
     ret = 1;
 err:
+    OSSL_ENCODER_CTX_free(ectx);
     OPENSSL_free(pub_der);
     EVP_PKEY_CTX_free(keygen_ctx);
     return ret;
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index 6b56fc7997..7d12a919c1 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -155,7 +155,7 @@ static int test_print_key_using_pem(const char *alg, const EVP_PKEY *pk)
 static int test_print_key_type_using_encoder(const char *alg, int type,
                                              const EVP_PKEY *pk)
 {
-    const char *output_type;
+    const char *output_type, *output_structure;
     int selection;
     OSSL_ENCODER_CTX *ctx = NULL;
     BIO *membio = BIO_new(BIO_s_mem());
@@ -164,36 +164,42 @@ static int test_print_key_type_using_encoder(const char *alg, int type,
     switch (type) {
     case PRIV_TEXT:
         output_type = "TEXT";
+        output_structure = NULL;
         selection = OSSL_KEYMGMT_SELECT_KEYPAIR
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PRIV_PEM:
         output_type = "PEM";
+        output_structure = "pkcs8";
         selection = OSSL_KEYMGMT_SELECT_KEYPAIR
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PRIV_DER:
         output_type = "DER";
+        output_structure = "pkcs8";
         selection = OSSL_KEYMGMT_SELECT_KEYPAIR
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_TEXT:
         output_type = "TEXT";
+        output_structure = NULL;
         selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_PEM:
         output_type = "PEM";
+        output_structure = "SubjectPublicKeyInfo";
         selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
 
     case PUB_DER:
         output_type = "DER";
+        output_structure = "SubjectPublicKeyInfo";
         selection = OSSL_KEYMGMT_SELECT_PUBLIC_KEY
             | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
         break;
@@ -208,8 +214,9 @@ static int test_print_key_type_using_encoder(const char *alg, int type,
 
     /* Make a context, it's valid for several prints */
     TEST_note("Setting up a OSSL_ENCODER context with passphrase");
-    if (!TEST_ptr(ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pk, output_type,
-                                                         selection,
+    if (!TEST_ptr(ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pk, selection,
+                                                         output_type,
+                                                         output_structure,
                                                          NULL, NULL))
         /* Check that this operation is supported */
         || !TEST_int_ne(OSSL_ENCODER_CTX_get_num_encoders(ctx), 0))
diff --git a/test/recipes/30-test_evp_libctx.t b/test/recipes/30-test_evp_libctx.t
index 8c36a9e24e..2ba20057e3 100644
--- a/test/recipes/30-test_evp_libctx.t
+++ b/test/recipes/30-test_evp_libctx.t
@@ -31,7 +31,7 @@ plan tests =>
     + 1;
 
 unless ($no_fips) {
-    @test_args = ("-config", srctop_file("test","fips.cnf"),
+    @test_args = ("-config", srctop_file("test","fips-and-base.cnf"),
                   "-provider", "fips");
 
     ok(run(app(['openssl', 'fipsinstall',
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 40e1fcb43a..5bd793efbc 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5288,3 +5288,5 @@ EVP_PKEY_get1_encoded_public_key        ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_CTX_set_selection          ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_CTX_set_input_structure    ?	3_0_0	EXIST::FUNCTION:
 OSSL_DECODER_INSTANCE_get_input_structure ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_CTX_set_output_structure   ?	3_0_0	EXIST::FUNCTION:
+OSSL_ENCODER_INSTANCE_get_output_structure ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list