[openssl] master update

Richard Levitte levitte at openssl.org
Fri Mar 19 15:47:03 UTC 2021


The branch master has been updated
       via  aff442dc38707a5ab4a7acc811c9ef10cbdd3346 (commit)
       via  695c96ba5160a8053c2ff98c0f8971b44c195cbb (commit)
       via  55d9ccca8eec8ec57ab85175dea908f8b76fd4e1 (commit)
       via  c640b687855de512138d4c4b09b71c080c0adfeb (commit)
       via  ff1c10d920379854a80d43b69a58e20f1e76e76c (commit)
       via  f4e46b817d587c300faa04fd061ddcdea3dd64c9 (commit)
       via  cf333799979755dd46193b49c15db0afd262c6a0 (commit)
       via  e0be34beee9ef8ebab49c51581f796e013600b77 (commit)
       via  06f6761280285401f3aaa31502614f7c8dd0c4fb (commit)
       via  65ef000ec26e6ec2fab2c31f74be1b76275bbbe5 (commit)
      from  2d101b0f493a3066c5ea7152c00c44d70fcea4d8 (commit)


- Log -----------------------------------------------------------------
commit aff442dc38707a5ab4a7acc811c9ef10cbdd3346
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 18 13:15:18 2021 +0100

    Make ossl_d2i_PUBKEY_legacy() completely libcrypto internal
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit 695c96ba5160a8053c2ff98c0f8971b44c195cbb
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 18 13:13:47 2021 +0100

    Make evp_privatekey_from_binary() completely libcrypto internal
    
    We also rename it to d2i_PrivateKey_legacy(), to match d2i_PrivateKey_decoder()
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit 55d9ccca8eec8ec57ab85175dea908f8b76fd4e1
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Mar 17 19:17:32 2021 +0100

    TEST: Clarify and adjust test/recipes/30-test_evp.t
    
    There are a few test cases at the end of test/recipes/30-test_evp.t,
    which are designed to check that loading DSA keys when DSA is disabled,
    or SM2 keys when SM2 is disables fail in an understandable way.  These
    needed a small adjustment.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit c640b687855de512138d4c4b09b71c080c0adfeb
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Mar 17 19:17:03 2021 +0100

    STORE: Use the same error avoidance criteria as for the DER->key decoder
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit ff1c10d920379854a80d43b69a58e20f1e76e76c
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Mar 17 08:52:36 2021 +0100

    TEST: Modify test/endecode_test.c to give the decoder callback the structure
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit f4e46b817d587c300faa04fd061ddcdea3dd64c9
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Mar 15 15:05:59 2021 +0100

    PROV: Add type specific MSBLOB and PVK decoding for the MS->key decoders
    
    To make this cleaner, decoder_ms2key.c is split into decoder_msblob2key.c
    and decoder_pvk2key.c.
    
    This required a great deal of refactoring of crypto/pem/pvkfmt.c, to
    make cleaner internal functions that our decoder implementations can
    use.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit cf333799979755dd46193b49c15db0afd262c6a0
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Mar 18 10:41:53 2021 +0100

    PROV: Add type specific PKCS#8 decoding to the DER->key decoders
    
    This required refactoring a number of functions from the diverse
    EVP_PKEY_ASN1_METHOD implementations to become shared backend
    functions.  It also meant modifying a few of them to return pointers
    to our internal RSA / DSA/ DH / EC_KEY, ... structures instead of
    manipulating an EVP_PKEY pointer directly, letting the caller do the
    latter.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit e0be34beee9ef8ebab49c51581f796e013600b77
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Jan 29 04:47:47 2021 +0100

    PROV: Add RSA-PSS specific OSSL_FUNC_KEYMGMT_LOAD function
    
    The OSSL_FUNC_KEYMGMT_LOAD function for both plain RSA and RSA-PSS
    keys now also check that the key to be loaded is the correct type,
    and refuse to load it if it's not.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit 06f6761280285401f3aaa31502614f7c8dd0c4fb
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jan 28 08:56:53 2021 +0100

    PROV: Add type specific SubjectPublicKeyInfo decoding to the DER->key decoders
    
    This makes it possible to use d2i_<TYPE>_PUBKEY instead of the generic
    d2i_PUBKEY()
    
    This required adding a number of new d2i_<TYPE>_PUBKEY functions.
    These are all kept internal.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

commit 65ef000ec26e6ec2fab2c31f74be1b76275bbbe5
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Jan 28 08:48:55 2021 +0100

    PROV: Refactor DER->key decoder
    
    The decoding of DER into keys with keytype specific routines depended
    entirely on the absence of the generic algo specific C type from
    EVP_PKEYs.  That is not necessary, and may even prove to be a bit of a
    disadvantage, depending on what libcrypto has to offer in terms of
    type specific d2i functionality for different kinds of input
    structures.
    
    To remedy, we try with all available type specific functions first,
    and only turn to the general d2i functions (those that return an
    EVP_PKEY) as a last resort.
    
    Furthermore, there are cases where the decoder might not get the key
    type it expected.  This may happen when certain key types that share
    the same OpenSSL structure may be mixed up somehow.  The known cases
    are EC vs SM2 and RSA vs RSA-PSS.
    
    To remedy, we add the possibility to specify a checking function that
    can check if the key that was decoded meets decoder expectations.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14314)

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

Summary of changes:
 crypto/asn1/d2i_pr.c                               |  10 +-
 crypto/dh/dh_ameth.c                               |  50 +--
 crypto/dh/dh_backend.c                             |  66 ++++
 crypto/dsa/dsa_ameth.c                             |  63 +--
 crypto/dsa/dsa_backend.c                           |  73 ++++
 crypto/ec/ec_ameth.c                               |  84 +---
 crypto/ec/ec_backend.c                             |  79 ++++
 crypto/ec/ecx_backend.c                            | 109 ++++++
 crypto/ec/ecx_meth.c                               | 155 +++-----
 crypto/err/openssl.txt                             |   2 +
 crypto/pem/pem_err.c                               |   6 +-
 crypto/pem/pvkfmt.c                                | 272 +++++++++----
 crypto/rsa/rsa_ameth.c                             | 163 +-------
 crypto/rsa/rsa_backend.c                           | 155 ++++++++
 crypto/rsa/rsa_lib.c                               |  12 +
 crypto/x509/x_pubkey.c                             | 271 ++++++++++++-
 include/crypto/dh.h                                |   3 +
 include/crypto/dsa.h                               |   3 +
 include/crypto/ec.h                                |   7 +
 include/crypto/ecx.h                               |  16 +
 include/crypto/evp.h                               |   4 -
 include/crypto/pem.h                               |  21 +
 include/crypto/pemerr.h                            |   2 +-
 include/crypto/rsa.h                               |  10 +
 include/crypto/types.h                             |   6 +
 include/crypto/x509.h                              |  26 +-
 include/openssl/pemerr.h                           |   2 +
 providers/implementations/encode_decode/build.info |   3 +-
 .../implementations/encode_decode/decode_der2key.c | 430 +++++++++++++--------
 .../implementations/encode_decode/decode_ms2key.c  | 273 -------------
 .../encode_decode/decode_msblob2key.c              | 284 ++++++++++++++
 .../implementations/encode_decode/decode_pvk2key.c | 232 +++++++++++
 providers/implementations/keymgmt/rsa_kmgmt.c      |  20 +-
 .../implementations/storemgmt/file_store_der2obj.c |   4 +-
 test/endecode_test.c                               |  15 +-
 test/recipes/30-test_evp.t                         |  52 ++-
 36 files changed, 1974 insertions(+), 1009 deletions(-)
 delete mode 100644 providers/implementations/encode_decode/decode_ms2key.c
 create mode 100644 providers/implementations/encode_decode/decode_msblob2key.c
 create mode 100644 providers/implementations/encode_decode/decode_pvk2key.c

diff --git a/crypto/asn1/d2i_pr.c b/crypto/asn1/d2i_pr.c
index 4da5a0c9be..5d95c9e042 100644
--- a/crypto/asn1/d2i_pr.c
+++ b/crypto/asn1/d2i_pr.c
@@ -69,9 +69,9 @@ err:
     return NULL;
 }
 
-EVP_PKEY *evp_privatekey_from_binary(int keytype, EVP_PKEY **a,
-                                     const unsigned char **pp, long length,
-                                     OSSL_LIB_CTX *libctx, const char *propq)
+static EVP_PKEY *
+d2i_PrivateKey_legacy(int keytype, EVP_PKEY **a, const unsigned char **pp,
+                      long length, OSSL_LIB_CTX *libctx, const char *propq)
 {
     EVP_PKEY *ret;
     const unsigned char *p = *pp;
@@ -144,7 +144,7 @@ EVP_PKEY *d2i_PrivateKey_ex(int keytype, EVP_PKEY **a, const unsigned char **pp,
     ret = d2i_PrivateKey_decoder(keytype, a, pp, length, libctx, propq);
     /* try the legacy path if the decoder failed */
     if (ret == NULL)
-        ret = evp_privatekey_from_binary(keytype, a, pp, length, libctx, propq);
+        ret = d2i_PrivateKey_legacy(keytype, a, pp, length, libctx, propq);
     return ret;
 }
 
@@ -203,7 +203,7 @@ static EVP_PKEY *d2i_AutoPrivateKey_legacy(EVP_PKEY **a,
         keytype = EVP_PKEY_RSA;
     }
     sk_ASN1_TYPE_pop_free(inkey, ASN1_TYPE_free);
-    return evp_privatekey_from_binary(keytype, a, pp, length, libctx, propq);
+    return d2i_PrivateKey_legacy(keytype, a, pp, length, libctx, propq);
 }
 
 /*
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index 08656877f3..ffaf41d802 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -163,53 +163,15 @@ static int dh_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
 
 static int dh_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
 {
-    const unsigned char *p, *pm;
-    int pklen, pmlen;
-    int ptype;
-    const void *pval;
-    const ASN1_STRING *pstr;
-    const X509_ALGOR *palg;
-    ASN1_INTEGER *privkey = NULL;
-    DH *dh = NULL;
-
-    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
-        return 0;
-
-    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-    if (ptype != V_ASN1_SEQUENCE)
-        goto decerr;
-    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
-        goto decerr;
-
-    pstr = pval;
-    pm = pstr->data;
-    pmlen = pstr->length;
-    if ((dh = d2i_dhp(pkey, &pm, pmlen)) == NULL)
-        goto decerr;
+    int ret = 0;
+    DH *dh = ossl_dh_key_from_pkcs8(p8, NULL, NULL);
 
-    /* We have parameters now set private key */
-    if ((dh->priv_key = BN_secure_new()) == NULL
-        || !ASN1_INTEGER_to_BN(privkey, dh->priv_key)) {
-        ERR_raise(ERR_LIB_DH, DH_R_BN_ERROR);
-        goto dherr;
+    if (dh != NULL) {
+        ret = 1;
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh);
     }
-    /* Calculate public key, increments dirty_cnt */
-    if (!DH_generate_key(dh))
-        goto dherr;
-
-    EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh);
 
-    ASN1_STRING_clear_free(privkey);
-
-    return 1;
-
- decerr:
-    ERR_raise(ERR_LIB_DH, EVP_R_DECODE_ERROR);
- dherr:
-    DH_free(dh);
-    ASN1_STRING_clear_free(privkey);
-    return 0;
+    return ret;
 }
 
 static int dh_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
diff --git a/crypto/dh/dh_backend.c b/crypto/dh/dh_backend.c
index c848cb4870..8da830f9d8 100644
--- a/crypto/dh/dh_backend.c
+++ b/crypto/dh/dh_backend.c
@@ -13,6 +13,7 @@
  */
 #include "internal/deprecated.h"
 
+#include <openssl/err.h>
 #include <openssl/core_names.h>
 #include "internal/param_build_set.h"
 #include "crypto/dh.h"
@@ -115,3 +116,68 @@ int ossl_dh_key_todata(DH *dh, OSSL_PARAM_BLD *bld, OSSL_PARAM params[])
 
     return 1;
 }
+
+#ifndef FIPS_MODULE
+DH *ossl_dh_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                           OSSL_LIB_CTX *libctx, const char *propq)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    const X509_ALGOR *palg;
+    BIGNUM *privkey_bn = NULL;
+    ASN1_INTEGER *privkey = NULL;
+    DH *dh = NULL;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8inf))
+        return 0;
+
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if (ptype != V_ASN1_SEQUENCE)
+        goto decerr;
+    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
+        goto decerr;
+
+    pstr = pval;
+    pm = pstr->data;
+    pmlen = pstr->length;
+    switch (OBJ_obj2nid(palg->algorithm)) {
+    case NID_dhKeyAgreement:
+        dh = d2i_DHparams(NULL, &pm, pmlen);
+        break;
+    case NID_dhpublicnumber:
+        dh = d2i_DHxparams(NULL, &pm, pmlen);
+        break;
+    default:
+        goto decerr;
+    }
+    if (dh == NULL)
+        goto decerr;
+
+    /* We have parameters now set private key */
+    if ((privkey_bn = BN_secure_new()) == NULL
+        || !ASN1_INTEGER_to_BN(privkey, privkey_bn)) {
+        ERR_raise(ERR_LIB_DH, DH_R_BN_ERROR);
+        goto dherr;
+    }
+    if (!DH_set0_key(dh, NULL, privkey_bn))
+        goto dherr;
+    /* Calculate public key, increments dirty_cnt */
+    if (!DH_generate_key(dh))
+        goto dherr;
+
+    goto done;
+
+ decerr:
+    ERR_raise(ERR_LIB_DH, EVP_R_DECODE_ERROR);
+ dherr:
+    DH_free(dh);
+    dh = NULL;
+ done:
+    ASN1_STRING_clear_free(privkey);
+    return dh;
+}
+#endif
diff --git a/crypto/dsa/dsa_ameth.c b/crypto/dsa/dsa_ameth.c
index 50ecd3ed3b..e4c739daf9 100644
--- a/crypto/dsa/dsa_ameth.c
+++ b/crypto/dsa/dsa_ameth.c
@@ -149,69 +149,14 @@ static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
 
 static int dsa_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
 {
-    const unsigned char *p, *pm;
-    int pklen, pmlen;
-    int ptype;
-    const void *pval;
-    const ASN1_STRING *pstr;
-    const X509_ALGOR *palg;
-    ASN1_INTEGER *privkey = NULL;
-    BN_CTX *ctx = NULL;
-
-    DSA *dsa = NULL;
-
     int ret = 0;
+    DSA *dsa = ossl_dsa_key_from_pkcs8(p8, NULL, NULL);
 
-    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
-        return 0;
-    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
-        goto decerr;
-    if (privkey->type == V_ASN1_NEG_INTEGER || ptype != V_ASN1_SEQUENCE)
-        goto decerr;
-
-    pstr = pval;
-    pm = pstr->data;
-    pmlen = pstr->length;
-    if ((dsa = d2i_DSAparams(NULL, &pm, pmlen)) == NULL)
-        goto decerr;
-    /* We have parameters now set private key */
-    if ((dsa->priv_key = BN_secure_new()) == NULL
-        || !ASN1_INTEGER_to_BN(privkey, dsa->priv_key)) {
-        ERR_raise(ERR_LIB_DSA, DSA_R_BN_ERROR);
-        goto dsaerr;
-    }
-    /* Calculate public key */
-    if ((dsa->pub_key = BN_new()) == NULL) {
-        ERR_raise(ERR_LIB_DSA, ERR_R_MALLOC_FAILURE);
-        goto dsaerr;
-    }
-    if ((ctx = BN_CTX_new()) == NULL) {
-        ERR_raise(ERR_LIB_DSA, ERR_R_MALLOC_FAILURE);
-        goto dsaerr;
-    }
-
-    BN_set_flags(dsa->priv_key, BN_FLG_CONSTTIME);
-    if (!BN_mod_exp(dsa->pub_key, dsa->params.g, dsa->priv_key, dsa->params.p,
-                    ctx)) {
-        ERR_raise(ERR_LIB_DSA, DSA_R_BN_ERROR);
-        goto dsaerr;
+    if (dsa != NULL) {
+        ret = 1;
+        EVP_PKEY_assign_DSA(pkey, dsa);
     }
 
-    dsa->dirty_cnt++;
-    EVP_PKEY_assign_DSA(pkey, dsa);
-
-    ret = 1;
-    goto done;
-
- decerr:
-    ERR_raise(ERR_LIB_DSA, DSA_R_DECODE_ERROR);
- dsaerr:
-    DSA_free(dsa);
- done:
-    BN_CTX_free(ctx);
-    ASN1_STRING_clear_free(privkey);
     return ret;
 }
 
diff --git a/crypto/dsa/dsa_backend.c b/crypto/dsa/dsa_backend.c
index e6f8f3645e..f3e54aeb13 100644
--- a/crypto/dsa/dsa_backend.c
+++ b/crypto/dsa/dsa_backend.c
@@ -14,6 +14,7 @@
 #include "internal/deprecated.h"
 
 #include <openssl/core_names.h>
+#include <openssl/err.h>
 #include "crypto/dsa.h"
 
 /*
@@ -54,3 +55,75 @@ int ossl_dsa_key_fromdata(DSA *dsa, const OSSL_PARAM params[])
     BN_free(pub_key);
     return 0;
 }
+
+#ifndef FIPS_MODULE
+DSA *ossl_dsa_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                             OSSL_LIB_CTX *libctx, const char *propq)
+{
+    const unsigned char *p, *pm;
+    int pklen, pmlen;
+    int ptype;
+    const void *pval;
+    const ASN1_STRING *pstr;
+    const X509_ALGOR *palg;
+    ASN1_INTEGER *privkey = NULL;
+    const BIGNUM *dsa_p, *dsa_g;
+    BIGNUM *dsa_pubkey = NULL, *dsa_privkey = NULL;
+    BN_CTX *ctx = NULL;
+
+    DSA *dsa = NULL;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8inf))
+        return 0;
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+
+    if ((privkey = d2i_ASN1_INTEGER(NULL, &p, pklen)) == NULL)
+        goto decerr;
+    if (privkey->type == V_ASN1_NEG_INTEGER || ptype != V_ASN1_SEQUENCE)
+        goto decerr;
+
+    pstr = pval;
+    pm = pstr->data;
+    pmlen = pstr->length;
+    if ((dsa = d2i_DSAparams(NULL, &pm, pmlen)) == NULL)
+        goto decerr;
+    /* We have parameters now set private key */
+    if ((dsa_privkey = BN_secure_new()) == NULL
+        || !ASN1_INTEGER_to_BN(privkey, dsa_privkey)) {
+        ERR_raise(ERR_LIB_DSA, DSA_R_BN_ERROR);
+        goto dsaerr;
+    }
+    /* Calculate public key */
+    if ((dsa_pubkey = BN_new()) == NULL) {
+        ERR_raise(ERR_LIB_DSA, ERR_R_MALLOC_FAILURE);
+        goto dsaerr;
+    }
+    if ((ctx = BN_CTX_new()) == NULL) {
+        ERR_raise(ERR_LIB_DSA, ERR_R_MALLOC_FAILURE);
+        goto dsaerr;
+    }
+
+    dsa_p = DSA_get0_p(dsa);
+    dsa_g = DSA_get0_g(dsa);
+    BN_set_flags(dsa_privkey, BN_FLG_CONSTTIME);
+    if (!BN_mod_exp(dsa_pubkey, dsa_g, dsa_privkey, dsa_p, ctx)) {
+        ERR_raise(ERR_LIB_DSA, DSA_R_BN_ERROR);
+        goto dsaerr;
+    }
+    DSA_set0_key(dsa, dsa_pubkey, dsa_privkey);
+
+    goto done;
+
+ decerr:
+    ERR_raise(ERR_LIB_DSA, DSA_R_DECODE_ERROR);
+ dsaerr:
+    BN_free(dsa_privkey);
+    BN_free(dsa_pubkey);
+    DSA_free(dsa);
+    dsa = NULL;
+ done:
+    BN_CTX_free(ctx);
+    ASN1_STRING_clear_free(privkey);
+    return dsa;
+}
+#endif
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index d447fa4d1c..69370d6bc1 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -100,59 +100,10 @@ static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
     return 0;
 }
 
-static EC_KEY *eckey_type2param(int ptype, const void *pval,
-                                OSSL_LIB_CTX *libctx, const char *propq)
-{
-    EC_KEY *eckey = NULL;
-    EC_GROUP *group = NULL;
-
-    if ((eckey = EC_KEY_new_ex(libctx, propq)) == NULL) {
-        ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
-        goto ecerr;
-    }
-
-    if (ptype == V_ASN1_SEQUENCE) {
-        const ASN1_STRING *pstr = pval;
-        const unsigned char *pm = pstr->data;
-        int pmlen = pstr->length;
-
-
-        if (d2i_ECParameters(&eckey, &pm, pmlen) == NULL) {
-            ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
-            goto ecerr;
-        }
-    } else if (ptype == V_ASN1_OBJECT) {
-        const ASN1_OBJECT *poid = pval;
-
-        /*
-         * type == V_ASN1_OBJECT => the parameters are given by an asn1 OID
-         */
-
-        group = EC_GROUP_new_by_curve_name_ex(libctx, propq, OBJ_obj2nid(poid));
-        if (group == NULL)
-            goto ecerr;
-        EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
-        if (EC_KEY_set_group(eckey, group) == 0)
-            goto ecerr;
-        EC_GROUP_free(group);
-    } else {
-        ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
-        goto ecerr;
-    }
-
-    return eckey;
-
- ecerr:
-    EC_KEY_free(eckey);
-    EC_GROUP_free(group);
-    return NULL;
-}
-
 static int eckey_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
 {
     const unsigned char *p = NULL;
-    const void *pval;
-    int ptype, pklen;
+    int pklen;
     EC_KEY *eckey = NULL;
     X509_ALGOR *palg;
     OSSL_LIB_CTX *libctx = NULL;
@@ -161,9 +112,7 @@ static int eckey_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
     if (!ossl_x509_PUBKEY_get0_libctx(&libctx, &propq, pubkey)
         || !X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
         return 0;
-    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-    eckey = eckey_type2param(ptype, pval, libctx, propq);
+    eckey = ossl_ec_key_param_from_x509_algor(palg, libctx, propq);
 
     if (!eckey)
         return 0;
@@ -202,32 +151,15 @@ static int eckey_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
 static int eckey_priv_decode_ex(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8,
                                 OSSL_LIB_CTX *libctx, const char *propq)
 {
-    const unsigned char *p = NULL;
-    const void *pval;
-    int ptype, pklen;
-    EC_KEY *eckey = NULL;
-    const X509_ALGOR *palg;
-
-    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8))
-        return 0;
-    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-    eckey = eckey_type2param(ptype, pval, libctx, propq);
-    if (eckey == NULL)
-        goto err;
+    int ret = 0;
+    EC_KEY *eckey = ossl_ec_key_from_pkcs8(p8, libctx, propq);
 
-    /* We have parameters now set private key */
-    if (!d2i_ECPrivateKey(&eckey, &p, pklen)) {
-        ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
-        goto err;
+    if (eckey != NULL) {
+        ret = 1;
+        EVP_PKEY_assign_EC_KEY(pkey, eckey);
     }
 
-    EVP_PKEY_assign_EC_KEY(pkey, eckey);
-    return 1;
-
- err:
-    EC_KEY_free(eckey);
-    return 0;
+    return ret;
 }
 
 static int eckey_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c
index c4a5a81fda..9716ffc2f2 100644
--- a/crypto/ec/ec_backend.c
+++ b/crypto/ec/ec_backend.c
@@ -570,3 +570,82 @@ int ossl_ec_pt_format_param2id(const OSSL_PARAM *p, int *id)
     }
     return 0;
 }
+
+#ifndef FIPS_MODULE
+EC_KEY *ossl_ec_key_param_from_x509_algor(const X509_ALGOR *palg,
+                                     OSSL_LIB_CTX *libctx, const char *propq)
+{
+    int ptype = 0;
+    const void *pval = NULL;
+    EC_KEY *eckey = NULL;
+    EC_GROUP *group = NULL;
+
+    X509_ALGOR_get0(NULL, &ptype, &pval, palg);
+    if ((eckey = EC_KEY_new_ex(libctx, propq)) == NULL) {
+        ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
+        goto ecerr;
+    }
+
+    if (ptype == V_ASN1_SEQUENCE) {
+        const ASN1_STRING *pstr = pval;
+        const unsigned char *pm = pstr->data;
+        int pmlen = pstr->length;
+
+
+        if (d2i_ECParameters(&eckey, &pm, pmlen) == NULL) {
+            ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
+            goto ecerr;
+        }
+    } else if (ptype == V_ASN1_OBJECT) {
+        const ASN1_OBJECT *poid = pval;
+
+        /*
+         * type == V_ASN1_OBJECT => the parameters are given by an asn1 OID
+         */
+
+        group = EC_GROUP_new_by_curve_name_ex(libctx, propq, OBJ_obj2nid(poid));
+        if (group == NULL)
+            goto ecerr;
+        EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+        if (EC_KEY_set_group(eckey, group) == 0)
+            goto ecerr;
+        EC_GROUP_free(group);
+    } else {
+        ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
+        goto ecerr;
+    }
+
+    return eckey;
+
+ ecerr:
+    EC_KEY_free(eckey);
+    EC_GROUP_free(group);
+    return NULL;
+}
+
+EC_KEY *ossl_ec_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                               OSSL_LIB_CTX *libctx, const char *propq)
+{
+    const unsigned char *p = NULL;
+    int pklen;
+    EC_KEY *eckey = NULL;
+    const X509_ALGOR *palg;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &palg, p8inf))
+        return 0;
+    eckey = ossl_ec_key_param_from_x509_algor(palg, libctx, propq);
+    if (eckey == NULL)
+        goto err;
+
+    /* We have parameters now set private key */
+    if (!d2i_ECPrivateKey(&eckey, &p, pklen)) {
+        ERR_raise(ERR_LIB_EC, EC_R_DECODE_ERROR);
+        goto err;
+    }
+
+    return eckey;
+ err:
+    EC_KEY_free(eckey);
+    return NULL;
+}
+#endif
diff --git a/crypto/ec/ecx_backend.c b/crypto/ec/ecx_backend.c
index 17dd507c35..8f8fdc7705 100644
--- a/crypto/ec/ecx_backend.c
+++ b/crypto/ec/ecx_backend.c
@@ -7,9 +7,11 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <string.h>
 #include <openssl/core_names.h>
 #include <openssl/params.h>
 #include <openssl/ec.h>
+#include <openssl/rand.h>
 #include <openssl/err.h>
 #include "crypto/ecx.h"
 #include "ecx_backend.h"
@@ -90,3 +92,110 @@ int ossl_ecx_key_fromdata(ECX_KEY *ecx, const OSSL_PARAM params[],
     return 1;
 }
 
+#ifndef FIPS_MODULE
+ECX_KEY *ossl_ecx_key_op(const X509_ALGOR *palg,
+                         const unsigned char *p, int plen,
+                         int id, ecx_key_op_t op,
+                         OSSL_LIB_CTX *libctx, const char *propq)
+{
+    ECX_KEY *key = NULL;
+    unsigned char *privkey, *pubkey;
+
+    if (op != KEY_OP_KEYGEN) {
+        if (palg != NULL) {
+            int ptype;
+
+            /* Algorithm parameters must be absent */
+            X509_ALGOR_get0(NULL, &ptype, NULL, palg);
+            if (ptype != V_ASN1_UNDEF) {
+                ERR_raise(ERR_LIB_EC, EC_R_INVALID_ENCODING);
+                return 0;
+            }
+            if (id == EVP_PKEY_NONE)
+                id = OBJ_obj2nid(palg->algorithm);
+            else if (id != OBJ_obj2nid(palg->algorithm)) {
+                ERR_raise(ERR_LIB_EC, EC_R_INVALID_ENCODING);
+                return 0;
+            }
+        }
+
+        if (p == NULL || id == EVP_PKEY_NONE || plen != KEYLENID(id)) {
+            ERR_raise(ERR_LIB_EC, EC_R_INVALID_ENCODING);
+            return 0;
+        }
+    }
+
+    key = ossl_ecx_key_new(libctx, KEYNID2TYPE(id), 1, propq);
+    if (key == NULL) {
+        ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    pubkey = key->pubkey;
+
+    if (op == KEY_OP_PUBLIC) {
+        memcpy(pubkey, p, plen);
+    } else {
+        privkey = ossl_ecx_key_allocate_privkey(key);
+        if (privkey == NULL) {
+            ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (op == KEY_OP_KEYGEN) {
+            if (id != EVP_PKEY_NONE) {
+                if (RAND_priv_bytes_ex(libctx, privkey, KEYLENID(id)) <= 0)
+                    goto err;
+                if (id == EVP_PKEY_X25519) {
+                    privkey[0] &= 248;
+                    privkey[X25519_KEYLEN - 1] &= 127;
+                    privkey[X25519_KEYLEN - 1] |= 64;
+                } else if (id == EVP_PKEY_X448) {
+                    privkey[0] &= 252;
+                    privkey[X448_KEYLEN - 1] |= 128;
+                }
+            }
+        } else {
+            memcpy(privkey, p, KEYLENID(id));
+        }
+        if (!ossl_ecx_public_from_private(key)) {
+            ERR_raise(ERR_LIB_EC, EC_R_FAILED_MAKING_PUBLIC_KEY);
+            goto err;
+        }
+    }
+
+    return key;
+ err:
+    ossl_ecx_key_free(key);
+    return NULL;
+}
+
+ECX_KEY *ossl_ecx_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                                 OSSL_LIB_CTX *libctx, const char *propq)
+{
+    ECX_KEY *ecx = NULL;
+    const unsigned char *p;
+    int plen;
+    ASN1_OCTET_STRING *oct = NULL;
+    const X509_ALGOR *palg;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8inf))
+        return 0;
+
+    oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen);
+    if (oct == NULL) {
+        p = NULL;
+        plen = 0;
+    } else {
+        p = ASN1_STRING_get0_data(oct);
+        plen = ASN1_STRING_length(oct);
+    }
+
+    /*
+     * EVP_PKEY_NONE means that ecx_key_op() has to figure out the key type
+     * on its own.
+     */
+    ecx = ossl_ecx_key_op(palg, p, plen, EVP_PKEY_NONE, KEY_OP_PRIVATE,
+                          libctx, propq);
+    ASN1_OCTET_STRING_free(oct);
+    return ecx;
+}
+#endif
diff --git a/crypto/ec/ecx_meth.c b/crypto/ec/ecx_meth.c
index 68f943fc82..d476af0e3c 100644
--- a/crypto/ec/ecx_meth.c
+++ b/crypto/ec/ecx_meth.c
@@ -16,7 +16,6 @@
 #include <stdio.h>
 #include <openssl/x509.h>
 #include <openssl/ec.h>
-#include <openssl/rand.h>
 #include <openssl/core_names.h>
 #include <openssl/param_build.h>
 #include "internal/cryptlib.h"
@@ -28,80 +27,6 @@
 #include "curve448/curve448_local.h"
 #include "ecx_backend.h"
 
-typedef enum {
-    KEY_OP_PUBLIC,
-    KEY_OP_PRIVATE,
-    KEY_OP_KEYGEN
-} ecx_key_op_t;
-
-/* Setup EVP_PKEY using public, private or generation */
-static int ecx_key_op(EVP_PKEY *pkey, int id, const X509_ALGOR *palg,
-                      const unsigned char *p, int plen, ecx_key_op_t op,
-                      OSSL_LIB_CTX *libctx, const char *propq)
-{
-    ECX_KEY *key = NULL;
-    unsigned char *privkey, *pubkey;
-
-    if (op != KEY_OP_KEYGEN) {
-        if (palg != NULL) {
-            int ptype;
-
-            /* Algorithm parameters must be absent */
-            X509_ALGOR_get0(NULL, &ptype, NULL, palg);
-            if (ptype != V_ASN1_UNDEF) {
-                ERR_raise(ERR_LIB_EC, EC_R_INVALID_ENCODING);
-                return 0;
-            }
-        }
-
-        if (p == NULL || plen != KEYLENID(id)) {
-            ERR_raise(ERR_LIB_EC, EC_R_INVALID_ENCODING);
-            return 0;
-        }
-    }
-
-    key = ossl_ecx_key_new(libctx, KEYNID2TYPE(id), 1, propq);
-    if (key == NULL) {
-        ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-    pubkey = key->pubkey;
-
-    if (op == KEY_OP_PUBLIC) {
-        memcpy(pubkey, p, plen);
-    } else {
-        privkey = ossl_ecx_key_allocate_privkey(key);
-        if (privkey == NULL) {
-            ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
-            goto err;
-        }
-        if (op == KEY_OP_KEYGEN) {
-            if (RAND_priv_bytes_ex(libctx, privkey, KEYLENID(id)) <= 0)
-                goto err;
-            if (id == EVP_PKEY_X25519) {
-                privkey[0] &= 248;
-                privkey[X25519_KEYLEN - 1] &= 127;
-                privkey[X25519_KEYLEN - 1] |= 64;
-            } else if (id == EVP_PKEY_X448) {
-                privkey[0] &= 252;
-                privkey[X448_KEYLEN - 1] |= 128;
-            }
-        } else {
-            memcpy(privkey, p, KEYLENID(id));
-        }
-        if (!ossl_ecx_public_from_private(key)) {
-            ERR_raise(ERR_LIB_EC, EC_R_FAILED_MAKING_PUBLIC_KEY);
-            goto err;
-        }
-    }
-
-    EVP_PKEY_assign(pkey, id, key);
-    return 1;
- err:
-    ossl_ecx_key_free(key);
-    return 0;
-}
-
 static int ecx_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
 {
     const ECX_KEY *ecxkey = pkey->pkey.ecx;
@@ -132,11 +57,18 @@ static int ecx_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
     const unsigned char *p;
     int pklen;
     X509_ALGOR *palg;
+    ECX_KEY *ecx;
+    int ret = 0;
 
     if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
         return 0;
-    return ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, pklen,
-                      KEY_OP_PUBLIC, NULL, NULL);
+    ecx = ossl_ecx_key_op(palg, p, pklen, pkey->ameth->pkey_id,
+                          KEY_OP_PUBLIC, NULL, NULL);
+    if (ecx != NULL) {
+        ret = 1;
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx);
+    }
+    return ret;
 }
 
 static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
@@ -153,28 +85,15 @@ static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
 static int ecx_priv_decode_ex(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8,
                               OSSL_LIB_CTX *libctx, const char *propq)
 {
-    const unsigned char *p;
-    int plen;
-    ASN1_OCTET_STRING *oct = NULL;
-    const X509_ALGOR *palg;
-    int rv;
-
-    if (!PKCS8_pkey_get0(NULL, &p, &plen, &palg, p8))
-        return 0;
+    int ret = 0;
+    ECX_KEY *ecx = ossl_ecx_key_from_pkcs8(p8, libctx, propq);
 
-    oct = d2i_ASN1_OCTET_STRING(NULL, &p, plen);
-    if (oct == NULL) {
-        p = NULL;
-        plen = 0;
-    } else {
-        p = ASN1_STRING_get0_data(oct);
-        plen = ASN1_STRING_length(oct);
+    if (ecx != NULL) {
+        ret = 1;
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx);
     }
 
-    rv = ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, plen, KEY_OP_PRIVATE,
-                    libctx, propq);
-    ASN1_STRING_clear_free(oct);
-    return rv;
+    return ret;
 }
 
 static int ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
@@ -298,10 +217,16 @@ static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
 {
     switch (op) {
 
-    case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
-        return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, arg2, arg1,
-                          KEY_OP_PUBLIC, NULL, NULL);
+    case ASN1_PKEY_CTRL_SET1_TLS_ENCPT: {
+        ECX_KEY *ecx = ossl_ecx_key_op(NULL, arg2, arg1, pkey->ameth->pkey_id,
+                                       KEY_OP_PUBLIC, NULL, NULL);
 
+        if (ecx != NULL) {
+            EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx);
+            return 1;
+        }
+        return 0;
+    }
     case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
         if (pkey->pkey.ecx != NULL) {
             unsigned char **ppt = arg2;
@@ -336,23 +261,37 @@ static int ecx_set_priv_key(EVP_PKEY *pkey, const unsigned char *priv,
                             size_t len)
 {
     OSSL_LIB_CTX *libctx = NULL;
+    ECX_KEY *ecx = NULL;
 
     if (pkey->keymgmt != NULL)
         libctx = ossl_provider_libctx(EVP_KEYMGMT_provider(pkey->keymgmt));
 
-    return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, priv, len,
-                       KEY_OP_PRIVATE, libctx, NULL);
+    ecx = ossl_ecx_key_op(NULL, priv, len, pkey->ameth->pkey_id,
+                          KEY_OP_PRIVATE, libctx, NULL);
+
+    if (ecx != NULL) {
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx);
+        return 1;
+    }
+    return 0;
 }
 
 static int ecx_set_pub_key(EVP_PKEY *pkey, const unsigned char *pub, size_t len)
 {
     OSSL_LIB_CTX *libctx = NULL;
+    ECX_KEY *ecx = NULL;
 
     if (pkey->keymgmt != NULL)
         libctx = ossl_provider_libctx(EVP_KEYMGMT_provider(pkey->keymgmt));
 
-    return ecx_key_op(pkey, pkey->ameth->pkey_id, NULL, pub, len,
-                      KEY_OP_PUBLIC, libctx, NULL);
+    ecx = ossl_ecx_key_op(NULL, pub, len, pkey->ameth->pkey_id,
+                          KEY_OP_PUBLIC, libctx, NULL);
+
+    if (ecx != NULL) {
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx);
+        return 1;
+    }
+    return 0;
 }
 
 static int ecx_get_priv_key(const EVP_PKEY *pkey, unsigned char *priv,
@@ -750,8 +689,14 @@ const EVP_PKEY_ASN1_METHOD ossl_ed448_asn1_meth = {
 
 static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
 {
-    return ecx_key_op(pkey, ctx->pmeth->pkey_id, NULL, NULL, 0, KEY_OP_KEYGEN,
-                      NULL, NULL);
+    ECX_KEY *ecx = ossl_ecx_key_op(NULL, NULL, 0, ctx->pmeth->pkey_id,
+                                   KEY_OP_PUBLIC, NULL, NULL);
+
+    if (ecx != NULL) {
+        EVP_PKEY_assign(pkey, ctx->pmeth->pkey_id, ecx);
+        return 1;
+    }
+    return 0;
 }
 
 static int validate_ecx_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 68c2ea8aa3..0a407ba2dc 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -849,8 +849,10 @@ PEM_R_BAD_VERSION_NUMBER:117:bad version number
 PEM_R_BIO_WRITE_FAILURE:118:bio write failure
 PEM_R_CIPHER_IS_NULL:127:cipher is null
 PEM_R_ERROR_CONVERTING_PRIVATE_KEY:115:error converting private key
+PEM_R_EXPECTING_DSS_KEY_BLOB:131:expecting dss key blob
 PEM_R_EXPECTING_PRIVATE_KEY_BLOB:119:expecting private key blob
 PEM_R_EXPECTING_PUBLIC_KEY_BLOB:120:expecting public key blob
+PEM_R_EXPECTING_RSA_KEY_BLOB:132:expecting rsa key blob
 PEM_R_HEADER_TOO_LONG:128:header too long
 PEM_R_INCONSISTENT_HEADER:121:inconsistent header
 PEM_R_KEYBLOB_HEADER_PARSE_ERROR:122:keyblob header parse error
diff --git a/crypto/pem/pem_err.c b/crypto/pem/pem_err.c
index 52a1bc611f..50f9c2b925 100644
--- a/crypto/pem/pem_err.c
+++ b/crypto/pem/pem_err.c
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2021 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
@@ -26,10 +26,14 @@ static const ERR_STRING_DATA PEM_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_CIPHER_IS_NULL), "cipher is null"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_ERROR_CONVERTING_PRIVATE_KEY),
     "error converting private key"},
+    {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_DSS_KEY_BLOB),
+    "expecting dss key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PRIVATE_KEY_BLOB),
     "expecting private key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_PUBLIC_KEY_BLOB),
     "expecting public key blob"},
+    {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_EXPECTING_RSA_KEY_BLOB),
+    "expecting rsa key blob"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_HEADER_TOO_LONG), "header too long"},
     {ERR_PACK(ERR_LIB_PEM, 0, PEM_R_INCONSISTENT_HEADER),
     "inconsistent header"},
diff --git a/crypto/pem/pvkfmt.c b/crypto/pem/pvkfmt.c
index 0d8aa509b3..bc6f6fab71 100644
--- a/crypto/pem/pvkfmt.c
+++ b/crypto/pem/pvkfmt.c
@@ -59,6 +59,65 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
     return 1;
 }
 
+/*
+ * Create an EVP_PKEY from a type specific key.
+ * This takes ownership of |key|, as long as the |evp_type| is acceptable
+ * (EVP_PKEY_RSA or EVP_PKEY_DSA), even if the resulting EVP_PKEY wasn't
+ * created.
+ */
+#define isdss_to_evp_type(isdss)                                \
+    (isdss == 0 ? EVP_PKEY_RSA : isdss == 1 ? EVP_PKEY_DSA : EVP_PKEY_NONE)
+static EVP_PKEY *evp_pkey_new0_key(void *key, int evp_type)
+{
+    EVP_PKEY *pkey = NULL;
+
+    /*
+     * It's assumed that if |key| is NULL, something went wrong elsewhere
+     * and suitable errors are already reported.
+     */
+    if (key == NULL)
+        return NULL;
+
+    if (!ossl_assert(evp_type == EVP_PKEY_RSA || evp_type == EVP_PKEY_DSA)) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_INTERNAL_ERROR);
+        return NULL;
+    }
+
+    if ((pkey = EVP_PKEY_new()) != NULL) {
+        switch (evp_type) {
+        case EVP_PKEY_RSA:
+            if (EVP_PKEY_set1_RSA(pkey, key))
+                break;
+            EVP_PKEY_free(pkey);
+            pkey = NULL;
+            break;
+#ifndef OPENSSL_NO_DSA
+        case EVP_PKEY_DSA:
+            if (EVP_PKEY_set1_DSA(pkey, key))
+                break;
+            EVP_PKEY_free(pkey);
+            pkey = NULL;
+            break;
+#endif
+        }
+    }
+
+    switch (evp_type) {
+    case EVP_PKEY_RSA:
+        RSA_free(key);
+        break;
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        DSA_free(key);
+        break;
+#endif
+    }
+
+    if (pkey == NULL)
+        ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
+    return pkey;
+}
+
 /* Convert private key blob to EVP_PKEY: RSA and DSA keys supported */
 
 # define MS_PUBLICKEYBLOB        0x6
@@ -74,9 +133,6 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
 # define MS_KEYTYPE_KEYX         0x1
 # define MS_KEYTYPE_SIGN         0x2
 
-/* Maximum length of a blob after header */
-# define BLOB_MAX_LENGTH          102400
-
 /* The PVK file magic number: seems to spell out "bobsfile", who is Bob? */
 # define MS_PVKMAGIC             0xb0b5f11eL
 /* Salt length for PVK files */
@@ -86,13 +142,20 @@ static int read_lebn(const unsigned char **in, unsigned int nbyte, BIGNUM **r)
 /* Maximum salt length */
 # define PVK_MAX_SALTLEN         10240
 
-static EVP_PKEY *b2i_rsa(const unsigned char **in,
-                         unsigned int bitlen, int ispub);
-#ifndef OPENSSL_NO_DSA
-static EVP_PKEY *b2i_dss(const unsigned char **in,
-                         unsigned int bitlen, int ispub);
-#endif
-
+/*
+ * Read the MSBLOB header and get relevant data from it.
+ *
+ * |pisdss| and |pispub| have a double role, as they can be used for
+ * discovery as well as to check the the blob meets expectations.
+ * |*pisdss| is the indicator for whether the key is a DSA key or not.
+ * |*pispub| is the indicator for whether the key is public or not.
+ * In both cases, the following input values apply:
+ *
+ * 0    Expected to not be what the variable indicates.
+ * 1    Expected to be what the variable indicates.
+ * -1   No expectations, this function will assign 0 or 1 depending on
+ *      header data.
+ */
 int ossl_do_blob_header(const unsigned char **in, unsigned int length,
                         unsigned int *pmagic, unsigned int *pbitlen,
                         int *pisdss, int *pispub)
@@ -102,19 +165,24 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     if (length < 16)
         return 0;
     /* bType */
-    if (*p == MS_PUBLICKEYBLOB) {
+    switch (*p) {
+    case MS_PUBLICKEYBLOB:
         if (*pispub == 0) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
             return 0;
         }
         *pispub = 1;
-    } else if (*p == MS_PRIVATEKEYBLOB) {
+        break;
+
+    case MS_PRIVATEKEYBLOB:
         if (*pispub == 1) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
             return 0;
         }
         *pispub = 0;
-    } else {
+        break;
+
+    default:
         return 0;
     }
     p++;
@@ -127,12 +195,10 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     p += 6;
     *pmagic = read_ledword(&p);
     *pbitlen = read_ledword(&p);
-    *pisdss = 0;
-    switch (*pmagic) {
 
+    /* Consistency check for private vs public */
+    switch (*pmagic) {
     case MS_DSS1MAGIC:
-        *pisdss = 1;
-        /* fall thru */
     case MS_RSA1MAGIC:
         if (*pispub == 0) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB);
@@ -141,8 +207,6 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
         break;
 
     case MS_DSS2MAGIC:
-        *pisdss = 1;
-        /* fall thru */
     case MS_RSA2MAGIC:
         if (*pispub == 1) {
             ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB);
@@ -150,6 +214,30 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
         }
         break;
 
+    default:
+        ERR_raise(ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER);
+        return -1;
+    }
+
+    /* Check that we got the expected type */
+    switch (*pmagic) {
+    case MS_DSS1MAGIC:
+    case MS_DSS2MAGIC:
+        if (*pisdss == 0) {
+            ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_DSS_KEY_BLOB);
+            return 0;
+        }
+        *pisdss = 1;
+        break;
+    case MS_RSA1MAGIC:
+    case MS_RSA2MAGIC:
+        if (*pisdss == 1) {
+            ERR_raise(ERR_LIB_PEM, PEM_R_EXPECTING_RSA_KEY_BLOB);
+            return 0;
+        }
+        *pisdss = 0;
+        break;
+
     default:
         ERR_raise(ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER);
         return -1;
@@ -158,7 +246,7 @@ int ossl_do_blob_header(const unsigned char **in, unsigned int length,
     return 1;
 }
 
-static unsigned int blob_length(unsigned bitlen, int isdss, int ispub)
+unsigned int ossl_blob_length(unsigned bitlen, int isdss, int ispub)
 {
     unsigned int nbyte = (bitlen + 7) >> 3;
     unsigned int hnbyte = (bitlen + 15) >> 4;
@@ -191,30 +279,43 @@ static unsigned int blob_length(unsigned bitlen, int isdss, int ispub)
 
 }
 
-EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub)
+static void *do_b2i_key(const unsigned char **in, unsigned int length,
+                        int *isdss, int *ispub)
 {
     const unsigned char *p = *in;
     unsigned int bitlen, magic;
-    int isdss;
+    void *key = NULL;
 
-    if (ossl_do_blob_header(&p, length, &magic, &bitlen, &isdss, ispub) <= 0) {
+    if (ossl_do_blob_header(&p, length, &magic, &bitlen, isdss, ispub) <= 0) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR);
         return NULL;
     }
     length -= 16;
-    if (length < blob_length(bitlen, isdss, *ispub)) {
+    if (length < ossl_blob_length(bitlen, *isdss, *ispub)) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
         return NULL;
     }
-    if (!isdss)
-        return b2i_rsa(&p, bitlen, *ispub);
+    if (!*isdss)
+        key = ossl_b2i_RSA_after_header(&p, bitlen, *ispub);
 #ifndef OPENSSL_NO_DSA
     else
-        return b2i_dss(&p, bitlen, *ispub);
+        key = ossl_b2i_DSA_after_header(&p, bitlen, *ispub);
 #endif
 
-    ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
-    return NULL;
+    if (key == NULL) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        return NULL;
+    }
+
+    return key;
+}
+
+EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub)
+{
+    int isdss = -1;
+    void *key = do_b2i_key(in, length, &isdss, ispub);
+
+    return evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
 }
 
 EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
@@ -223,7 +324,8 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     unsigned char hdr_buf[16], *buf = NULL;
     unsigned int bitlen, magic, length;
     int isdss;
-    EVP_PKEY *ret = NULL;
+    void *key = NULL;
+    EVP_PKEY *pkey = NULL;
 
     if (BIO_read(in, hdr_buf, 16) != 16) {
         ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
@@ -233,7 +335,7 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     if (ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, ispub) <= 0)
         return NULL;
 
-    length = blob_length(bitlen, isdss, *ispub);
+    length = ossl_blob_length(bitlen, isdss, *ispub);
     if (length > BLOB_MAX_LENGTH) {
         ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG);
         return NULL;
@@ -250,26 +352,28 @@ EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub)
     }
 
     if (!isdss)
-        ret = b2i_rsa(&p, bitlen, *ispub);
+        key = ossl_b2i_RSA_after_header(&p, bitlen, *ispub);
 #ifndef OPENSSL_NO_DSA
     else
-        ret = b2i_dss(&p, bitlen, *ispub);
+        key = ossl_b2i_DSA_after_header(&p, bitlen, *ispub);
 #endif
 
-    if (ret == NULL)
+    if (key == NULL) {
         ERR_raise(ERR_LIB_PEM, PEM_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+        goto err;
+    }
 
+    pkey = evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
  err:
     OPENSSL_free(buf);
-    return ret;
+    return pkey;
 }
 
 #ifndef OPENSSL_NO_DSA
-static EVP_PKEY *b2i_dss(const unsigned char **in,
-                         unsigned int bitlen, int ispub)
+DSA *ossl_b2i_DSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub)
 {
     const unsigned char *p = *in;
-    EVP_PKEY *ret = NULL;
     DSA *dsa = NULL;
     BN_CTX *ctx = NULL;
     BIGNUM *pbn = NULL, *qbn = NULL, *gbn = NULL, *priv_key = NULL;
@@ -277,8 +381,7 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
     unsigned int nbyte = (bitlen + 7) >> 3;
 
     dsa = DSA_new();
-    ret = EVP_PKEY_new();
-    if (dsa == NULL || ret == NULL)
+    if (dsa == NULL)
         goto memerr;
     if (!read_lebn(&p, nbyte, &pbn))
         goto memerr;
@@ -319,11 +422,8 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
         goto memerr;
     pub_key = priv_key = NULL;
 
-    if (!EVP_PKEY_set1_DSA(ret, dsa))
-        goto memerr;
-    DSA_free(dsa);
     *in = p;
-    return ret;
+    return dsa;
 
  memerr:
     ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
@@ -333,17 +433,15 @@ static EVP_PKEY *b2i_dss(const unsigned char **in,
     BN_free(gbn);
     BN_free(pub_key);
     BN_free(priv_key);
-    EVP_PKEY_free(ret);
     BN_CTX_free(ctx);
     return NULL;
 }
 #endif
 
-static EVP_PKEY *b2i_rsa(const unsigned char **in,
-                         unsigned int bitlen, int ispub)
+RSA *ossl_b2i_RSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub)
 {
     const unsigned char *pin = *in;
-    EVP_PKEY *ret = NULL;
     BIGNUM *e = NULL, *n = NULL, *d = NULL;
     BIGNUM *p = NULL, *q = NULL, *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
     RSA *rsa = NULL;
@@ -351,8 +449,7 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
     unsigned int hnbyte = (bitlen + 15) >> 4;
 
     rsa = RSA_new();
-    ret = EVP_PKEY_new();
-    if (rsa == NULL || ret == NULL)
+    if (rsa == NULL)
         goto memerr;
     e = BN_new();
     if (e == NULL)
@@ -385,11 +482,8 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
         goto memerr;
     n = e = d = NULL;
 
-    if (!EVP_PKEY_set1_RSA(ret, rsa))
-        goto memerr;
-    RSA_free(rsa);
     *in = pin;
-    return ret;
+    return rsa;
  memerr:
     ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
     BN_free(e);
@@ -401,7 +495,6 @@ static EVP_PKEY *b2i_rsa(const unsigned char **in,
     BN_free(iqmp);
     BN_free(d);
     RSA_free(rsa);
-    EVP_PKEY_free(ret);
     return NULL;
 }
 
@@ -463,23 +556,12 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     unsigned char *p;
     unsigned int bitlen = 0, magic = 0, keyalg = 0;
     int outlen = -1, noinc = 0;
-    int pktype;
-#ifndef OPENSSL_NO_PROVIDER_CODE
-    EVP_PKEY *pkcopy = NULL;
-
-    if (evp_pkey_is_provided(pk)) {
-        if (!evp_pkey_copy_downgraded(&pkcopy, pk))
-            goto end;
-        pk = pkcopy;
-    }
-#endif
 
-    pktype = EVP_PKEY_id(pk);
-    if (pktype == EVP_PKEY_RSA) {
+    if (EVP_PKEY_is_a(pk, "RSA")) {
         bitlen = check_bitlen_rsa(EVP_PKEY_get0_RSA(pk), ispub, &magic);
         keyalg = MS_KEYALG_RSA_KEYX;
 #ifndef OPENSSL_NO_DSA
-    } else if (pktype == EVP_PKEY_DSA) {
+    } else if (EVP_PKEY_is_a(pk, "DSA")) {
         bitlen = check_bitlen_dsa(EVP_PKEY_get0_DSA(pk), ispub, &magic);
         keyalg = MS_KEYALG_DSS_SIGN;
 #endif
@@ -487,8 +569,8 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     if (bitlen == 0) {
         goto end;
     }
-    outlen = 16 + blob_length(bitlen,
-                              keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
+    outlen = 16
+        + ossl_blob_length(bitlen, keyalg == MS_KEYALG_DSS_SIGN ? 1 : 0, ispub);
     if (out == NULL)
         goto end;
     if (*out)
@@ -521,9 +603,6 @@ static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
     if (!noinc)
         *out += outlen;
  end:
-#ifndef OPENSSL_NO_PROVIDER_CODE
-    EVP_PKEY_free(pkcopy);
-#endif
     return outlen;
 }
 
@@ -730,14 +809,15 @@ static int derive_pvk_key(unsigned char *key,
 }
 #endif
 
-static EVP_PKEY *do_PVK_body(const unsigned char **in,
+static void *do_PVK_body_key(const unsigned char **in,
                              unsigned int saltlen, unsigned int keylen,
-                             pem_password_cb *cb, void *u)
+                             pem_password_cb *cb, void *u,
+                             int *isdss, int *ispub)
 {
-    EVP_PKEY *ret = NULL;
     const unsigned char *p = *in;
     unsigned char *enctmp = NULL;
     unsigned char keybuf[20];
+    void *key = NULL;
 
     EVP_CIPHER_CTX *cctx = EVP_CIPHER_CTX_new();
     if (saltlen) {
@@ -802,22 +882,23 @@ static EVP_PKEY *do_PVK_body(const unsigned char **in,
 #endif
     }
 
-    ret = b2i_PrivateKey(&p, keylen);
+    key = do_b2i_key(&p, keylen, isdss, ispub);
  err:
     EVP_CIPHER_CTX_free(cctx);
     if (enctmp != NULL) {
         OPENSSL_cleanse(keybuf, sizeof(keybuf));
         OPENSSL_free(enctmp);
     }
-    return ret;
+    return key;
 }
 
-EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+static void *do_PVK_key_bio(BIO *in, pem_password_cb *cb, void *u,
+                            int *isdss, int *ispub)
 {
     unsigned char pvk_hdr[24], *buf = NULL;
     const unsigned char *p;
     int buflen;
-    EVP_PKEY *ret = NULL;
+    void *key = NULL;
     unsigned int saltlen, keylen;
 
     if (BIO_read(in, pvk_hdr, 24) != 24) {
@@ -839,11 +920,38 @@ EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
         ERR_raise(ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT);
         goto err;
     }
-    ret = do_PVK_body(&p, saltlen, keylen, cb, u);
+    key = do_PVK_body_key(&p, saltlen, keylen, cb, u, isdss, ispub);
 
  err:
     OPENSSL_clear_free(buf, buflen);
-    return ret;
+    return key;
+}
+
+#ifndef OPENSSL_NO_DSA
+DSA *b2i_DSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = 1;
+    int ispub = 0;               /* PVK keys are always private */
+
+    return do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+}
+#endif
+
+RSA *b2i_RSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = 0;
+    int ispub = 0;               /* PVK keys are always private */
+
+    return do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+}
+
+EVP_PKEY *b2i_PVK_bio(BIO *in, pem_password_cb *cb, void *u)
+{
+    int isdss = -1;
+    int ispub = -1;
+    void *key = do_PVK_key_bio(in, cb, u, &isdss, &ispub);
+
+    return evp_pkey_new0_key(key, isdss_to_evp_type(isdss));
 }
 
 static int i2b_PVK(unsigned char **out, const EVP_PKEY *pk, int enclevel,
diff --git a/crypto/rsa/rsa_ameth.c b/crypto/rsa/rsa_ameth.c
index 9e9366757d..067b7db12d 100644
--- a/crypto/rsa/rsa_ameth.c
+++ b/crypto/rsa/rsa_ameth.c
@@ -25,9 +25,6 @@
 #include "crypto/rsa.h"
 #include "rsa_local.h"
 
-static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg);
-static int rsa_sync_to_pss_params_30(RSA *rsa);
-
 /* Set any parameters associated with pkey */
 static int rsa_param_encode(const EVP_PKEY *pkey,
                             ASN1_STRING **pstr, int *pstrtype)
@@ -53,29 +50,6 @@ static int rsa_param_encode(const EVP_PKEY *pkey,
     return 1;
 }
 /* Decode any parameters and set them in RSA structure */
-static int rsa_param_decode(RSA *rsa, const X509_ALGOR *alg)
-{
-    const ASN1_OBJECT *algoid;
-    const void *algp;
-    int algptype;
-
-    X509_ALGOR_get0(&algoid, &algptype, &algp, alg);
-    if (OBJ_obj2nid(algoid) != EVP_PKEY_RSA_PSS)
-        return 1;
-    if (algptype == V_ASN1_UNDEF)
-        return 1;
-    if (algptype != V_ASN1_SEQUENCE) {
-        ERR_raise(ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS);
-        return 0;
-    }
-    rsa->pss = rsa_pss_decode(alg);
-    if (rsa->pss == NULL)
-        return 0;
-    if (!rsa_sync_to_pss_params_30(rsa))
-        return 0;
-    return 1;
-}
-
 static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
 {
     unsigned char *penc = NULL;
@@ -107,7 +81,7 @@ static int rsa_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
         return 0;
     if ((rsa = d2i_RSAPublicKey(NULL, &p, pklen)) == NULL)
         return 0;
-    if (!rsa_param_decode(rsa, alg)) {
+    if (!ossl_rsa_param_decode(rsa, alg)) {
         RSA_free(rsa);
         return 0;
     }
@@ -194,38 +168,14 @@ static int rsa_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
 
 static int rsa_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
 {
-    const unsigned char *p;
-    RSA *rsa;
-    int pklen;
-    const X509_ALGOR *alg;
+    int ret = 0;
+    RSA *rsa = ossl_rsa_key_from_pkcs8(p8, NULL, NULL);
 
-    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &alg, p8))
-        return 0;
-    rsa = d2i_RSAPrivateKey(NULL, &p, pklen);
-    if (rsa == NULL) {
-        ERR_raise(ERR_LIB_RSA, ERR_R_RSA_LIB);
-        return 0;
-    }
-    if (!rsa_param_decode(rsa, alg)) {
-        RSA_free(rsa);
-        return 0;
+    if (rsa != NULL) {
+        ret = 1;
+        EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, rsa);
     }
-
-    RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
-    switch (pkey->ameth->pkey_id) {
-    case EVP_PKEY_RSA:
-        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA);
-        break;
-    case EVP_PKEY_RSA_PSS:
-        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS);
-        break;
-    default:
-        /* Leave the type bits zero */
-        break;
-    }
-
-    EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, rsa);
-    return 1;
+    return ret;
 }
 
 static int int_rsa_size(const EVP_PKEY *pkey)
@@ -444,33 +394,12 @@ static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
     return pkey_rsa_print(bp, pkey, indent, 1);
 }
 
-static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg)
-{
-    RSA_PSS_PARAMS *pss;
-
-    pss = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(RSA_PSS_PARAMS),
-                                    alg->parameter);
-
-    if (pss == NULL)
-        return NULL;
-
-    if (pss->maskGenAlgorithm != NULL) {
-        pss->maskHash = ossl_x509_algor_mgf1_decode(pss->maskGenAlgorithm);
-        if (pss->maskHash == NULL) {
-            RSA_PSS_PARAMS_free(pss);
-            return NULL;
-        }
-    }
-
-    return pss;
-}
-
 static int rsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
                          const ASN1_STRING *sig, int indent, ASN1_PCTX *pctx)
 {
     if (OBJ_obj2nid(sigalg->algorithm) == EVP_PKEY_RSA_PSS) {
         int rv;
-        RSA_PSS_PARAMS *pss = rsa_pss_decode(sigalg);
+        RSA_PSS_PARAMS *pss = ossl_rsa_pss_decode(sigalg);
 
         rv = rsa_pss_param_print(bp, 0, pss, indent);
         RSA_PSS_PARAMS_free(pss);
@@ -601,7 +530,7 @@ int ossl_rsa_pss_to_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pkctx,
         return -1;
     }
     /* Decode PSS parameters */
-    pss = rsa_pss_decode(sigalg);
+    pss = ossl_rsa_pss_decode(sigalg);
 
     if (!ossl_rsa_pss_get_param(pss, &md, &mgf1md, &saltlen)) {
         ERR_raise(ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS);
@@ -656,36 +585,6 @@ static int rsa_pss_verify_param(const EVP_MD **pmd, const EVP_MD **pmgf1md,
     return 1;
 }
 
-static int rsa_pss_get_param_unverified(const RSA_PSS_PARAMS *pss,
-                                        const EVP_MD **pmd,
-                                        const EVP_MD **pmgf1md,
-                                        int *psaltlen, int *ptrailerField)
-{
-    RSA_PSS_PARAMS_30 pss_params;
-
-    /* Get the defaults from the ONE place */
-    (void)ossl_rsa_pss_params_30_set_defaults(&pss_params);
-
-    if (pss == NULL)
-        return 0;
-    *pmd = ossl_x509_algor_get_md(pss->hashAlgorithm);
-    if (*pmd == NULL)
-        return 0;
-    *pmgf1md = ossl_x509_algor_get_md(pss->maskHash);
-    if (*pmgf1md == NULL)
-        return 0;
-    if (pss->saltLength)
-        *psaltlen = ASN1_INTEGER_get(pss->saltLength);
-    else
-        *psaltlen = ossl_rsa_pss_params_30_saltlen(&pss_params);
-    if (pss->trailerField)
-        *ptrailerField = ASN1_INTEGER_get(pss->trailerField);
-    else
-        *ptrailerField = ossl_rsa_pss_params_30_trailerfield(&pss_params);;
-
-    return 1;
-}
-
 int ossl_rsa_pss_get_param(const RSA_PSS_PARAMS *pss, const EVP_MD **pmd,
                            const EVP_MD **pmgf1md, int *psaltlen)
 {
@@ -699,45 +598,11 @@ int ossl_rsa_pss_get_param(const RSA_PSS_PARAMS *pss, const EVP_MD **pmd,
      */
     int trailerField = 0;
 
-    return rsa_pss_get_param_unverified(pss, pmd, pmgf1md, psaltlen,
-                                        &trailerField)
+    return ossl_rsa_pss_get_param_unverified(pss, pmd, pmgf1md, psaltlen,
+                                             &trailerField)
         && rsa_pss_verify_param(pmd, pmgf1md, psaltlen, &trailerField);
 }
 
-static int rsa_sync_to_pss_params_30(RSA *rsa)
-{
-    if (rsa != NULL && rsa->pss != NULL) {
-        const EVP_MD *md = NULL, *mgf1md = NULL;
-        int md_nid, mgf1md_nid, saltlen, trailerField;
-        RSA_PSS_PARAMS_30 pss_params;
-
-        /*
-         * We don't care about the validity of the fields here, we just
-         * want to synchronise values.  Verifying here makes it impossible
-         * to even read a key with invalid values, making it hard to test
-         * a bad situation.
-         *
-         * Other routines use ossl_rsa_pss_get_param(), so the values will be
-         * checked, eventually.
-         */
-        if (!rsa_pss_get_param_unverified(rsa->pss, &md, &mgf1md,
-                                          &saltlen, &trailerField))
-            return 0;
-        md_nid = EVP_MD_type(md);
-        mgf1md_nid = EVP_MD_type(mgf1md);
-        if (!ossl_rsa_pss_params_30_set_defaults(&pss_params)
-            || !ossl_rsa_pss_params_30_set_hashalg(&pss_params, md_nid)
-            || !ossl_rsa_pss_params_30_set_maskgenhashalg(&pss_params,
-                                                          mgf1md_nid)
-            || !ossl_rsa_pss_params_30_set_saltlen(&pss_params, saltlen)
-            || !ossl_rsa_pss_params_30_set_trailerfield(&pss_params,
-                                                        trailerField))
-            return 0;
-        rsa->pss_params = pss_params;
-    }
-    return 1;
-}
-
 /*
  * Customised RSA item verification routine. This is called when a signature
  * is encountered requiring special handling. We currently only handle PSS.
@@ -806,7 +671,7 @@ static int rsa_sig_info_set(X509_SIG_INFO *siginf, const X509_ALGOR *sigalg,
     if (OBJ_obj2nid(sigalg->algorithm) != EVP_PKEY_RSA_PSS)
         return 0;
     /* Decode PSS parameters */
-    pss = rsa_pss_decode(sigalg);
+    pss = ossl_rsa_pss_decode(sigalg);
     if (!ossl_rsa_pss_get_param(pss, &md, &mgf1md, &saltlen))
         goto err;
     mdnid = EVP_MD_type(md);
@@ -894,8 +759,8 @@ static int rsa_int_export_to(const EVP_PKEY *from, int rsa_type,
         int md_nid, mgf1md_nid, saltlen, trailerfield;
         RSA_PSS_PARAMS_30 pss_params;
 
-        if (!rsa_pss_get_param_unverified(rsa->pss, &md, &mgf1md,
-                                          &saltlen, &trailerfield))
+        if (!ossl_rsa_pss_get_param_unverified(rsa->pss, &md, &mgf1md,
+                                               &saltlen, &trailerfield))
             goto err;
         md_nid = EVP_MD_type(md);
         mgf1md_nid = EVP_MD_type(mgf1md);
diff --git a/crypto/rsa/rsa_backend.c b/crypto/rsa/rsa_backend.c
index 8c16a56db5..01ee875058 100644
--- a/crypto/rsa/rsa_backend.c
+++ b/crypto/rsa/rsa_backend.c
@@ -16,9 +16,11 @@
 #include <string.h>
 #include <openssl/core_names.h>
 #include <openssl/params.h>
+#include <openssl/err.h>
 #include <openssl/evp.h>
 #include "internal/sizes.h"
 #include "internal/param_build_set.h"
+#include "crypto/asn1.h"
 #include "crypto/rsa.h"
 
 #include "e_os.h"                /* strcasecmp for Windows() */
@@ -319,3 +321,156 @@ int ossl_rsa_pss_params_30_fromdata(RSA_PSS_PARAMS_30 *pss_params,
     EVP_MD_free(mgf1md);
     return ret;
 }
+
+#ifndef FIPS_MODULE
+RSA_PSS_PARAMS *ossl_rsa_pss_decode(const X509_ALGOR *alg)
+{
+    RSA_PSS_PARAMS *pss;
+
+    pss = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(RSA_PSS_PARAMS),
+                                    alg->parameter);
+
+    if (pss == NULL)
+        return NULL;
+
+    if (pss->maskGenAlgorithm != NULL) {
+        pss->maskHash = ossl_x509_algor_mgf1_decode(pss->maskGenAlgorithm);
+        if (pss->maskHash == NULL) {
+            RSA_PSS_PARAMS_free(pss);
+            return NULL;
+        }
+    }
+
+    return pss;
+}
+
+static int ossl_rsa_sync_to_pss_params_30(RSA *rsa)
+{
+    const RSA_PSS_PARAMS *legacy_pss = NULL;
+    RSA_PSS_PARAMS_30 *pss = NULL;
+
+    if (rsa != NULL
+        && (legacy_pss = RSA_get0_pss_params(rsa)) != NULL
+        && (pss = ossl_rsa_get0_pss_params_30(rsa)) != NULL) {
+        const EVP_MD *md = NULL, *mgf1md = NULL;
+        int md_nid, mgf1md_nid, saltlen, trailerField;
+        RSA_PSS_PARAMS_30 pss_params;
+
+        /*
+         * We don't care about the validity of the fields here, we just
+         * want to synchronise values.  Verifying here makes it impossible
+         * to even read a key with invalid values, making it hard to test
+         * a bad situation.
+         *
+         * Other routines use ossl_rsa_pss_get_param(), so the values will
+         * be checked, eventually.
+         */
+        if (!ossl_rsa_pss_get_param_unverified(legacy_pss, &md, &mgf1md,
+                                               &saltlen, &trailerField))
+            return 0;
+        md_nid = EVP_MD_type(md);
+        mgf1md_nid = EVP_MD_type(mgf1md);
+        if (!ossl_rsa_pss_params_30_set_defaults(&pss_params)
+            || !ossl_rsa_pss_params_30_set_hashalg(&pss_params, md_nid)
+            || !ossl_rsa_pss_params_30_set_maskgenhashalg(&pss_params,
+                                                          mgf1md_nid)
+            || !ossl_rsa_pss_params_30_set_saltlen(&pss_params, saltlen)
+            || !ossl_rsa_pss_params_30_set_trailerfield(&pss_params,
+                                                        trailerField))
+            return 0;
+        *pss = pss_params;
+    }
+    return 1;
+}
+
+int ossl_rsa_pss_get_param_unverified(const RSA_PSS_PARAMS *pss,
+                                      const EVP_MD **pmd, const EVP_MD **pmgf1md,
+                                      int *psaltlen, int *ptrailerField)
+{
+    RSA_PSS_PARAMS_30 pss_params;
+
+    /* Get the defaults from the ONE place */
+    (void)ossl_rsa_pss_params_30_set_defaults(&pss_params);
+
+    if (pss == NULL)
+        return 0;
+    *pmd = ossl_x509_algor_get_md(pss->hashAlgorithm);
+    if (*pmd == NULL)
+        return 0;
+    *pmgf1md = ossl_x509_algor_get_md(pss->maskHash);
+    if (*pmgf1md == NULL)
+        return 0;
+    if (pss->saltLength)
+        *psaltlen = ASN1_INTEGER_get(pss->saltLength);
+    else
+        *psaltlen = ossl_rsa_pss_params_30_saltlen(&pss_params);
+    if (pss->trailerField)
+        *ptrailerField = ASN1_INTEGER_get(pss->trailerField);
+    else
+        *ptrailerField = ossl_rsa_pss_params_30_trailerfield(&pss_params);;
+
+    return 1;
+}
+
+int ossl_rsa_param_decode(RSA *rsa, const X509_ALGOR *alg)
+{
+    RSA_PSS_PARAMS *pss;
+    const ASN1_OBJECT *algoid;
+    const void *algp;
+    int algptype;
+
+    X509_ALGOR_get0(&algoid, &algptype, &algp, alg);
+    if (OBJ_obj2nid(algoid) != EVP_PKEY_RSA_PSS)
+        return 1;
+    if (algptype == V_ASN1_UNDEF)
+        return 1;
+    if (algptype != V_ASN1_SEQUENCE) {
+        ERR_raise(ERR_LIB_RSA, RSA_R_INVALID_PSS_PARAMETERS);
+        return 0;
+    }
+    if ((pss = ossl_rsa_pss_decode(alg)) == NULL
+        || !ossl_rsa_set0_pss_params(rsa, pss)) {
+        RSA_PSS_PARAMS_free(pss);
+        return 0;
+    }
+    if (!ossl_rsa_sync_to_pss_params_30(rsa))
+        return 0;
+    return 1;
+}
+
+RSA *ossl_rsa_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                             OSSL_LIB_CTX *libctx, const char *propq)
+{
+    const unsigned char *p;
+    RSA *rsa;
+    int pklen;
+    const X509_ALGOR *alg;
+
+    if (!PKCS8_pkey_get0(NULL, &p, &pklen, &alg, p8inf))
+        return 0;
+    rsa = d2i_RSAPrivateKey(NULL, &p, pklen);
+    if (rsa == NULL) {
+        ERR_raise(ERR_LIB_RSA, ERR_R_RSA_LIB);
+        return NULL;
+    }
+    if (!ossl_rsa_param_decode(rsa, alg)) {
+        RSA_free(rsa);
+        return NULL;
+    }
+
+    RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+    switch (OBJ_obj2nid(alg->algorithm)) {
+    case EVP_PKEY_RSA:
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA);
+        break;
+    case EVP_PKEY_RSA_PSS:
+        RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS);
+        break;
+    default:
+        /* Leave the type bits zero */
+        break;
+    }
+
+    return rsa;
+}
+#endif
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index 89cd4da527..bfd274a66a 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -657,6 +657,18 @@ const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *r)
 #endif
 }
 
+/* Internal */
+int ossl_rsa_set0_pss_params(RSA *r, RSA_PSS_PARAMS *pss)
+{
+#ifdef FIPS_MODULE
+    return 0;
+#else
+    RSA_PSS_PARAMS_free(r->pss);
+    r->pss = pss;
+    return 1;
+#endif
+}
+
 /* Internal */
 RSA_PSS_PARAMS_30 *ossl_rsa_get0_pss_params_30(RSA *r)
 {
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index b1268f22c9..b2c8e4c83e 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -438,8 +438,8 @@ static EVP_PKEY *d2i_PUBKEY_int(EVP_PKEY **a,
 }
 
 /* For the algorithm specific d2i functions further down */
-EVP_PKEY *ossl_d2i_PUBKEY_legacy(EVP_PKEY **a,
-                                 const unsigned char **pp, long length)
+static EVP_PKEY *d2i_PUBKEY_legacy(EVP_PKEY **a,
+                                   const unsigned char **pp, long length)
 {
     return d2i_PUBKEY_int(a, pp, length, NULL, NULL, 1, d2i_X509_PUBKEY);
 }
@@ -512,11 +512,11 @@ int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
 RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp, long length)
 {
     EVP_PKEY *pkey;
-    RSA *key;
+    RSA *key = NULL;
     const unsigned char *q;
 
     q = *pp;
-    pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length);
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
     if (pkey == NULL)
         return NULL;
     key = EVP_PKEY_get1_RSA(pkey);
@@ -549,15 +549,99 @@ int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp)
     return ret;
 }
 
+#ifndef OPENSSL_NO_DH
+DH *ossl_d2i_DH_PUBKEY(DH **a, const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    DH *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_DH)
+        key = EVP_PKEY_get1_DH(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        DH_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_DH_PUBKEY(const DH *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+    if (!a)
+        return 0;
+    pktmp = EVP_PKEY_new();
+    if (pktmp == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign_DH(pktmp, (DH *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+
+DH *ossl_d2i_DHx_PUBKEY(DH **a, const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    DH *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_DHX)
+        key = EVP_PKEY_get1_DH(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        DH_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_DHx_PUBKEY(const DH *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+    if (!a)
+        return 0;
+    pktmp = EVP_PKEY_new();
+    if (pktmp == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign(pktmp, EVP_PKEY_DHX, (DH *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+#endif
+
 #ifndef OPENSSL_NO_DSA
 DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length)
 {
     EVP_PKEY *pkey;
-    DSA *key;
+    DSA *key = NULL;
     const unsigned char *q;
 
     q = *pp;
-    pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length);
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
     if (pkey == NULL)
         return NULL;
     key = EVP_PKEY_get1_DSA(pkey);
@@ -595,14 +679,15 @@ int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp)
 EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length)
 {
     EVP_PKEY *pkey;
-    EC_KEY *key;
+    EC_KEY *key = NULL;
     const unsigned char *q;
 
     q = *pp;
-    pkey = ossl_d2i_PUBKEY_legacy(NULL, &q, length);
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
     if (pkey == NULL)
         return NULL;
-    key = EVP_PKEY_get1_EC_KEY(pkey);
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_EC)
+        key = EVP_PKEY_get1_EC_KEY(pkey);
     EVP_PKEY_free(pkey);
     if (key == NULL)
         return NULL;
@@ -631,6 +716,174 @@ int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp)
     EVP_PKEY_free(pktmp);
     return ret;
 }
+
+ECX_KEY *ossl_d2i_ED25519_PUBKEY(ECX_KEY **a,
+                                 const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    ECX_KEY *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    key = ossl_evp_pkey_get1_ED25519(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        ossl_ecx_key_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_ED25519_PUBKEY(const ECX_KEY *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+
+    if (a == NULL)
+        return 0;
+    if ((pktmp = EVP_PKEY_new()) == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign(pktmp, EVP_PKEY_ED25519, (ECX_KEY *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+
+ECX_KEY *ossl_d2i_ED448_PUBKEY(ECX_KEY **a,
+                               const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    ECX_KEY *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_ED448)
+        key = ossl_evp_pkey_get1_ED448(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        ossl_ecx_key_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_ED448_PUBKEY(const ECX_KEY *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+
+    if (a == NULL)
+        return 0;
+    if ((pktmp = EVP_PKEY_new()) == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign(pktmp, EVP_PKEY_ED448, (ECX_KEY *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+
+ECX_KEY *ossl_d2i_X25519_PUBKEY(ECX_KEY **a,
+                                const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    ECX_KEY *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_X25519)
+        key = ossl_evp_pkey_get1_X25519(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        ossl_ecx_key_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_X25519_PUBKEY(const ECX_KEY *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+
+    if (a == NULL)
+        return 0;
+    if ((pktmp = EVP_PKEY_new()) == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign(pktmp, EVP_PKEY_X25519, (ECX_KEY *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+
+ECX_KEY *ossl_d2i_X448_PUBKEY(ECX_KEY **a,
+                              const unsigned char **pp, long length)
+{
+    EVP_PKEY *pkey;
+    ECX_KEY *key = NULL;
+    const unsigned char *q;
+
+    q = *pp;
+    pkey = d2i_PUBKEY_legacy(NULL, &q, length);
+    if (pkey == NULL)
+        return NULL;
+    if (EVP_PKEY_id(pkey) == EVP_PKEY_X448)
+        key = ossl_evp_pkey_get1_X448(pkey);
+    EVP_PKEY_free(pkey);
+    if (key == NULL)
+        return NULL;
+    *pp = q;
+    if (a != NULL) {
+        ossl_ecx_key_free(*a);
+        *a = key;
+    }
+    return key;
+}
+
+int ossl_i2d_X448_PUBKEY(const ECX_KEY *a, unsigned char **pp)
+{
+    EVP_PKEY *pktmp;
+    int ret;
+
+    if (a == NULL)
+        return 0;
+    if ((pktmp = EVP_PKEY_new()) == NULL) {
+        ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE);
+        return -1;
+    }
+    (void)EVP_PKEY_assign(pktmp, EVP_PKEY_X448, (ECX_KEY *)a);
+    ret = i2d_PUBKEY(pktmp, pp);
+    pktmp->pkey.ptr = NULL;
+    EVP_PKEY_free(pktmp);
+    return ret;
+}
+
 #endif
 
 int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj,
diff --git a/include/crypto/dh.h b/include/crypto/dh.h
index d8b597a0f1..b16ac8f42f 100644
--- a/include/crypto/dh.h
+++ b/include/crypto/dh.h
@@ -14,6 +14,7 @@
 # include <openssl/core.h>
 # include <openssl/params.h>
 # include <openssl/dh.h>
+# include <openssl/x509.h>
 # include "internal/ffc.h"
 
 DH *ossl_dh_new_by_nid_ex(OSSL_LIB_CTX *libctx, int nid);
@@ -35,6 +36,8 @@ int ossl_dh_params_fromdata(DH *dh, const OSSL_PARAM params[]);
 int ossl_dh_key_fromdata(DH *dh, const OSSL_PARAM params[]);
 int ossl_dh_params_todata(DH *dh, OSSL_PARAM_BLD *bld, OSSL_PARAM params[]);
 int ossl_dh_key_todata(DH *dh, OSSL_PARAM_BLD *bld, OSSL_PARAM params[]);
+DH *ossl_dh_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                           OSSL_LIB_CTX *libctx, const char *propq);
 
 int ossl_dh_check_pub_key_partial(const DH *dh, const BIGNUM *pub_key, int *ret);
 int ossl_dh_check_priv_key(const DH *dh, const BIGNUM *priv_key, int *ret);
diff --git a/include/crypto/dsa.h b/include/crypto/dsa.h
index 0c15c51da0..38c49c3295 100644
--- a/include/crypto/dsa.h
+++ b/include/crypto/dsa.h
@@ -13,6 +13,7 @@
 
 # include <openssl/core.h>
 # include <openssl/dsa.h>
+# include <openssl/x509.h>
 # include "internal/ffc.h"
 
 #define DSA_PARAMGEN_TYPE_FIPS_186_4   0   /* Use FIPS186-4 standard */
@@ -31,6 +32,8 @@ int ossl_dsa_sign_int(int type, const unsigned char *dgst, int dlen,
 FFC_PARAMS *ossl_dsa_get0_params(DSA *dsa);
 int ossl_dsa_ffc_params_fromdata(DSA *dsa, const OSSL_PARAM params[]);
 int ossl_dsa_key_fromdata(DSA *dsa, const OSSL_PARAM params[]);
+DSA *ossl_dsa_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                             OSSL_LIB_CTX *libctx, const char *propq);
 
 int ossl_dsa_generate_public_key(BN_CTX *ctx, const DSA *dsa,
                                  const BIGNUM *priv_key, BIGNUM *pub_key);
diff --git a/include/crypto/ec.h b/include/crypto/ec.h
index a3d87e9d1a..c679fd8d11 100644
--- a/include/crypto/ec.h
+++ b/include/crypto/ec.h
@@ -25,6 +25,7 @@ int evp_pkey_ctx_set_ec_param_enc_prov(EVP_PKEY_CTX *ctx, int param_enc);
 # ifndef OPENSSL_NO_EC
 #  include <openssl/core.h>
 #  include <openssl/ec.h>
+#  include <openssl/x509.h>
 #  include "crypto/types.h"
 
 /*-
@@ -78,6 +79,12 @@ int ossl_ec_group_set_params(EC_GROUP *group, const OSSL_PARAM params[]);
 int ossl_ec_key_fromdata(EC_KEY *ecx, const OSSL_PARAM params[],
                          int include_private);
 int ossl_ec_key_otherparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[]);
+EC_KEY *ossl_ec_key_param_from_x509_algor(const X509_ALGOR *palg,
+                                          OSSL_LIB_CTX *libctx,
+                                          const char *propq);
+EC_KEY *ossl_ec_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                               OSSL_LIB_CTX *libctx, const char *propq);
+
 int ossl_ec_set_ecdh_cofactor_mode(EC_KEY *ec, int mode);
 int ossl_ec_encoding_name2id(const char *name);
 int ossl_ec_encoding_param2id(const OSSL_PARAM *p, int *id);
diff --git a/include/crypto/ecx.h b/include/crypto/ecx.h
index 101a5748e8..dfc70fb4e6 100644
--- a/include/crypto/ecx.h
+++ b/include/crypto/ecx.h
@@ -20,7 +20,9 @@
 #  include <openssl/core.h>
 #  include <openssl/e_os2.h>
 #  include <openssl/crypto.h>
+#  include <openssl/x509.h>
 #  include "internal/refcount.h"
+#  include "crypto/types.h"
 
 #  define X25519_KEYLEN         32
 #  define X448_KEYLEN           56
@@ -76,6 +78,7 @@ struct ecx_key_st {
 
 typedef struct ecx_key_st ECX_KEY;
 
+size_t ossl_ecx_key_length(ECX_KEY_TYPE type);
 ECX_KEY *ossl_ecx_key_new(OSSL_LIB_CTX *libctx, ECX_KEY_TYPE type,
                           int haspubkey, const char *propq);
 void ossl_ecx_key_set0_libctx(ECX_KEY *key, OSSL_LIB_CTX *libctx);
@@ -124,9 +127,22 @@ ossl_x448_public_from_private(uint8_t out_public_value[56],
 
 
 /* Backend support */
+typedef enum {
+    KEY_OP_PUBLIC,
+    KEY_OP_PRIVATE,
+    KEY_OP_KEYGEN
+} ecx_key_op_t;
+
+ECX_KEY *ossl_ecx_key_op(const X509_ALGOR *palg,
+                         const unsigned char *p, int plen,
+                         int pkey_id, ecx_key_op_t op,
+                         OSSL_LIB_CTX *libctx, const char *propq);
+
 int ossl_ecx_public_from_private(ECX_KEY *key);
 int ossl_ecx_key_fromdata(ECX_KEY *ecx, const OSSL_PARAM params[],
                           int include_private);
+ECX_KEY *ossl_ecx_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                                 OSSL_LIB_CTX *libctx, const char *propq);
 
 ECX_KEY *ossl_evp_pkey_get1_X25519(EVP_PKEY *pkey);
 ECX_KEY *ossl_evp_pkey_get1_X448(EVP_PKEY *pkey);
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 751b330b43..18c50cdd33 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -868,10 +868,6 @@ int evp_set_default_properties_int(OSSL_LIB_CTX *libctx, const char *propq,
 
 void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force);
 
-EVP_PKEY *evp_privatekey_from_binary(int keytype, EVP_PKEY **a,
-                                     const unsigned char **pp, long length,
-                                     OSSL_LIB_CTX *libctx, const char *propq);
-
 /* Three possible states: */
 # define EVP_PKEY_STATE_UNKNOWN         0
 # define EVP_PKEY_STATE_LEGACY          1
diff --git a/include/crypto/pem.h b/include/crypto/pem.h
index 4b02a00a85..2a0e6424a5 100644
--- a/include/crypto/pem.h
+++ b/include/crypto/pem.h
@@ -12,15 +12,36 @@
 # pragma once
 
 # include <openssl/pem.h>
+# include "crypto/types.h"
 
 /* Found in crypto/pem/pvkfmt.c */
+
+/* Maximum length of a blob after header */
+# define BLOB_MAX_LENGTH          102400
+
 int ossl_do_blob_header(const unsigned char **in, unsigned int length,
                         unsigned int *pmagic, unsigned int *pbitlen,
                         int *pisdss, int *pispub);
+unsigned int ossl_blob_length(unsigned bitlen, int isdss, int ispub);
 int ossl_do_PVK_header(const unsigned char **in, unsigned int length,
                        int skip_magic,
                        unsigned int *psaltlen, unsigned int *pkeylen);
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+#  ifndef OPENSSL_NO_DSA
+DSA *ossl_b2i_DSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub);
+#  endif
+RSA *ossl_b2i_RSA_after_header(const unsigned char **in, unsigned int bitlen,
+                               int ispub);
+# endif
 EVP_PKEY *ossl_b2i(const unsigned char **in, unsigned int length, int *ispub);
 EVP_PKEY *ossl_b2i_bio(BIO *in, int *ispub);
 
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+#  ifndef OPENSSL_NO_DSA
+DSA *b2i_DSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u);
+#  endif
+RSA *b2i_RSA_PVK_bio(BIO *in, pem_password_cb *cb, void *u);
+# endif
+
 #endif
diff --git a/include/crypto/pemerr.h b/include/crypto/pemerr.h
index 08a56f1425..24c4d0c585 100644
--- a/include/crypto/pemerr.h
+++ b/include/crypto/pemerr.h
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h
index 7fca88dfe4..69fa8a4d8a 100644
--- a/include/crypto/rsa.h
+++ b/include/crypto/rsa.h
@@ -13,6 +13,7 @@
 
 # include <openssl/core.h>
 # include <openssl/rsa.h>
+# include <openssl/x509.h>
 # include "crypto/types.h"
 
 typedef struct rsa_pss_params_30_st {
@@ -69,6 +70,14 @@ int ossl_rsa_pss_params_30_fromdata(RSA_PSS_PARAMS_30 *pss_params,
                                     int *defaults_set,
                                     const OSSL_PARAM params[],
                                     OSSL_LIB_CTX *libctx);
+int ossl_rsa_set0_pss_params(RSA *r, RSA_PSS_PARAMS *pss);
+int ossl_rsa_pss_get_param_unverified(const RSA_PSS_PARAMS *pss,
+                                      const EVP_MD **pmd, const EVP_MD **pmgf1md,
+                                      int *psaltlen, int *ptrailerField);
+RSA_PSS_PARAMS *ossl_rsa_pss_decode(const X509_ALGOR *alg);
+int ossl_rsa_param_decode(RSA *rsa, const X509_ALGOR *alg);
+RSA *ossl_rsa_key_from_pkcs8(const PKCS8_PRIV_KEY_INFO *p8inf,
+                             OSSL_LIB_CTX *libctx, const char *propq);
 
 int ossl_rsa_padding_check_PKCS1_type_2_TLS(OSSL_LIB_CTX *ctx, unsigned char *to,
                                             size_t tlen,
@@ -113,4 +122,5 @@ void ossl_rsa_acvp_test_free(RSA_ACVP_TEST *t);
 # define RSA_ACVP_TEST void
 # endif
 
+RSA *evp_pkey_get1_RSA_PSS(EVP_PKEY *pkey);
 #endif
diff --git a/include/crypto/types.h b/include/crypto/types.h
index 275d1d36c1..13a5f9ce1d 100644
--- a/include/crypto/types.h
+++ b/include/crypto/types.h
@@ -12,6 +12,12 @@
 #ifdef OPENSSL_NO_DEPRECATED_3_0
 typedef struct rsa_st RSA;
 typedef struct rsa_meth_st RSA_METHOD;
+# ifndef OPENSSL_NO_EC
 typedef struct ec_key_st EC_KEY;
 typedef struct ec_key_method_st EC_KEY_METHOD;
+# endif
+#endif
+
+#ifndef OPENSSL_NO_EC
+typedef struct ecx_key_st ECX_KEY;
 #endif
diff --git a/include/crypto/x509.h b/include/crypto/x509.h
index 623b880959..09b1fb15e3 100644
--- a/include/crypto/x509.h
+++ b/include/crypto/x509.h
@@ -14,6 +14,7 @@
 # include "internal/refcount.h"
 # include <openssl/asn1.h>
 # include <openssl/x509.h>
+# include "crypto/types.h"
 
 /* Internal X509 structures and functions: not for application use */
 
@@ -325,7 +326,26 @@ int ossl_x509_PUBKEY_get0_libctx(OSSL_LIB_CTX **plibctx, const char **ppropq,
 /* Calculate default key identifier according to RFC 5280 section 4.2.1.2 (1) */
 ASN1_OCTET_STRING *ossl_x509_pubkey_hash(X509_PUBKEY *pubkey);
 
-/* A variant of d2i_PUBKEY() that is guaranteed to only return legacy keys */
-EVP_PKEY *ossl_d2i_PUBKEY_legacy(EVP_PKEY **a,
-                                 const unsigned char **in, long length);
+RSA *ossl_d2i_RSA_PSS_PUBKEY(RSA **a, const unsigned char **pp, long length);
+int ossl_i2d_RSA_PSS_PUBKEY(const RSA *a, unsigned char **pp);
+# ifndef OPENSSL_NO_DH
+DH *ossl_d2i_DH_PUBKEY(DH **a, const unsigned char **pp, long length);
+int ossl_i2d_DH_PUBKEY(const DH *a, unsigned char **pp);
+DH *ossl_d2i_DHx_PUBKEY(DH **a, const unsigned char **pp, long length);
+int ossl_i2d_DHx_PUBKEY(const DH *a, unsigned char **pp);
+# endif
+# ifndef OPENSSL_NO_EC
+ECX_KEY *ossl_d2i_ED25519_PUBKEY(ECX_KEY **a,
+                                 const unsigned char **pp, long length);
+int ossl_i2d_ED25519_PUBKEY(const ECX_KEY *a, unsigned char **pp);
+ECX_KEY *ossl_d2i_ED448_PUBKEY(ECX_KEY **a,
+                               const unsigned char **pp, long length);
+int ossl_i2d_ED448_PUBKEY(const ECX_KEY *a, unsigned char **pp);
+ECX_KEY *ossl_d2i_X25519_PUBKEY(ECX_KEY **a,
+                                const unsigned char **pp, long length);
+int ossl_i2d_X25519_PUBKEY(const ECX_KEY *a, unsigned char **pp);
+ECX_KEY *ossl_d2i_X448_PUBKEY(ECX_KEY **a,
+                              const unsigned char **pp, long length);
+int ossl_i2d_X448_PUBKEY(const ECX_KEY *a, unsigned char **pp);
+# endif
 #endif
diff --git a/include/openssl/pemerr.h b/include/openssl/pemerr.h
index 16ca273a98..18f6d9ef4c 100644
--- a/include/openssl/pemerr.h
+++ b/include/openssl/pemerr.h
@@ -31,8 +31,10 @@
 # define PEM_R_BIO_WRITE_FAILURE                          118
 # define PEM_R_CIPHER_IS_NULL                             127
 # define PEM_R_ERROR_CONVERTING_PRIVATE_KEY               115
+# define PEM_R_EXPECTING_DSS_KEY_BLOB                     131
 # define PEM_R_EXPECTING_PRIVATE_KEY_BLOB                 119
 # define PEM_R_EXPECTING_PUBLIC_KEY_BLOB                  120
+# define PEM_R_EXPECTING_RSA_KEY_BLOB                     132
 # define PEM_R_HEADER_TOO_LONG                            128
 # define PEM_R_INCONSISTENT_HEADER                        121
 # define PEM_R_KEYBLOB_HEADER_PARSE_ERROR                 122
diff --git a/providers/implementations/encode_decode/build.info b/providers/implementations/encode_decode/build.info
index 5b8d9f6ef2..694e3c94a5 100644
--- a/providers/implementations/encode_decode/build.info
+++ b/providers/implementations/encode_decode/build.info
@@ -12,7 +12,8 @@ $EC_GOAL=../../libimplementations.a
 
 SOURCE[$ENCODER_GOAL]=endecoder_common.c
 
-SOURCE[$DECODER_GOAL]=decode_der2key.c decode_pem2der.c decode_ms2key.c
+SOURCE[$DECODER_GOAL]=decode_der2key.c decode_pem2der.c \
+                      decode_msblob2key.c decode_pvk2key.c
 
 SOURCE[$ENCODER_GOAL]=encode_key2any.c encode_key2text.c encode_key2ms.c
 # encode_key2blob.c is only being included when EC is enabled, because we
diff --git a/providers/implementations/encode_decode/decode_der2key.c b/providers/implementations/encode_decode/decode_der2key.c
index 359116a8a9..f50fca3896 100644
--- a/providers/implementations/encode_decode/decode_der2key.c
+++ b/providers/implementations/encode_decode/decode_der2key.c
@@ -44,7 +44,8 @@
         if (ERR_GET_LIB(err) == ERR_LIB_ASN1                            \
             && (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG           \
                 || ERR_GET_REASON(err) == ASN1_R_UNSUPPORTED_TYPE       \
-                || ERR_GET_REASON(err) == ERR_R_NESTED_ASN1_ERROR))     \
+                || ERR_GET_REASON(err) == ERR_R_NESTED_ASN1_ERROR       \
+                || ERR_GET_REASON(err) == ASN1_R_NOT_ENOUGH_DATA))      \
             ERR_pop_to_mark();                                          \
         else                                                            \
             ERR_clear_last_mark();                                      \
@@ -55,67 +56,13 @@
         SET_ERR_MARK();                                                 \
     } while(0)
 
-static int read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
-                    unsigned char **data, long *len)
-{
-    BUF_MEM *mem = NULL;
-    BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
-    int ok = (asn1_d2i_read_bio(in, &mem) >= 0);
-
-    if (ok) {
-        *data = (unsigned char *)mem->data;
-        *len = (long)mem->length;
-        OPENSSL_free(mem);
-    }
-    BIO_free(in);
-    return ok;
-}
-
-static int der_from_p8(unsigned char **new_der, long *new_der_len,
-                       unsigned char *input_der, long input_der_len,
-                       OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    const unsigned char *derp;
-    X509_SIG *p8 = NULL;
-    int ok = 0;
-
-    if (!ossl_assert(new_der != NULL && *new_der == NULL)
-        || !ossl_assert(new_der_len != NULL))
-        return 0;
-
-    derp = input_der;
-    if ((p8 = d2i_X509_SIG(NULL, &derp, input_der_len)) != NULL) {
-        char pbuf[PEM_BUFSIZE];
-        size_t plen = 0;
-
-        if (!pw_cb(pbuf, sizeof(pbuf), &plen, NULL, pw_cbarg)) {
-            ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE);
-        } else {
-            const X509_ALGOR *alg = NULL;
-            const ASN1_OCTET_STRING *oct = NULL;
-            int len = 0;
-
-            X509_SIG_get0(p8, &alg, &oct);
-            if (PKCS12_pbe_crypt(alg, pbuf, plen, oct->data, oct->length,
-                                 new_der, &len, 0) != NULL)
-                ok = 1;
-            *new_der_len = len;
-        }
-    }
-    X509_SIG_free(p8);
-    return ok;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static OSSL_FUNC_decoder_freectx_fn der2key_freectx;
-static OSSL_FUNC_decoder_decode_fn der2key_decode;
-static OSSL_FUNC_decoder_export_object_fn der2key_export_object;
-
 struct der2key_ctx_st;           /* Forward declaration */
-typedef void *(extract_key_fn)(EVP_PKEY *);
-typedef void (adjust_key_fn)(void *, struct der2key_ctx_st *ctx);
-typedef void (free_key_fn)(void *);
+typedef int check_key_fn(void *, struct der2key_ctx_st *ctx);
+typedef void adjust_key_fn(void *, struct der2key_ctx_st *ctx);
+typedef void free_key_fn(void *);
+typedef void *d2i_PKCS8_fn(void **, const unsigned char **, long,
+                           struct der2key_ctx_st *,
+                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg);
 struct keytype_desc_st {
     const char *keytype_name;
     const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
@@ -135,14 +82,19 @@ struct keytype_desc_st {
     int selection_mask;
 
     /* For type specific decoders, we use the corresponding d2i */
-    d2i_of_void *d2i_private_key;
-    d2i_of_void *d2i_public_key;
-    d2i_of_void *d2i_key_params;
+    d2i_of_void *d2i_private_key; /* From type-specific DER */
+    d2i_of_void *d2i_public_key;  /* From type-specific DER */
+    d2i_of_void *d2i_key_params;  /* From type-specific DER */
+    d2i_PKCS8_fn *d2i_PKCS8;      /* Wrapped in a PKCS#8, possibly encrypted */
+    d2i_of_void *d2i_PUBKEY;      /* Wrapped in a SubjectPublicKeyInfo */
 
     /*
-     * For PKCS#8 decoders, we use EVP_PKEY extractors, EVP_PKEY_get1_{TYPE}()
+     * For any key, we may need to check that the key meets expectations.
+     * This is useful when the same functions can decode several variants
+     * of a key.
      */
-    extract_key_fn *extract_key;
+    check_key_fn *check_key;
+
     /*
      * For any key, we may need to make provider specific adjustments, such
      * as ensure the key carries the correct library context.
@@ -158,8 +110,67 @@ struct keytype_desc_st {
 struct der2key_ctx_st {
     PROV_CTX *provctx;
     const struct keytype_desc_st *desc;
+    /* Flag used to signal that a failure is fatal */
+    unsigned int flag_fatal : 1;
 };
 
+static int read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+                    unsigned char **data, long *len)
+{
+    BUF_MEM *mem = NULL;
+    BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
+    int ok = (asn1_d2i_read_bio(in, &mem) >= 0);
+
+    if (ok) {
+        *data = (unsigned char *)mem->data;
+        *len = (long)mem->length;
+        OPENSSL_free(mem);
+    }
+    BIO_free(in);
+    return ok;
+}
+
+typedef void *key_from_pkcs8_t(const PKCS8_PRIV_KEY_INFO *p8inf,
+                               OSSL_LIB_CTX *libctx, const char *propq);
+static void *der2key_decode_p8(const unsigned char **input_der,
+                               long input_der_len, struct der2key_ctx_st *ctx,
+                               OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+                               key_from_pkcs8_t *key_from_pkcs8)
+{
+    X509_SIG *p8 = NULL;
+    PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+    const X509_ALGOR *alg = NULL;
+    void *key = NULL;
+
+    ctx->flag_fatal = 0;
+    if ((p8 = d2i_X509_SIG(NULL, input_der, input_der_len)) != NULL) {
+        char pbuf[PEM_BUFSIZE];
+        size_t plen = 0;
+
+        if (!pw_cb(pbuf, sizeof(pbuf), &plen, NULL, pw_cbarg))
+            ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE);
+        else
+            p8inf = PKCS8_decrypt(p8, pbuf, plen);
+        if (p8inf == NULL)
+            ctx->flag_fatal = 1;
+        X509_SIG_free(p8);
+    } else {
+        p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, input_der, input_der_len);
+    }
+    if (p8inf != NULL
+        && PKCS8_pkey_get0(NULL, NULL, NULL, &alg, p8inf)
+        && OBJ_obj2nid(alg->algorithm) == ctx->desc->evp_type)
+        key = key_from_pkcs8(p8inf, PROV_LIBCTX_OF(ctx->provctx), NULL);
+    PKCS8_PRIV_KEY_INFO_free(p8inf);
+    return key;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static OSSL_FUNC_decoder_freectx_fn der2key_freectx;
+static OSSL_FUNC_decoder_decode_fn der2key_decode;
+static OSSL_FUNC_decoder_export_object_fn der2key_export_object;
+
 static struct der2key_ctx_st *
 der2key_newctx(void *provctx, const struct keytype_desc_st *desc)
 {
@@ -251,16 +262,11 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
 {
     struct der2key_ctx_st *ctx = vctx;
-    void *libctx = PROV_LIBCTX_OF(ctx->provctx);
     unsigned char *der = NULL;
     const unsigned char *derp;
     long der_len = 0;
-    unsigned char *new_der = NULL;
-    long new_der_len;
-    EVP_PKEY *pkey = NULL;
     void *key = NULL;
     int orig_selection = selection;
-    int dec_err;
     int ok = 0;
 
     /*
@@ -280,91 +286,52 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
 
     SET_ERR_MARK();
     if (!read_der(ctx->provctx, cin, &der, &der_len))
-        goto end;
+        goto next;
 
-    if (ctx->desc->extract_key == NULL) {
-        /*
-         * There's no EVP_PKEY extractor, so we use the type specific
-         * functions.
-         */
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        RESET_ERR_MARK();
         derp = der;
-        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
-            key = ctx->desc->d2i_private_key(NULL, &derp, der_len);
-            if (key == NULL && orig_selection != 0)
+        if (ctx->desc->d2i_PKCS8 != NULL) {
+            key = ctx->desc->d2i_PKCS8(NULL, &derp, der_len, ctx,
+                                       pw_cb, pw_cbarg);
+            if (ctx->flag_fatal)
                 goto end;
+        } else if (ctx->desc->d2i_private_key != NULL) {
+            key = ctx->desc->d2i_private_key(NULL, &derp, der_len);
         }
-        if (key == NULL
-            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        if (key == NULL && orig_selection != 0)
+            goto next;
+    }
+    if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        RESET_ERR_MARK();
+        derp = der;
+        if (ctx->desc->d2i_PUBKEY != NULL)
+            key = ctx->desc->d2i_PUBKEY(NULL, &derp, der_len);
+        else
             key = ctx->desc->d2i_public_key(NULL, &derp, der_len);
-            if (key == NULL && orig_selection != 0)
-                goto end;
-        }
-        if (key == NULL
-            && (selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) {
-            key = ctx->desc->d2i_key_params(NULL, &derp, der_len);
-        }
-    } else {
-        /*
-         * There is a EVP_PKEY extractor, so we use the more generic
-         * EVP_PKEY functions, since they know how to unpack PKCS#8 and
-         * SubjectPublicKeyInfo.
-         */
-
-        /*
-         * Opportunistic attempt to decrypt.  If it doesn't work, we try
-         * to decode our input unencrypted.
-         */
-        if (der_from_p8(&new_der, &new_der_len, der, der_len,
-                        pw_cb, pw_cbarg)) {
-            OPENSSL_free(der);
-            der = new_der;
-            der_len = new_der_len;
-        }
-        /* decryption errors are fatal and should be reported */
-        dec_err = ERR_peek_last_error();
-        if (ERR_GET_LIB(dec_err) == ERR_LIB_PROV
-                && ERR_GET_REASON(dec_err) == PROV_R_BAD_DECRYPT)
-            goto end;
-
+        if (key == NULL && orig_selection != 0)
+            goto next;
+    }
+    if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) {
         RESET_ERR_MARK();
-        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
-            derp = der;
-            pkey = evp_privatekey_from_binary(ctx->desc->evp_type, NULL,
-                                              &derp, der_len, libctx, NULL);
-        }
-
-        if (pkey == NULL
-            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
-            RESET_ERR_MARK();
-            derp = der;
-            pkey = ossl_d2i_PUBKEY_legacy(NULL, &derp, der_len);
-        }
-
-        if (pkey != NULL) {
-            /*
-             * Tear out the low-level key pointer from the pkey,
-             * but only if it matches the expected key type.
-             *
-             * The check should be done with EVP_PKEY_is_a(), but
-             * as long as we still have #legacy internal keys, it's safer
-             * to use the type numbers inside the provider.
-             */
-            if (EVP_PKEY_id(pkey) == ctx->desc->evp_type)
-                key = ctx->desc->extract_key(pkey);
-
-            /*
-             * ctx->desc->extract_key() is expected to have incremented
-             * |key|'s reference count, so it should be safe to free |pkey|
-             * now.
-             */
-            EVP_PKEY_free(pkey);
-        }
+        derp = der;
+        if (ctx->desc->d2i_key_params != NULL)
+            key = ctx->desc->d2i_key_params(NULL, &derp, der_len);
+        if (key == NULL && orig_selection != 0)
+            goto next;
+    }
+    RESET_ERR_MARK();
+    if (key != NULL
+        && ctx->desc->check_key != NULL
+        && !ctx->desc->check_key(key, ctx)) {
+        CLEAR_ERR_MARK();
+        goto end;
     }
 
     if (key != NULL && ctx->desc->adjust_key != NULL)
         ctx->desc->adjust_key(key, ctx);
 
- end:
+ next:
     /*
      * Prune low-level ASN.1 parse errors from error queue, assuming
      * that this is called by decoder_process() in a loop trying several
@@ -372,7 +339,13 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
      */
     CLEAR_ERR_MARK();
 
+    /*
+     * We free memory here so it's not held up during the callback, because
+     * we know the process is recursive and the allocated chunks of memory
+     * add up.
+     */
     OPENSSL_free(der);
+    der = NULL;
 
     if (key != NULL) {
         OSSL_PARAM params[4];
@@ -392,7 +365,10 @@ static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
 
         ok = data_cb(params, data_cbarg);
     }
+
+ end:
     ctx->desc->free_key(key);
+    OPENSSL_free(der);
 
     return ok;
 }
@@ -420,11 +396,21 @@ static int der2key_export_object(void *vctx,
 
 #ifndef OPENSSL_NO_DH
 # define dh_evp_type                    EVP_PKEY_DH
-# define dh_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_DH
 # define dh_d2i_private_key             NULL
 # define dh_d2i_public_key              NULL
 # define dh_d2i_key_params              (d2i_of_void *)d2i_DHparams
+
+static void *dh_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                          struct der2key_ctx_st *ctx,
+                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_dh_key_from_pkcs8);
+}
+
+# define dh_d2i_PUBKEY                  (d2i_of_void *)ossl_d2i_DH_PUBKEY
 # define dh_free                        (free_key_fn *)DH_free
+# define dh_check                       NULL
 
 static void dh_adjust(void *key, struct der2key_ctx_st *ctx)
 {
@@ -432,11 +418,13 @@ static void dh_adjust(void *key, struct der2key_ctx_st *ctx)
 }
 
 # define dhx_evp_type                   EVP_PKEY_DHX
-# define dhx_evp_extract                (extract_key_fn *)EVP_PKEY_get1_DH
 # define dhx_d2i_private_key            NULL
 # define dhx_d2i_public_key             NULL
 # define dhx_d2i_key_params             (d2i_of_void *)d2i_DHxparams
+# define dhx_d2i_PKCS8                  dh_d2i_PKCS8
+# define dhx_d2i_PUBKEY                 (d2i_of_void *)ossl_d2i_DHx_PUBKEY
 # define dhx_free                       (free_key_fn *)DH_free
+# define dhx_check                      NULL
 # define dhx_adjust                     dh_adjust
 #endif
 
@@ -444,11 +432,21 @@ static void dh_adjust(void *key, struct der2key_ctx_st *ctx)
 
 #ifndef OPENSSL_NO_DSA
 # define dsa_evp_type                   EVP_PKEY_DSA
-# define dsa_evp_extract                (extract_key_fn *)EVP_PKEY_get1_DSA
 # define dsa_d2i_private_key            (d2i_of_void *)d2i_DSAPrivateKey
 # define dsa_d2i_public_key             (d2i_of_void *)d2i_DSAPublicKey
 # define dsa_d2i_key_params             (d2i_of_void *)d2i_DSAparams
+
+static void *dsa_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                           struct der2key_ctx_st *ctx,
+                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_dsa_key_from_pkcs8);
+}
+
+# define dsa_d2i_PUBKEY                 (d2i_of_void *)d2i_DSA_PUBKEY
 # define dsa_free                       (free_key_fn *)DSA_free
+# define dsa_check                      NULL
 
 static void dsa_adjust(void *key, struct der2key_ctx_st *ctx)
 {
@@ -460,12 +458,30 @@ static void dsa_adjust(void *key, struct der2key_ctx_st *ctx)
 
 #ifndef OPENSSL_NO_EC
 # define ec_evp_type                    EVP_PKEY_EC
-# define ec_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_EC_KEY
 # define ec_d2i_private_key             (d2i_of_void *)d2i_ECPrivateKey
 # define ec_d2i_public_key              NULL
 # define ec_d2i_key_params              (d2i_of_void *)d2i_ECParameters
+
+static void *ec_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                          struct der2key_ctx_st *ctx,
+                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8);
+}
+
+# define ec_d2i_PUBKEY                  (d2i_of_void *)d2i_EC_PUBKEY
 # define ec_free                        (free_key_fn *)EC_KEY_free
 
+static int ec_check(void *key, struct der2key_ctx_st *ctx)
+{
+    /* We're trying to be clever by comparing two truths */
+
+    int sm2 = (EC_KEY_get_flags(key) & EC_FLAG_SM2_RANGE) != 0;
+
+    return sm2 == (ctx->desc->evp_type == EVP_PKEY_SM2);
+}
+
 static void ec_adjust(void *key, struct der2key_ctx_st *ctx)
 {
     ossl_ec_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
@@ -476,50 +492,76 @@ static void ec_adjust(void *key, struct der2key_ctx_st *ctx)
  * so no d2i functions to be had.
  */
 
+static void *ecx_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                           struct der2key_ctx_st *ctx,
+                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_ecx_key_from_pkcs8);
+}
+
 static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx)
 {
     ossl_ecx_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
 }
 
 # define ed25519_evp_type               EVP_PKEY_ED25519
-# define ed25519_evp_extract            (extract_key_fn *)ossl_evp_pkey_get1_ED25519
 # define ed25519_d2i_private_key        NULL
 # define ed25519_d2i_public_key         NULL
 # define ed25519_d2i_key_params         NULL
+# define ed25519_d2i_PKCS8              ecx_d2i_PKCS8
+# define ed25519_d2i_PUBKEY             (d2i_of_void *)ossl_d2i_ED25519_PUBKEY
 # define ed25519_free                   (free_key_fn *)ossl_ecx_key_free
+# define ed25519_check                  NULL
 # define ed25519_adjust                 ecx_key_adjust
 
 # define ed448_evp_type                 EVP_PKEY_ED448
-# define ed448_evp_extract              (extract_key_fn *)ossl_evp_pkey_get1_ED448
 # define ed448_d2i_private_key          NULL
 # define ed448_d2i_public_key           NULL
 # define ed448_d2i_key_params           NULL
+# define ed448_d2i_PKCS8                ecx_d2i_PKCS8
+# define ed448_d2i_PUBKEY               (d2i_of_void *)ossl_d2i_ED448_PUBKEY
 # define ed448_free                     (free_key_fn *)ossl_ecx_key_free
+# define ed448_check                    NULL
 # define ed448_adjust                   ecx_key_adjust
 
 # define x25519_evp_type                EVP_PKEY_X25519
-# define x25519_evp_extract             (extract_key_fn *)ossl_evp_pkey_get1_X25519
 # define x25519_d2i_private_key         NULL
 # define x25519_d2i_public_key          NULL
 # define x25519_d2i_key_params          NULL
+# define x25519_d2i_PKCS8               ecx_d2i_PKCS8
+# define x25519_d2i_PUBKEY              (d2i_of_void *)ossl_d2i_X25519_PUBKEY
 # define x25519_free                    (free_key_fn *)ossl_ecx_key_free
+# define x25519_check                   NULL
 # define x25519_adjust                  ecx_key_adjust
 
 # define x448_evp_type                  EVP_PKEY_X448
-# define x448_evp_extract               (extract_key_fn *)ossl_evp_pkey_get1_X448
 # define x448_d2i_private_key           NULL
 # define x448_d2i_public_key            NULL
 # define x448_d2i_key_params            NULL
+# define x448_d2i_PKCS8                 ecx_d2i_PKCS8
+# define x448_d2i_PUBKEY                (d2i_of_void *)ossl_d2i_X448_PUBKEY
 # define x448_free                      (free_key_fn *)ossl_ecx_key_free
+# define x448_check                     NULL
 # define x448_adjust                    ecx_key_adjust
 
 # ifndef OPENSSL_NO_SM2
 #  define sm2_evp_type                  EVP_PKEY_SM2
-#  define sm2_evp_extract               (extract_key_fn *)EVP_PKEY_get1_EC_KEY
 #  define sm2_d2i_private_key           (d2i_of_void *)d2i_ECPrivateKey
 #  define sm2_d2i_public_key            NULL
 #  define sm2_d2i_key_params            (d2i_of_void *)d2i_ECParameters
+
+static void *sm2_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                           struct der2key_ctx_st *ctx,
+                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8);
+}
+
+#  define sm2_d2i_PUBKEY                (d2i_of_void *)d2i_EC_PUBKEY
 #  define sm2_free                      (free_key_fn *)EC_KEY_free
+#  define sm2_check                     ec_check
 #  define sm2_adjust                    ec_adjust
 # endif
 #endif
@@ -527,23 +569,47 @@ static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx)
 /* ---------------------------------------------------------------------- */
 
 #define rsa_evp_type                    EVP_PKEY_RSA
-#define rsa_evp_extract                 (extract_key_fn *)EVP_PKEY_get1_RSA
 #define rsa_d2i_private_key             (d2i_of_void *)d2i_RSAPrivateKey
 #define rsa_d2i_public_key              (d2i_of_void *)d2i_RSAPublicKey
 #define rsa_d2i_key_params              NULL
+
+static void *rsa_d2i_PKCS8(void **key, const unsigned char **der, long der_len,
+                           struct der2key_ctx_st *ctx,
+                           OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    return der2key_decode_p8(der, der_len, ctx, pw_cb, pw_cbarg,
+                             (key_from_pkcs8_t *)ossl_rsa_key_from_pkcs8);
+}
+
+#define rsa_d2i_PUBKEY                  (d2i_of_void *)d2i_RSA_PUBKEY
 #define rsa_free                        (free_key_fn *)RSA_free
 
+static int rsa_check(void *key, struct der2key_ctx_st *ctx)
+{
+    switch (RSA_test_flags(key, RSA_FLAG_TYPE_MASK)) {
+    case RSA_FLAG_TYPE_RSA:
+        return ctx->desc->evp_type == EVP_PKEY_RSA;
+    case RSA_FLAG_TYPE_RSASSAPSS:
+        return ctx->desc->evp_type == EVP_PKEY_RSA_PSS;
+    }
+
+    /* Currently unsupported RSA key type */
+    return 0;
+}
+
 static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
 {
     ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
 }
 
 #define rsapss_evp_type                 EVP_PKEY_RSA_PSS
-#define rsapss_evp_extract              (extract_key_fn *)EVP_PKEY_get1_RSA
 #define rsapss_d2i_private_key          (d2i_of_void *)d2i_RSAPrivateKey
 #define rsapss_d2i_public_key           (d2i_of_void *)d2i_RSAPublicKey
 #define rsapss_d2i_key_params           NULL
+#define rsapss_d2i_PKCS8                rsa_d2i_PKCS8
+#define rsapss_d2i_PUBKEY               (d2i_of_void *)d2i_RSA_PUBKEY
 #define rsapss_free                     (free_key_fn *)RSA_free
+#define rsapss_check                    rsa_check
 #define rsapss_adjust                   rsa_adjust
 
 /* ---------------------------------------------------------------------- */
@@ -553,63 +619,75 @@ static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
  * for each kind of object we want to decode.
  */
 #define DO_type_specific_keypair(keytype)               \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_KEYPAIR ),                \
         keytype##_d2i_private_key,                      \
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_pub(keytype)                   \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ),             \
         NULL,                                           \
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_priv(keytype)                  \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ),            \
         keytype##_d2i_private_key,                      \
         NULL,                                           \
         NULL,                                           \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_params(keytype)                \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
         NULL,                                           \
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific(keytype)                       \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_ALL ),                    \
         keytype##_d2i_private_key,                      \
         keytype##_d2i_public_key,                       \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_type_specific_no_pub(keytype)                \
-    "type-specific", 0,                                 \
+    "type-specific", keytype##_evp_type,                \
         ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY               \
           | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),       \
         keytype##_d2i_private_key,                      \
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
@@ -619,7 +697,9 @@ static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
         NULL,                                           \
         NULL,                                           \
         NULL,                                           \
-        keytype##_evp_extract,                          \
+        keytype##_d2i_PKCS8,                            \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
@@ -629,58 +709,70 @@ static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
         NULL,                                           \
         NULL,                                           \
         NULL,                                           \
-        keytype##_evp_extract,                          \
+        NULL,                                           \
+        keytype##_d2i_PUBKEY,                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DH(keytype)                                  \
-    "DH", 0,                                            \
+    "DH", keytype##_evp_type,                           \
         ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
         NULL,                                           \
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DHX(keytype)                                 \
-    "DHX", 0,                                           \
+    "DHX", keytype##_evp_type,                          \
         ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),         \
         NULL,                                           \
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_DSA(keytype)                                 \
-    "DSA", 0,                                           \
+    "DSA", keytype##_evp_type,                          \
         ( OSSL_KEYMGMT_SELECT_ALL ),                    \
         keytype##_d2i_private_key,                      \
         keytype##_d2i_public_key,                       \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_EC(keytype)                                  \
-    "EC", 0,                                            \
+    "EC", keytype##_evp_type,                           \
         ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY               \
           | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ),       \
         keytype##_d2i_private_key,                      \
         NULL,                                           \
         keytype##_d2i_key_params,                       \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
 #define DO_RSA(keytype)                                 \
-    "RSA", 0,                                           \
+    "RSA", keytype##_evp_type,                          \
         ( OSSL_KEYMGMT_SELECT_KEYPAIR ),                \
         keytype##_d2i_private_key,                      \
         keytype##_d2i_public_key,                       \
         NULL,                                           \
         NULL,                                           \
+        NULL,                                           \
+        keytype##_check,                                \
         keytype##_adjust,                               \
         keytype##_free
 
diff --git a/providers/implementations/encode_decode/decode_ms2key.c b/providers/implementations/encode_decode/decode_ms2key.c
deleted file mode 100644
index f38717cbb3..0000000000
--- a/providers/implementations/encode_decode/decode_ms2key.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2020-2021 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
- */
-
-/*
- * low level APIs are deprecated for public use, but still ok for
- * internal use.
- */
-#include "internal/deprecated.h"
-
-#include <string.h>
-
-#include <openssl/core_dispatch.h>
-#include <openssl/core_names.h>
-#include <openssl/core_object.h>
-#include <openssl/crypto.h>
-#include <openssl/params.h>
-#include <openssl/pem.h>         /* For public PVK functions */
-#include <openssl/x509.h>
-#include "internal/passphrase.h"
-#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
-#include "prov/bio.h"
-#include "prov/implementations.h"
-#include "endecoder_local.h"
-
-static EVP_PKEY *read_msblob(PROV_CTX *provctx, OSSL_CORE_BIO *cin, int *ispub)
-{
-    BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
-    EVP_PKEY *pkey = ossl_b2i_bio(in, ispub);
-
-    BIO_free(in);
-    return pkey;
-}
-
-static EVP_PKEY *read_pvk(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
-                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    BIO *in = NULL;
-    EVP_PKEY *pkey = NULL;
-    struct ossl_passphrase_data_st pwdata;
-
-    memset(&pwdata, 0, sizeof(pwdata));
-    if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
-        return NULL;
-
-    in = ossl_bio_new_from_core_bio(provctx, cin);
-    pkey = b2i_PVK_bio(in, ossl_pw_pem_password, &pwdata);
-    BIO_free(in);
-
-    return pkey;
-}
-
-static OSSL_FUNC_decoder_freectx_fn ms2key_freectx;
-static OSSL_FUNC_decoder_gettable_params_fn ms2key_gettable_params;
-static OSSL_FUNC_decoder_get_params_fn msblob2key_get_params;
-static OSSL_FUNC_decoder_get_params_fn pvk2key_get_params;
-static OSSL_FUNC_decoder_decode_fn msblob2key_decode;
-static OSSL_FUNC_decoder_decode_fn pvk2key_decode;
-static OSSL_FUNC_decoder_export_object_fn ms2key_export_object;
-
-typedef void *(extract_key_fn)(EVP_PKEY *);
-typedef void (free_key_fn)(void *);
-struct keytype_desc_st {
-    int type;                 /* EVP key type */
-    const char *name;         /* Keytype */
-    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
-
-    /*
-     * These must be the correct EVP_PKEY_get1_{TYPE}() and {TYPE}_free()
-     * function for the key.
-     */
-    extract_key_fn *extract_key;
-    free_key_fn *free_key;
-};
-
-/*
- * Context used for DER to key decoding.
- */
-struct ms2key_ctx_st {
-    PROV_CTX *provctx;
-    const struct keytype_desc_st *desc;
-};
-
-static struct ms2key_ctx_st *
-ms2key_newctx(void *provctx, const struct keytype_desc_st *desc)
-{
-    struct ms2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
-
-    if (ctx != NULL) {
-        ctx->provctx = provctx;
-        ctx->desc = desc;
-    }
-    return ctx;
-}
-
-static void ms2key_freectx(void *vctx)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-
-    OPENSSL_free(ctx);
-}
-
-static const OSSL_PARAM *ms2key_gettable_params(ossl_unused void *provctx)
-{
-    static const OSSL_PARAM gettables[] = {
-        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
-        OSSL_PARAM_END,
-    };
-
-    return gettables;
-}
-
-static int msblob2key_get_params(OSSL_PARAM params[])
-{
-    OSSL_PARAM *p;
-
-    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
-    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "MSBLOB"))
-        return 0;
-
-    return 1;
-}
-
-static int pvk2key_get_params(OSSL_PARAM params[])
-{
-    OSSL_PARAM *p;
-
-    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
-    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "PVK"))
-        return 0;
-
-    return 1;
-}
-
-static int ms2key_post(struct ms2key_ctx_st *ctx, EVP_PKEY *pkey,
-                       OSSL_CALLBACK *data_cb, void *data_cbarg)
-{
-    void *key = NULL;
-    int ok = 0;
-
-    if (pkey != NULL) {
-        /*
-         * Tear out the low-level key pointer from the pkey,
-         * but only if it matches the expected key type.
-         *
-         * The check should be done with EVP_PKEY_is_a(), but
-         * as long as we still have #legacy internal keys, it's safer to
-         * use the type numbers in side the provider.
-         */
-        if (EVP_PKEY_id(pkey) == ctx->desc->type)
-            key = ctx->desc->extract_key(pkey);
-    }
-
-    if (key != NULL) {
-        OSSL_PARAM params[4];
-        int object_type = OSSL_OBJECT_PKEY;
-
-        params[0] =
-            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
-        params[1] =
-            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
-                                             (char *)ctx->desc->name, 0);
-        /* The address of the key becomes the octet string */
-        params[2] =
-            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
-                                              &key, sizeof(key));
-        params[3] = OSSL_PARAM_construct_end();
-
-        ok = data_cb(params, data_cbarg);
-    }
-    ctx->desc->free_key(key);
-
-    return ok;
-}
-
-static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
-                             OSSL_CALLBACK *data_cb, void *data_cbarg,
-                             OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    int ispub = -1;
-    EVP_PKEY *pkey = read_msblob(ctx->provctx, cin, &ispub);
-    int ok = 0;
-
-    if (selection == 0
-        || (ispub
-            ? (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0
-            : (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0))
-        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
-
-    EVP_PKEY_free(pkey);
-    return ok;
-}
-
-static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
-                          OSSL_CALLBACK *data_cb, void *data_cbarg,
-                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    EVP_PKEY *pkey = read_pvk(ctx->provctx, cin, pw_cb, pw_cbarg);
-    int ok = 0;
-
-    if (selection == 0
-        || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ms2key_post(ctx, pkey, data_cb, data_cbarg);
-
-    EVP_PKEY_free(pkey);
-    return ok;
-}
-
-static int ms2key_export_object(void *vctx,
-                                const void *reference, size_t reference_sz,
-                                OSSL_CALLBACK *export_cb, void *export_cbarg)
-{
-    struct ms2key_ctx_st *ctx = vctx;
-    OSSL_FUNC_keymgmt_export_fn *export =
-        ossl_prov_get_keymgmt_export(ctx->desc->fns);
-    void *keydata;
-
-    if (reference_sz == sizeof(keydata) && export != NULL) {
-        /* The contents of the reference is the address to our object */
-        keydata = *(void **)reference;
-
-        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
-                      export_cb, export_cbarg);
-    }
-    return 0;
-}
-
-#define IMPLEMENT_TYPE(KEYTYPEstr, KEYTYPE, keytype, extract, free)     \
-    static const struct keytype_desc_st keytype##_desc;                 \
-    static OSSL_FUNC_decoder_newctx_fn ms2##keytype##_newctx;           \
-    static void *ms2##keytype##_newctx(void *provctx)                   \
-    {                                                                   \
-        return ms2key_newctx(provctx, &keytype##_desc);                 \
-    }                                                                   \
-    static const struct keytype_desc_st keytype##_desc =                \
-        { EVP_PKEY_##KEYTYPE, KEYTYPEstr,                               \
-          ossl_##keytype##_keymgmt_functions,                           \
-          (extract_key_fn *)extract,                                    \
-          (free_key_fn *)free }
-
-#define IMPLEMENT_MS(mstype, keytype)                                   \
-    const OSSL_DISPATCH                                                 \
-        ossl_##mstype##_to_##keytype##_decoder_functions[] = {          \
-        { OSSL_FUNC_DECODER_NEWCTX,                                     \
-          (void (*)(void))ms2##keytype##_newctx },                      \
-        { OSSL_FUNC_DECODER_FREECTX,                                    \
-          (void (*)(void))ms2key_freectx },                             \
-        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
-          (void (*)(void))ms2key_gettable_params },                     \
-        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
-          (void (*)(void))mstype##2key_get_params },                    \
-        { OSSL_FUNC_DECODER_DECODE,                                     \
-          (void (*)(void))mstype##2key_decode },                        \
-        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
-          (void (*)(void))ms2key_export_object },                       \
-        { 0, NULL }                                                     \
-    }
-
-#ifndef OPENSSL_NO_DSA
-IMPLEMENT_TYPE("DSA", DSA, dsa, EVP_PKEY_get1_DSA, DSA_free);
-IMPLEMENT_MS(msblob, dsa);
-IMPLEMENT_MS(pvk, dsa);
-#endif
-IMPLEMENT_TYPE("RSA", RSA, rsa, EVP_PKEY_get1_RSA, RSA_free);
-IMPLEMENT_MS(msblob, rsa);
-IMPLEMENT_MS(pvk, rsa);
diff --git a/providers/implementations/encode_decode/decode_msblob2key.c b/providers/implementations/encode_decode/decode_msblob2key.c
new file mode 100644
index 0000000000..f47d06f59d
--- /dev/null
+++ b/providers/implementations/encode_decode/decode_msblob2key.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2020-2021 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
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>         /* For public PVK functions */
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include "internal/passphrase.h"
+#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct msblob2key_ctx_st;            /* Forward declaration */
+typedef void *b2i_of_void_fn(const unsigned char **in, unsigned int bitlen,
+                             int ispub);
+typedef void adjust_key_fn(void *, struct msblob2key_ctx_st *ctx);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+    int type;                 /* EVP key type */
+    const char *name;         /* Keytype */
+    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+    b2i_of_void_fn *read_private_key;
+    b2i_of_void_fn *read_public_key;
+    adjust_key_fn *adjust_key;
+    free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn msblob2key_freectx;
+static OSSL_FUNC_decoder_gettable_params_fn msblob2key_gettable_params;
+static OSSL_FUNC_decoder_get_params_fn msblob2key_get_params;
+static OSSL_FUNC_decoder_decode_fn msblob2key_decode;
+static OSSL_FUNC_decoder_export_object_fn msblob2key_export_object;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct msblob2key_ctx_st {
+    PROV_CTX *provctx;
+    const struct keytype_desc_st *desc;
+};
+
+static struct msblob2key_ctx_st *
+msblob2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+    struct msblob2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+        ctx->desc = desc;
+    }
+    return ctx;
+}
+
+static void msblob2key_freectx(void *vctx)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *msblob2key_gettable_params(ossl_unused void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int msblob2key_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "MSBLOB"))
+        return 0;
+
+    return 1;
+}
+
+static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+                             OSSL_CALLBACK *data_cb, void *data_cbarg,
+                             OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+    BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+    const unsigned char *p;
+    unsigned char hdr_buf[16], *buf = NULL;
+    unsigned int bitlen, magic, length;
+    int isdss = -1;
+    int ispub = -1;
+    void *key = NULL;
+    int ok = 0;
+
+    if (BIO_read(in, hdr_buf, 16) != 16) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+        goto err;
+    }
+    p = hdr_buf;
+    if (ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) <= 0)
+        goto err;
+
+    if ((isdss && ctx->desc->type != EVP_PKEY_DSA)
+        || (!isdss && ctx->desc->type != EVP_PKEY_RSA))
+        goto err;
+
+    length = ossl_blob_length(bitlen, isdss, ispub);
+    if (length > BLOB_MAX_LENGTH) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG);
+        goto err;
+    }
+    buf = OPENSSL_malloc(length);
+    if (buf == NULL) {
+        ERR_raise(ERR_LIB_PEM, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+    p = buf;
+    if (BIO_read(in, buf, length) != (int)length) {
+        ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+        goto err;
+    }
+
+    if ((selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        && !ispub
+        && ctx->desc->read_private_key != NULL) {
+        struct ossl_passphrase_data_st pwdata;
+
+        memset(&pwdata, 0, sizeof(pwdata));
+        if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+            goto err;
+        p = buf;
+        key = ctx->desc->read_private_key(&p, bitlen, ispub);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+    if (key == NULL && (selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        && ispub
+        && ctx->desc->read_public_key != NULL) {
+        p = buf;
+        key = ctx->desc->read_public_key(&p, bitlen, ispub);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+
+    if (key != NULL && ctx->desc->adjust_key != NULL)
+        ctx->desc->adjust_key(key, ctx);
+
+ next:
+    /*
+     * We free resources here so it's not held up during the callback, because
+     * we know the process is recursive and the allocated chunks of memory
+     * add up.
+     */
+    OPENSSL_free(buf);
+    BIO_free(in);
+    buf = NULL;
+    in = NULL;
+
+    if (key != NULL) {
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
+
+        params[0] =
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                             (char *)ctx->desc->name, 0);
+        /* The address of the key becomes the octet string */
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+                                              &key, sizeof(key));
+        params[3] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+
+ err:
+    BIO_free(in);
+    OPENSSL_free(buf);
+    ctx->desc->free_key(key);
+
+    return ok;
+}
+
+static int
+msblob2key_export_object(void *vctx,
+                         const void *reference, size_t reference_sz,
+                         OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    struct msblob2key_ctx_st *ctx = vctx;
+    OSSL_FUNC_keymgmt_export_fn *export =
+        ossl_prov_get_keymgmt_export(ctx->desc->fns);
+    void *keydata;
+
+    if (reference_sz == sizeof(keydata) && export != NULL) {
+        /* The contents of the reference is the address to our object */
+        keydata = *(void **)reference;
+
+        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
+                      export_cb, export_cbarg);
+    }
+    return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_decode_private_key  (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_decode_public_key   (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_adjust              NULL
+#define dsa_free                (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_decode_private_key  (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+#define rsa_decode_public_key   (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+
+static void rsa_adjust(void *key, struct msblob2key_ctx_st *ctx)
+{
+    ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free                        (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MSBLOB(KEYTYPE, keytype)                              \
+    static const struct keytype_desc_st mstype##2##keytype##_desc = {   \
+        EVP_PKEY_##KEYTYPE, #KEYTYPE,                                   \
+        ossl_##keytype##_keymgmt_functions,                             \
+        keytype##_decode_private_key,                                   \
+        keytype##_decode_public_key,                                    \
+        keytype##_adjust,                                               \
+        keytype##_free                                                  \
+    };                                                                  \
+    static OSSL_FUNC_decoder_newctx_fn msblob2##keytype##_newctx;       \
+    static void *msblob2##keytype##_newctx(void *provctx)               \
+    {                                                                   \
+        return msblob2key_newctx(provctx, &mstype##2##keytype##_desc);  \
+    }                                                                   \
+    const OSSL_DISPATCH                                                 \
+    ossl_msblob_to_##keytype##_decoder_functions[] = {                  \
+        { OSSL_FUNC_DECODER_NEWCTX,                                     \
+          (void (*)(void))msblob2##keytype##_newctx },                  \
+        { OSSL_FUNC_DECODER_FREECTX,                                    \
+          (void (*)(void))msblob2key_freectx },                         \
+        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
+          (void (*)(void))msblob2key_gettable_params },                 \
+        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
+          (void (*)(void))msblob2key_get_params },                      \
+        { OSSL_FUNC_DECODER_DECODE,                                     \
+          (void (*)(void))msblob2key_decode },                          \
+        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
+          (void (*)(void))msblob2key_export_object },                   \
+        { 0, NULL }                                                     \
+    }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MSBLOB(DSA, dsa);
+#endif
+IMPLEMENT_MSBLOB(RSA, rsa);
diff --git a/providers/implementations/encode_decode/decode_pvk2key.c b/providers/implementations/encode_decode/decode_pvk2key.c
new file mode 100644
index 0000000000..3f2c80abdc
--- /dev/null
+++ b/providers/implementations/encode_decode/decode_pvk2key.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2020-2021 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
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>         /* For public PVK functions */
+#include <openssl/x509.h>
+#include "internal/passphrase.h"
+#include "crypto/pem.h"          /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct pvk2key_ctx_st;            /* Forward declaration */
+typedef int check_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void adjust_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void *b2i_PVK_of_bio_pw_fn(BIO *in, pem_password_cb *cb, void *u);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+    int type;                 /* EVP key type */
+    const char *name;         /* Keytype */
+    const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+    b2i_PVK_of_bio_pw_fn *read_private_key;
+    adjust_key_fn *adjust_key;
+    free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn pvk2key_freectx;
+static OSSL_FUNC_decoder_gettable_params_fn pvk2key_gettable_params;
+static OSSL_FUNC_decoder_get_params_fn pvk2key_get_params;
+static OSSL_FUNC_decoder_decode_fn pvk2key_decode;
+static OSSL_FUNC_decoder_export_object_fn pvk2key_export_object;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct pvk2key_ctx_st {
+    PROV_CTX *provctx;
+    const struct keytype_desc_st *desc;
+};
+
+static struct pvk2key_ctx_st *
+pvk2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+    struct pvk2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+        ctx->desc = desc;
+    }
+    return ctx;
+}
+
+static void pvk2key_freectx(void *vctx)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *pvk2key_gettable_params(ossl_unused void *provctx)
+{
+    static const OSSL_PARAM gettables[] = {
+        { OSSL_DECODER_PARAM_INPUT_TYPE, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+        OSSL_PARAM_END,
+    };
+
+    return gettables;
+}
+
+static int pvk2key_get_params(OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_DECODER_PARAM_INPUT_TYPE);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, "PVK"))
+        return 0;
+
+    return 1;
+}
+
+static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+                         OSSL_CALLBACK *data_cb, void *data_cbarg,
+                         OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+    BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+    void *key = NULL;
+    int ok = 0;
+
+    if ((selection == 0
+         || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        && ctx->desc->read_private_key != NULL) {
+        struct ossl_passphrase_data_st pwdata;
+
+        memset(&pwdata, 0, sizeof(pwdata));
+        if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+            goto end;
+        key = ctx->desc->read_private_key(in, ossl_pw_pem_password, &pwdata);
+        if (selection != 0 && key == NULL)
+            goto next;
+    }
+
+    if (key != NULL && ctx->desc->adjust_key != NULL)
+        ctx->desc->adjust_key(key, ctx);
+
+ next:
+    /*
+     * We free resources here so it's not held up during the callback, because
+     * we know the process is recursive and the allocated chunks of memory
+     * add up.
+     */
+    BIO_free(in);
+    in = NULL;
+
+    if (key != NULL) {
+        OSSL_PARAM params[4];
+        int object_type = OSSL_OBJECT_PKEY;
+
+        params[0] =
+            OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+        params[1] =
+            OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                             (char *)ctx->desc->name, 0);
+        /* The address of the key becomes the octet string */
+        params[2] =
+            OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+                                              &key, sizeof(key));
+        params[3] = OSSL_PARAM_construct_end();
+
+        ok = data_cb(params, data_cbarg);
+    }
+
+ end:
+    BIO_free(in);
+    ctx->desc->free_key(key);
+
+    return ok;
+}
+
+static int pvk2key_export_object(void *vctx,
+                                const void *reference, size_t reference_sz,
+                                OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+    struct pvk2key_ctx_st *ctx = vctx;
+    OSSL_FUNC_keymgmt_export_fn *export =
+        ossl_prov_get_keymgmt_export(ctx->desc->fns);
+    void *keydata;
+
+    if (reference_sz == sizeof(keydata) && export != NULL) {
+        /* The contents of the reference is the address to our object */
+        keydata = *(void **)reference;
+
+        return export(keydata, OSSL_KEYMGMT_SELECT_ALL,
+                      export_cb, export_cbarg);
+    }
+    return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_private_key_bio     (b2i_PVK_of_bio_pw_fn *)b2i_DSA_PVK_bio
+#define dsa_adjust              NULL
+#define dsa_free                (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_private_key_bio     (b2i_PVK_of_bio_pw_fn *)b2i_RSA_PVK_bio
+
+static void rsa_adjust(void *key, struct pvk2key_ctx_st *ctx)
+{
+    ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free                (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MS(KEYTYPE, keytype)                                  \
+    static const struct keytype_desc_st                                 \
+    pvk2##keytype##_desc = {                                            \
+        EVP_PKEY_##KEYTYPE, #KEYTYPE,                                   \
+        ossl_##keytype##_keymgmt_functions,                             \
+        keytype##_private_key_bio,                                      \
+        keytype##_adjust,                                               \
+        keytype##_free                                                  \
+    };                                                                  \
+    static OSSL_FUNC_decoder_newctx_fn pvk2##keytype##_newctx;          \
+    static void *pvk2##keytype##_newctx(void *provctx)                  \
+    {                                                                   \
+        return pvk2key_newctx(provctx, &pvk2##keytype##_desc);          \
+    }                                                                   \
+    const OSSL_DISPATCH                                                 \
+    ossl_##pvk_to_##keytype##_decoder_functions[] = {                   \
+        { OSSL_FUNC_DECODER_NEWCTX,                                     \
+          (void (*)(void))pvk2##keytype##_newctx },                     \
+        { OSSL_FUNC_DECODER_FREECTX,                                    \
+          (void (*)(void))pvk2key_freectx },                            \
+        { OSSL_FUNC_DECODER_GETTABLE_PARAMS,                            \
+          (void (*)(void))pvk2key_gettable_params },                    \
+        { OSSL_FUNC_DECODER_GET_PARAMS,                                 \
+          (void (*)(void))pvk2key_get_params },                         \
+        { OSSL_FUNC_DECODER_DECODE,                                     \
+          (void (*)(void))pvk2key_decode },                             \
+        { OSSL_FUNC_DECODER_EXPORT_OBJECT,                              \
+          (void (*)(void))pvk2key_export_object },                      \
+        { 0, NULL }                                                     \
+    }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MS(DSA, dsa);
+#endif
+IMPLEMENT_MS(RSA, rsa);
diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c
index 1c4fb3bcd5..394f3836dd 100644
--- a/providers/implementations/keymgmt/rsa_kmgmt.c
+++ b/providers/implementations/keymgmt/rsa_kmgmt.c
@@ -36,6 +36,7 @@ static OSSL_FUNC_keymgmt_gen_settable_params_fn rsapss_gen_settable_params;
 static OSSL_FUNC_keymgmt_gen_fn rsa_gen;
 static OSSL_FUNC_keymgmt_gen_cleanup_fn rsa_gen_cleanup;
 static OSSL_FUNC_keymgmt_load_fn rsa_load;
+static OSSL_FUNC_keymgmt_load_fn rsapss_load;
 static OSSL_FUNC_keymgmt_free_fn rsa_freedata;
 static OSSL_FUNC_keymgmt_get_params_fn rsa_get_params;
 static OSSL_FUNC_keymgmt_gettable_params_fn rsa_gettable_params;
@@ -610,13 +611,18 @@ static void rsa_gen_cleanup(void *genctx)
     OPENSSL_free(gctx);
 }
 
-void *rsa_load(const void *reference, size_t reference_sz)
+static void *common_load(const void *reference, size_t reference_sz,
+                         int expected_rsa_type)
 {
     RSA *rsa = NULL;
 
     if (ossl_prov_is_running() && reference_sz == sizeof(rsa)) {
         /* The contents of the reference is the address to our object */
         rsa = *(RSA **)reference;
+
+        if (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK) != expected_rsa_type)
+            return NULL;
+
         /* We grabbed, so we detach it */
         *(RSA **)reference = NULL;
         return rsa;
@@ -624,6 +630,16 @@ void *rsa_load(const void *reference, size_t reference_sz)
     return NULL;
 }
 
+static void *rsa_load(const void *reference, size_t reference_sz)
+{
+    return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSA);
+}
+
+static void *rsapss_load(const void *reference, size_t reference_sz)
+{
+    return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSASSAPSS);
+}
+
 /* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */
 static const char *rsa_query_operation_name(int operation_id)
 {
@@ -661,7 +677,7 @@ const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[] = {
       (void (*)(void))rsapss_gen_settable_params },
     { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen },
     { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup },
-    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsa_load },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsapss_load },
     { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata },
     { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params },
     { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params },
diff --git a/providers/implementations/storemgmt/file_store_der2obj.c b/providers/implementations/storemgmt/file_store_der2obj.c
index 74fa40df9f..a36e9ab003 100644
--- a/providers/implementations/storemgmt/file_store_der2obj.c
+++ b/providers/implementations/storemgmt/file_store_der2obj.c
@@ -101,7 +101,9 @@ static int der2obj_decode(void *provctx, OSSL_CORE_BIO *cin, int selection,
     err = ERR_peek_last_error();
     if (ERR_GET_LIB(err) == ERR_LIB_ASN1
             && (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG
-                || ERR_GET_REASON(err) == ERR_R_NESTED_ASN1_ERROR))
+                || ERR_GET_REASON(err) == ASN1_R_UNSUPPORTED_TYPE
+                || ERR_GET_REASON(err) == ERR_R_NESTED_ASN1_ERROR
+                || ERR_GET_REASON(err) == ASN1_R_NOT_ENOUGH_DATA))
         ERR_pop_to_mark();
     else
         ERR_clear_last_mark();
diff --git a/test/endecode_test.c b/test/endecode_test.c
index c3a86e38d5..6f69172623 100644
--- a/test/endecode_test.c
+++ b/test/endecode_test.c
@@ -112,8 +112,8 @@ typedef int (encoder)(void **encoded, long *encoded_len,
                       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,
-                      int selection, const char *pass);
+                      const char *input_type, const char *structure_type,
+                      const char *keytype, int selection, const char *pass);
 typedef int (tester)(const void *data1, size_t data1_len,
                      const void *data2, size_t data2_len);
 typedef int (checker)(const char *type, const void *data, size_t data_len);
@@ -145,8 +145,9 @@ static int test_encode_decode(const char *type, EVP_PKEY *pkey,
                              output_type, output_structure, pass, pcipher))
         || !TEST_true(check_cb(type, encoded, encoded_len))
         || !TEST_true(decode_cb((void **)&pkey2, encoded, encoded_len,
+                                output_type, output_structure,
                                 (flags & FLAG_DECODE_WITH_TYPE ? type : NULL),
-                                output_type, selection, pass))
+                                selection, pass))
         || !TEST_true(encode_cb(&encoded2, &encoded2_len, pkey2, selection,
                                 output_type, output_structure, pass, pcipher)))
         goto end;
@@ -226,8 +227,10 @@ static int encode_EVP_PKEY_prov(void **encoded, long *encoded_len,
 }
 
 static int decode_EVP_PKEY_prov(void **object, void *encoded, long encoded_len,
-                                const char *keytype, const char *input_type,
-                                int selection, const char *pass)
+                                const char *input_type,
+                                const char *structure_type,
+                                const char *keytype, int selection,
+                                const char *pass)
 {
     EVP_PKEY *pkey = NULL, *testpkey = NULL;
     OSSL_DECODER_CTX *dctx = NULL;
@@ -258,7 +261,7 @@ static int decode_EVP_PKEY_prov(void **object, void *encoded, long encoded_len,
 
         if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_for_pkey(&testpkey,
                                                            testtype,
-                                                           NULL,
+                                                           structure_type,
                                                            keytype,
                                                            selection,
                                                            NULL, NULL))
diff --git a/test/recipes/30-test_evp.t b/test/recipes/30-test_evp.t
index fde0f658ee..6a1bf664a4 100644
--- a/test/recipes/30-test_evp.t
+++ b/test/recipes/30-test_evp.t
@@ -130,19 +130,29 @@ foreach my $f ( @defltfiles ) {
        "running evp_test -config $conf $f");
 }
 
+# test_errors OPTIONS
+#
+# OPTIONS may include:
+#
+# key      => "filename"        # expected to be found in $SRCDIR/test/certs
+# out      => "filename"        # file to write error strings to
+# args     => [ ... extra openssl pkey args ... ]
+# expected => regexps to match error lines against
 sub test_errors { # actually tests diagnostics of OSSL_STORE
-    my ($expected, $key, @opts) = @_;
-    my $infile = srctop_file('test', 'certs', $key);
-    my @args = qw(openssl pkey -in);
-    push(@args, $infile, @opts);
-    my $tmpfile = 'out.txt';
-    my $res = !run(app([@args], stderr => $tmpfile));
-    my $found = 0;
-    open(my $in, '<', $tmpfile) or die "Could not open file $tmpfile";
-    while(<$in>) {
-        print; # this may help debugging
-        $res &&= !m/asn1 encoding/; # output must not include ASN.1 parse errors
-        $found = 1 if m/$expected/; # output must include $expected
+    my %opts = @_;
+    my $infile = srctop_file('test', 'certs', $opts{key});
+    my @args = ( qw(openssl pkey -in), $infile, @{$opts{args} // []} );
+    my $res = !run(app([@args], stderr => $opts{out}));
+    my $found = !exists $opts{expected};
+    open(my $in, '<', $opts{out}) or die "Could not open file $opts{out}";
+    while(my $errline = <$in>) {
+        print $errline; # this may help debugging
+
+        # output must not include ASN.1 parse errors
+        $res &&= $errline !~ m/asn1 encoding/;
+        # output must include what is expressed in $opts{$expected}
+        $found = 1
+            if exists $opts{expected} && $errline =~ m/$opts{expected}/;
     }
     close $in;
     # $tmpfile is kept to help with investigation in case of failure
@@ -152,15 +162,19 @@ sub test_errors { # actually tests diagnostics of OSSL_STORE
 SKIP: {
     skip "DSA not disabled", 2 if !disabled("dsa");
 
-    ok(test_errors("unsupported algorithm", "server-dsa-key.pem"),
-       "error loading unsupported dsa private key");
-    ok(test_errors("unsupported algorithm", "server-dsa-pubkey.pem", "-pubin"),
-       "error loading unsupported dsa public key");
+    ok(test_errors(key => 'server-dsa-key.pem',
+                   out => 'server-dsa-key.err'),
+       "expected error loading unsupported dsa private key");
+    ok(test_errors(key => 'server-dsa-pubkey.pem',
+                   out => 'server-dsa-pubkey.err',
+                   args => [ '-pubin' ],
+                   expected => 'unsupported algorithm'),
+       "expected error loading unsupported dsa public key");
 }
 
 SKIP: {
-    skip "sm2 not disabled", 1 if !disabled("sm2");
+    skip "SM2 not disabled", 1 if !disabled("sm2");
 
-    ok(test_errors("unknown group|unsupported algorithm", "sm2.key"),
-       "error loading unsupported sm2 private key");
+    ok(test_errors(key => 'sm2.key', out => 'sm2.err'),
+       "expected error loading unsupported sm2 private key");
 }


More information about the openssl-commits mailing list