[openssl] master update

Richard Levitte levitte at openssl.org
Fri Nov 29 20:01:30 UTC 2019


The branch master has been updated
       via  14ee781eef0e55563432f377d8911529823bee58 (commit)
       via  31d3a7590274b48b194ed070ae531238764647f9 (commit)
       via  c48e2d106b8740f817b099310fb084375b743196 (commit)
       via  6ae5543c5f8476f4cae9c00209ef062746bbfd3c (commit)
       via  264b789bc240e6d0f642b274b6ab3cf8470e4aef (commit)
       via  045e51cbf49a0773f7d045180d9d8b8b73d70e90 (commit)
       via  677add38003b37cab06ebc59260c8d28745bb948 (commit)
       via  cb58d81e68c72ab0128e0a5fc6faa007f8632acd (commit)
       via  63665fff84a4c79cd2acece4409036699f2e44a7 (commit)
       via  54c1711f87cdc50e2266e2f0a063ccace08173d2 (commit)
       via  f864a9396a690958d26c87827ba6f26e7b010caf (commit)
       via  866234ac35e665f20c646059b1d92c5e9eb0c7ab (commit)
       via  1793d270f3c6f6b859e40ef1fa7bea1fd7e447c8 (commit)
       via  742496f1309d04f4921ca64e4b6315a45a47b4af (commit)
       via  0d003c52d3dcf4b076bb01a6767cdd5ace2d79f6 (commit)
       via  36fa4d8a0df9dc168047fadd0365966c7116b31d (commit)
       via  3d83c7353630450a4ce4fffc9c75693565e094c2 (commit)
      from  cc38e643cbfb6da84fb2bb6a188698d0bc082a20 (commit)


- Log -----------------------------------------------------------------
commit 14ee781eef0e55563432f377d8911529823bee58
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Nov 27 08:59:09 2019 +0100

    util/find-doc-nits: ignore macros ending in _fnsig
    
    These are helper macros alongside the IMPLEMENT_ and DECLARE_ macros.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 31d3a7590274b48b194ed070ae531238764647f9
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Nov 19 10:50:14 2019 +0100

    util/find-doc-nits: limit the prototype check
    
    The prototype checks shouldn't be performed on SYNOPSIS lines that
    aren't function prototypes.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit c48e2d106b8740f817b099310fb084375b743196
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 02:31:33 2019 +0100

    Add NEWS and CHANGES entries about OSSL_SERIALIZER
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 6ae5543c5f8476f4cae9c00209ef062746bbfd3c
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 02:30:50 2019 +0100

    TEST: add tests of text and PEM printout of a provider made key
    
    This renames test/evp_fromdata_test.c to test/evp_pkey_provided_test.c,
    to encourage additional testing of provider made keys.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 264b789bc240e6d0f642b274b6ab3cf8470e4aef
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 02:01:13 2019 +0100

    PROV SERIALIZER: add support for writing DSA keys and parameters
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 045e51cbf49a0773f7d045180d9d8b8b73d70e90
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:57:56 2019 +0100

    PROV SERIALIZER: add support for writing DH keys and parameters
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 677add38003b37cab06ebc59260c8d28745bb948
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:56:22 2019 +0100

    PROV SERIALIZER: add support for writing RSA keys
    
    This also adds the missing accessor RSA_get0_pss_params(), so those
    parameters can be included in the PKCS#8 data structure without
    needing to know the inside of the RSA structure.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit cb58d81e68c72ab0128e0a5fc6faa007f8632acd
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:54:11 2019 +0100

    PROV SERIALIZER: add common functionality to serialize keys
    
    To support generic output of public keys wrapped in a X509_PUBKEY,
    additional PEM and i2d/d2i routines are added for that type.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 63665fff84a4c79cd2acece4409036699f2e44a7
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:50:18 2019 +0100

    PROV BIO: add a BIO_vprintf() upcall, and a provider BIO library
    
    The BIO_vprintf() will allow the provider to print any text, given a
    BIO supplied by libcrypto.
    
    Additionally, we add a provider library with functions to collect all
    the currently supplied BIO upcalls, as well as wrappers around those
    upcalls.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 54c1711f87cdc50e2266e2f0a063ccace08173d2
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:47:32 2019 +0100

    SERIALIZER: add hooks in EVP_PKEY_print_ routines
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit f864a9396a690958d26c87827ba6f26e7b010caf
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:44:23 2019 +0100

    SERIALIZER: add hooks in PEM_write_bio_ and PEM_write_fp_ routines
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 866234ac35e665f20c646059b1d92c5e9eb0c7ab
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:34:26 2019 +0100

    SERIALIZER: add support for serializing EVP_PKEYs
    
    The following public functions is added:
    
    - OSSL_SERIALIZER_CTX_new_by_EVP_PKEY()
    - OSSL_SERIALIZER_CTX_set_cipher()
    - OSSL_SERIALIZER_CTX_set_passphrase()
    - OSSL_SERIALIZER_CTX_set_passphrase_cb()
    - OSSL_SERIALIZER_CTX_set_passphrase_ui()
    
    OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() selects a suitable serializer
    for the given EVP_PKEY, and sets up the OSSL_SERIALIZER_CTX to
    function together with OSSL_SERIALIZER_to_bio() and
    OSSL_SERIALIZER_to_fp().
    
    OSSL_SERIALIZER_CTX_set_cipher() indicates what cipher should be used
    to produce an encrypted serialization of the EVP_PKEY.  This is passed
    directly to the provider using OSSL_SERIALIZER_CTX_set_params().
    
    OSSL_SERIALIZER_CTX_set_passphrase() can be used to set a pass phrase
    to be used for the encryption.  This is passed directly to the
    provider using OSSL_SERIALIZER_CTX_set_params().
    
    OSSL_SERIALIZER_CTX_set_passphrase_cb() and
    OSSL_SERIALIZER_CTX_set_passphrase_ui() sets up a callback to be used
    to prompt for a passphrase.  This is stored in the context, and is
    called via an internal intermediary at the time of serialization.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 1793d270f3c6f6b859e40ef1fa7bea1fd7e447c8
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Nov 19 14:17:04 2019 +0100

    CORE: expose the property parsers and checker to the rest of the libraries
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 742496f1309d04f4921ca64e4b6315a45a47b4af
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:32:22 2019 +0100

    SERIALIZER: add functions for serialization to file
    
    These functions are added:
    
    - OSSL_SERIALIZER_to_bio()
    - OSSL_SERIALIZER_to_fp() (unless 'no-stdio')
    
    OSSL_SERIALIZER_to_bio() and OSSL_SERIALIZER_to_fp() work as wrapper
    functions, and call an internal "do_output" function with the given
    serializer context and a BIO to output the serialized result to.
    
    The internal "do_output" function must have intimate knowledge of the
    object being output.  This will defined independently with context
    creators for specific OpenSSL types.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 0d003c52d3dcf4b076bb01a6767cdd5ace2d79f6
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Nov 18 01:29:06 2019 +0100

    SERIALIZER: New API for serialization of objects through providers
    
    Serialization is needed to be able to take a provider object (such as
    the provider side key data) and output it in PEM form, DER form, text
    form (for display), and possibly other future forms (XML? JSON? JWK?)
    
    The idea is that a serializer should be able to handle objects it has
    intimate knowledge of, as well as object data in OSSL_PARAM form.  The
    latter will allow libcrypto to serialize some object with a different
    provider than the one holding the data, if exporting of that data is
    allowed and there is a serializer that can handle it.
    
    We will provide serializers for the types of objects we know about,
    which should be useful together with any other provider that provides
    implementations of the same type of object.
    
    Serializers are selected by method name and a couple of additional
    properties:
    
    - format        used to tell what format the output should be in.
                    Possibilities could include "format=text",
                    "format=pem", "format=der", "format=pem-pkcs1"
                    (traditional), "format=der-pkcs1" (traditional)
    - type          used to tell exactly what type of data should be
                    output, for example "type=public" (the public part of
                    a key), "type=private" (the private part of a key),
                    "type=domainparams" (domain parameters).
    
    This also adds a passphrase callback function type,
    OSSL_PASSPHRASE_CALLBACK, which is a bit like OSSL_CALLBACK, but it
    takes a few extra arguments to place the result in.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 36fa4d8a0df9dc168047fadd0365966c7116b31d
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Nov 19 09:55:56 2019 +0100

    CORE: pass the full algorithm definition to the method constructor
    
    So far, the API level method constructors that are called by
    ossl_method_construct_this() were passed the algorithm name string and
    the dispatch table and had no access to anything else.
    
    This change gives them access to the full OSSL_ALGORITHM item, thereby
    giving them access to the property definition.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

commit 3d83c7353630450a4ce4fffc9c75693565e094c2
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Nov 9 00:18:05 2019 +0100

    CORE: ossl_namemap_add_names(): new function to add multiple names
    
    This was originally the private add_names_to_namemap() in
    crypto/evp/evp_fetch.c, but made more generally useful.
    
    To make for more consistent function naming, ossl_namemap_add() and
    ossl_namemap_add_n() are renamed to ossl_namemap_add_name() and
    ossl_namemap_add_name_n().
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/10394)

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

Summary of changes:
 CHANGES                                            |  18 +
 NEWS                                               |   1 +
 crypto/build.info                                  |   2 +-
 crypto/core_fetch.c                                |   5 +-
 crypto/core_namemap.c                              |  71 ++-
 crypto/cpt_err.c                                   |   4 +
 crypto/err/err.c                                   |   3 +
 crypto/err/openssl.ec                              |   1 +
 crypto/err/openssl.txt                             |   5 +
 crypto/evp/evp_fetch.c                             |  81 +---
 crypto/evp/p_lib.c                                 |  38 ++
 crypto/pem/pem_all.c                               |   5 +-
 crypto/pem/pem_local.h                             | 128 +++++
 crypto/pem/pem_pkey.c                              |  26 +-
 crypto/property/property_local.h                   |   9 -
 crypto/property/property_parse.c                   |   2 +
 crypto/provider_core.c                             |   1 +
 crypto/rsa/rsa_lib.c                               |   5 +
 crypto/serializer/build.info                       |   2 +
 crypto/serializer/serializer_err.c                 |  31 ++
 crypto/serializer/serializer_lib.c                 |  43 ++
 crypto/serializer/serializer_local.h               |  50 ++
 crypto/serializer/serializer_meth.c                | 516 +++++++++++++++++++++
 crypto/serializer/serializer_pkey.c                | 386 +++++++++++++++
 crypto/x509/x_all.c                                |  24 +
 doc/internal/man3/ossl_namemap_new.pod             |  31 +-
 doc/man3/OSSL_SERIALIZER.pod                       | 129 ++++++
 doc/man3/OSSL_SERIALIZER_CTX.pod                   |  94 ++++
 doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod   | 134 ++++++
 doc/man3/OSSL_SERIALIZER_to_bio.pod                |  59 +++
 doc/man3/PEM_read_CMS.pod                          |   6 +-
 doc/man3/RSA_get0_key.pod                          |   8 +-
 doc/man3/d2i_X509.pod                              |   4 +
 doc/man7/openssl-core.h.pod                        |  23 +-
 doc/man7/provider-base.pod                         |   5 +-
 doc/man7/provider-serializer.pod                   | 276 +++++++++++
 doc/man7/provider.pod                              |   8 +
 include/crypto/serializer.h                        |  13 +
 include/internal/core.h                            |   4 +-
 include/internal/cryptlib.h                        |   3 +-
 include/internal/namemap.h                         |  13 +-
 include/internal/property.h                        |   9 +
 include/openssl/core.h                             |  10 +
 include/openssl/core_names.h                       |  11 +
 include/openssl/core_numbers.h                     |  28 +-
 include/openssl/cryptoerr.h                        |   2 +
 include/openssl/err.h                              |   3 +
 include/openssl/pem.h                              |  58 ++-
 include/openssl/rsa.h                              |   5 +-
 include/openssl/serializer.h                       | 100 ++++
 include/openssl/serializererr.h                    |  34 ++
 include/openssl/types.h                            |   6 +
 include/openssl/x509.h                             |   4 +
 providers/common/bio_prov.c                        |  96 ++++
 providers/common/build.info                        |   2 +-
 providers/common/include/prov/bio.h                |  22 +
 providers/common/include/prov/providercommonerr.h  |   2 +
 providers/common/provider_err.c                    |   2 +
 providers/defltprov.c                              |  41 ++
 providers/implementations/build.info               |   3 +-
 .../implementations/include/prov/implementations.h |  26 ++
 providers/implementations/serializers/build.info   |  12 +
 .../serializers/serializer_common.c                | 339 ++++++++++++++
 .../implementations/serializers/serializer_dh.c    | 151 ++++++
 .../serializers/serializer_dh_param.c              | 135 ++++++
 .../serializers/serializer_dh_priv.c               | 250 ++++++++++
 .../serializers/serializer_dh_pub.c                | 140 ++++++
 .../implementations/serializers/serializer_dsa.c   | 152 ++++++
 .../serializers/serializer_dsa_param.c             | 135 ++++++
 .../serializers/serializer_dsa_priv.c              | 244 ++++++++++
 .../serializers/serializer_dsa_pub.c               | 151 ++++++
 .../implementations/serializers/serializer_local.h | 101 ++++
 .../implementations/serializers/serializer_rsa.c   | 103 ++++
 .../serializers/serializer_rsa_priv.c              | 279 +++++++++++
 .../serializers/serializer_rsa_pub.c               | 135 ++++++
 test/build.info                                    |   8 +-
 test/evp_fromdata_test.c                           |  87 ----
 test/evp_pkey_provided_test.c                      | 196 ++++++++
 test/namemap_internal_test.c                       |  16 +-
 ..._evp_fromdata.t => 30-test_evp_pkey_provided.t} |   2 +-
 util/find-doc-nits                                 |  18 +-
 util/libcrypto.num                                 |  31 ++
 util/missingcrypto.txt                             |   1 +
 util/other.syms                                    |   8 +
 84 files changed, 5165 insertions(+), 260 deletions(-)
 create mode 100644 crypto/pem/pem_local.h
 create mode 100644 crypto/serializer/build.info
 create mode 100644 crypto/serializer/serializer_err.c
 create mode 100644 crypto/serializer/serializer_lib.c
 create mode 100644 crypto/serializer/serializer_local.h
 create mode 100644 crypto/serializer/serializer_meth.c
 create mode 100644 crypto/serializer/serializer_pkey.c
 create mode 100644 doc/man3/OSSL_SERIALIZER.pod
 create mode 100644 doc/man3/OSSL_SERIALIZER_CTX.pod
 create mode 100644 doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
 create mode 100644 doc/man3/OSSL_SERIALIZER_to_bio.pod
 create mode 100644 doc/man7/provider-serializer.pod
 create mode 100644 include/crypto/serializer.h
 create mode 100644 include/openssl/serializer.h
 create mode 100644 include/openssl/serializererr.h
 create mode 100644 providers/common/bio_prov.c
 create mode 100644 providers/common/include/prov/bio.h
 create mode 100644 providers/implementations/serializers/build.info
 create mode 100644 providers/implementations/serializers/serializer_common.c
 create mode 100644 providers/implementations/serializers/serializer_dh.c
 create mode 100644 providers/implementations/serializers/serializer_dh_param.c
 create mode 100644 providers/implementations/serializers/serializer_dh_priv.c
 create mode 100644 providers/implementations/serializers/serializer_dh_pub.c
 create mode 100644 providers/implementations/serializers/serializer_dsa.c
 create mode 100644 providers/implementations/serializers/serializer_dsa_param.c
 create mode 100644 providers/implementations/serializers/serializer_dsa_priv.c
 create mode 100644 providers/implementations/serializers/serializer_dsa_pub.c
 create mode 100644 providers/implementations/serializers/serializer_local.h
 create mode 100644 providers/implementations/serializers/serializer_rsa.c
 create mode 100644 providers/implementations/serializers/serializer_rsa_priv.c
 create mode 100644 providers/implementations/serializers/serializer_rsa_pub.c
 delete mode 100644 test/evp_fromdata_test.c
 create mode 100644 test/evp_pkey_provided_test.c
 rename test/recipes/{30-test_evp_fromdata.t => 30-test_evp_pkey_provided.t} (87%)

diff --git a/CHANGES b/CHANGES
index fd11739193..208780e9c9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,24 @@
 
  Changes between 1.1.1 and 3.0.0 [xx XXX xxxx]
 
+  *) Introduced a new method type and API, OSSL_SERIALIZER, to
+     represent generic serializers.  An implementation is expected to
+     be able to serialize an object associated with a given name (such
+     as an algorithm name for an asymmetric key) into forms given by
+     implementation properties.
+
+     Serializers are primarily used from inside libcrypto, through
+     calls to functions like EVP_PKEY_print_private(),
+     PEM_write_bio_PrivateKey() and similar.
+
+     Serializers are specified in such a way that they can be made to
+     directly handle the provider side portion of an object, if this
+     provider side part comes from the same provider as the serializer
+     itself, but can also be made to handle objects in parametrized
+     form (as an OSSL_PARAM array of data).  This allows a provider to
+     offer generic serializers as a service for any other provider.
+     [Richard Levitte]
+
   *) Added a .pragma directive to the syntax of configuration files, to
      allow varying behavior in a supported and predictable manner.
      Currently added pragma:
diff --git a/NEWS b/NEWS
index 0463af6e25..7c11f61db7 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@
 
   Major changes between OpenSSL 1.1.1 and OpenSSL 3.0.0 [under development]
 
+      o Added OSSL_SERIALIZER, a generic serializer API.
       o Added error raising macros, ERR_raise() and ERR_raise_data().
       o Deprecated ERR_put_error().
       o Added OSSL_PROVIDER_available(), to check provider availibility.
diff --git a/crypto/build.info b/crypto/build.info
index 5f90a2eda2..5beaf528be 100644
--- a/crypto/build.info
+++ b/crypto/build.info
@@ -5,7 +5,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \
         md2 md4 md5 sha mdc2 hmac ripemd whrlpool poly1305 \
         siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
         seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
-        err comp ocsp cms ts srp cmac ct async ess crmf cmp
+        err comp ocsp cms ts srp cmac ct async ess crmf cmp serializer
 
 LIBS=../libcrypto
 
diff --git a/crypto/core_fetch.c b/crypto/core_fetch.c
index ed50bb87d5..7f815a50ac 100644
--- a/crypto/core_fetch.c
+++ b/crypto/core_fetch.c
@@ -31,9 +31,8 @@ static void ossl_method_construct_this(OSSL_PROVIDER *provider,
     struct construct_data_st *data = cbdata;
     void *method = NULL;
 
-    if ((method = data->mcm->construct(algo->algorithm_names,
-                                       algo->implementation, provider,
-                                       data->mcm_data)) == NULL)
+    if ((method = data->mcm->construct(algo, provider, data->mcm_data))
+        == NULL)
         return;
 
     /*
diff --git a/crypto/core_namemap.c b/crypto/core_namemap.c
index 9b19d60db3..39356076e5 100644
--- a/crypto/core_namemap.c
+++ b/crypto/core_namemap.c
@@ -224,8 +224,8 @@ const char *ossl_namemap_num2name(const OSSL_NAMEMAP *namemap, int number,
     return data.name;
 }
 
-int ossl_namemap_add_n(OSSL_NAMEMAP *namemap, int number,
-                       const char *name, size_t name_len)
+int ossl_namemap_add_name_n(OSSL_NAMEMAP *namemap, int number,
+                            const char *name, size_t name_len)
 {
     NAMENUM_ENTRY *namenum = NULL;
     int tmp_number;
@@ -265,10 +265,73 @@ int ossl_namemap_add_n(OSSL_NAMEMAP *namemap, int number,
     return 0;
 }
 
-int ossl_namemap_add(OSSL_NAMEMAP *namemap, int number, const char *name)
+int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, const char *name)
 {
     if (name == NULL)
         return 0;
 
-    return ossl_namemap_add_n(namemap, number, name, strlen(name));
+    return ossl_namemap_add_name_n(namemap, number, name, strlen(name));
+}
+
+int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number,
+                           const char *names, const char separator)
+{
+    const char *p, *q;
+    size_t l;
+
+    /* Check that we have a namemap */
+    if (!ossl_assert(namemap != NULL)) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    /*
+     * Check that no name is an empty string, and that all names have at
+     * most one numeric identity together.
+     */
+    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
+        int this_number;
+
+        if ((q = strchr(p, separator)) == NULL)
+            l = strlen(p);       /* offset to \0 */
+        else
+            l = q - p;           /* offset to the next separator */
+
+        this_number = ossl_namemap_name2num_n(namemap, p, l);
+
+        if (*p == '\0' || *p == separator) {
+            ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_BAD_ALGORITHM_NAME);
+            return 0;
+        }
+        if (number == 0) {
+            number = this_number;
+        } else if (this_number != 0 && this_number != number) {
+            ERR_raise_data(ERR_LIB_CRYPTO, CRYPTO_R_CONFLICTING_NAMES,
+                           "\"%.*s\" has an existing different identity %d (from \"%s\")",
+                           l, p, this_number, names);
+            return 0;
+        }
+    }
+
+    /* Now that we have checked, register all names */
+    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
+        int this_number;
+
+        if ((q = strchr(p, separator)) == NULL)
+            l = strlen(p);       /* offset to \0 */
+        else
+            l = q - p;           /* offset to the next separator */
+
+        this_number = ossl_namemap_add_name_n(namemap, number, p, l);
+        if (number == 0) {
+            number = this_number;
+        } else if (this_number != number) {
+            ERR_raise_data(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR,
+                           "Got number %d when expecting %d",
+                           this_number, number);
+            return 0;
+        }
+    }
+
+    return number;
 }
diff --git a/crypto/cpt_err.c b/crypto/cpt_err.c
index 0028b329a3..0201f31e61 100644
--- a/crypto/cpt_err.c
+++ b/crypto/cpt_err.c
@@ -14,6 +14,10 @@
 #ifndef OPENSSL_NO_ERR
 
 static const ERR_STRING_DATA CRYPTO_str_reasons[] = {
+    {ERR_PACK(ERR_LIB_CRYPTO, 0, CRYPTO_R_BAD_ALGORITHM_NAME),
+    "bad algorithm name"},
+    {ERR_PACK(ERR_LIB_CRYPTO, 0, CRYPTO_R_CONFLICTING_NAMES),
+    "conflicting names"},
     {ERR_PACK(ERR_LIB_CRYPTO, 0, CRYPTO_R_FIPS_MODE_NOT_SUPPORTED),
     "fips mode not supported"},
     {ERR_PACK(ERR_LIB_CRYPTO, 0, CRYPTO_R_ILLEGAL_HEX_DIGIT),
diff --git a/crypto/err/err.c b/crypto/err/err.c
index bc33a06b7e..e77cfe83cf 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -75,6 +75,7 @@ static ERR_STRING_DATA ERR_str_libraries[] = {
     {ERR_PACK(ERR_LIB_SM2, 0, 0), "SM2 routines"},
     {ERR_PACK(ERR_LIB_ESS, 0, 0), "ESS routines"},
     {ERR_PACK(ERR_LIB_PROV, 0, 0), "Provider routines"},
+    {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, 0), "SERIALIZER routines"},
     {0, NULL},
 };
 
@@ -111,6 +112,8 @@ static ERR_STRING_DATA ERR_str_reasons[] = {
     {ERR_R_DISABLED, "called a function that was disabled at compile-time"},
     {ERR_R_INIT_FAIL, "init fail"},
     {ERR_R_OPERATION_FAIL, "operation fail"},
+    {ERR_R_INVALID_PROVIDER_FUNCTIONS, "invalid provider functions"},
+    {ERR_R_INTERRUPTED_OR_CANCELLED, "interrupted or cancelled"},
 
     {0, NULL},
 };
diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec
index 211edd42f3..485c0c89ce 100644
--- a/crypto/err/openssl.ec
+++ b/crypto/err/openssl.ec
@@ -40,6 +40,7 @@ L OSSL_STORE    include/openssl/store.h         crypto/store/store_err.c
 L ESS           include/openssl/ess.h           crypto/ess/ess_err.c
 L PROP          include/internal/property.h     crypto/property/property_err.c
 L PROV          providers/common/include/prov/providercommon.h providers/common/provider_err.c
+L OSSL_SERIALIZER include/openssl/serializer.h  crypto/serializer/serializer_err.c
 
 # additional header files to be scanned for function names
 L NONE          include/openssl/x509_vfy.h      NONE
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 9c0f94413a..e81c32fe4f 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2206,6 +2206,8 @@ CRMF_R_UNSUPPORTED_METHOD_FOR_CREATING_POPO:115:\
 	unsupported method for creating popo
 CRMF_R_UNSUPPORTED_POPO_METHOD:116:unsupported popo method
 CRMF_R_UNSUPPORTED_POPO_NOT_ACCEPTED:117:unsupported popo not accepted
+CRYPTO_R_BAD_ALGORITHM_NAME:117:bad algorithm name
+CRYPTO_R_CONFLICTING_NAMES:118:conflicting names
 CRYPTO_R_FIPS_MODE_NOT_SUPPORTED:101:fips mode not supported
 CRYPTO_R_ILLEGAL_HEX_DIGIT:102:illegal hex digit
 CRYPTO_R_INSUFFICIENT_DATA_SPACE:106:insufficient data space
@@ -2548,6 +2550,7 @@ OCSP_R_STATUS_TOO_OLD:127:status too old
 OCSP_R_UNKNOWN_MESSAGE_DIGEST:119:unknown message digest
 OCSP_R_UNKNOWN_NID:120:unknown nid
 OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE:129:unsupported requestorname type
+OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY:100:incorrect property query
 OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE:107:ambiguous content type
 OSSL_STORE_R_BAD_PASSWORD_READ:115:bad password read
 OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC:113:error verifying pkcs12 mac
@@ -2681,6 +2684,7 @@ PROV_R_AES_KEY_SETUP_FAILED:101:aes key setup failed
 PROV_R_BAD_DECRYPT:100:bad decrypt
 PROV_R_BAD_ENCODING:141:bad encoding
 PROV_R_BAD_LENGTH:142:bad length
+PROV_R_BN_ERROR:160:bn error
 PROV_R_BOTH_MODE_AND_MODE_INT:127:both mode and mode int
 PROV_R_CIPHER_OPERATION_FAILED:102:cipher operation failed
 PROV_R_FAILED_TO_GENERATE_KEY:121:failed to generate key
@@ -2723,6 +2727,7 @@ PROV_R_NOT_SUPPORTED:136:not supported
 PROV_R_NOT_XOF_OR_INVALID_LENGTH:113:not xof or invalid length
 PROV_R_NO_KEY_SET:114:no key set
 PROV_R_OUTPUT_BUFFER_TOO_SMALL:106:output buffer too small
+PROV_R_READ_KEY:159:read key
 PROV_R_TAG_NOTSET:119:tag notset
 PROV_R_TAG_NOT_NEEDED:120:tag not needed
 PROV_R_UNABLE_TO_LOAD_SHA1:143:unable to load sha1
diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c
index 69ca6a0300..b2040e06f4 100644
--- a/crypto/evp/evp_fetch.c
+++ b/crypto/evp/evp_fetch.c
@@ -52,75 +52,12 @@ struct evp_method_data_st {
     void (*destruct_method)(void *method);
 };
 
-static int add_names_to_namemap(OSSL_NAMEMAP *namemap,
-                                const char *names)
-{
-    const char *p, *q;
-    size_t l;
-    int id = 0;
-
-    /* Check that we have a namemap and that there is at least one name */
-    if (namemap == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
-        return 0;
-    }
-
-    /*
-     * Check that no name is an empty string, and that all names have at
-     * most one numeric identity together.
-     */
-    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
-        int this_id;
-
-        if ((q = strchr(p, NAME_SEPARATOR)) == NULL)
-            l = strlen(p);       /* offset to \0 */
-        else
-            l = q - p;           /* offset to the next separator */
-
-        this_id = ossl_namemap_name2num_n(namemap, p, l);
-
-        if (*p == '\0' || *p == NAME_SEPARATOR) {
-            ERR_raise(ERR_LIB_EVP, EVP_R_BAD_ALGORITHM_NAME);
-            return 0;
-        }
-        if (id == 0)
-            id = this_id;
-        else if (this_id != 0 && this_id != id) {
-            ERR_raise_data(ERR_LIB_EVP, EVP_R_CONFLICTING_ALGORITHM_NAME,
-                           "\"%.*s\" has an existing different identity %d (from \"%s\")",
-                           l, p, this_id, names);
-            return 0;
-        }
-    }
-
-    /* Now that we have checked, register all names */
-    for (p = names; *p != '\0'; p = (q == NULL ? p + l : q + 1)) {
-        int this_id;
-
-        if ((q = strchr(p, NAME_SEPARATOR)) == NULL)
-            l = strlen(p);       /* offset to \0 */
-        else
-            l = q - p;           /* offset to the next separator */
-
-        this_id = ossl_namemap_add_n(namemap, id, p, l);
-        if (id == 0)
-            id = this_id;
-        else if (this_id != id) {
-            ERR_raise_data(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR,
-                           "Got id %d when expecting %d", this_id, id);
-            return 0;
-        }
-    }
-
-    return id;
-}
-
 #ifndef FIPS_MODE
 /* Creates an initial namemap with names found in the legacy method db */
 static void get_legacy_evp_names(const char *main_name, const char *alias,
                                  void *arg)
 {
-    int main_id = ossl_namemap_add(arg, 0, main_name);
+    int main_id = ossl_namemap_add_name(arg, 0, main_name);
 
     /*
      * We could check that the returned value is the same as main_id,
@@ -133,7 +70,7 @@ static void get_legacy_evp_names(const char *main_name, const char *alias,
      * simply a no-op.
      */
     if (alias != NULL)
-        (void)ossl_namemap_add(arg, main_id, alias);
+        (void)ossl_namemap_add_name(arg, main_id, alias);
 }
 
 static void get_legacy_cipher_names(const OBJ_NAME *on, void *arg)
@@ -296,23 +233,26 @@ static int put_evp_method_in_store(OPENSSL_CTX *libctx, void *store,
  * The core fetching functionality passes the name of the implementation.
  * This function is responsible to getting an identity number for it.
  */
-static void *construct_evp_method(const char *names, const OSSL_DISPATCH *fns,
+static void *construct_evp_method(const OSSL_ALGORITHM *algodef,
                                   OSSL_PROVIDER *prov, void *data)
 {
     /*
      * This function is only called if get_evp_method_from_store() returned
      * NULL, so it's safe to say that of all the spots to create a new
      * namemap entry, this is it.  Should the name already exist there, we
-     * know that ossl_namemap_add() will return its corresponding number.
+     * know that ossl_namemap_add_name() will return its corresponding
+     * number.
      */
     struct evp_method_data_st *methdata = data;
     OPENSSL_CTX *libctx = ossl_provider_library_context(prov);
     OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
-    int name_id = add_names_to_namemap(namemap, names);
+    const char *names = algodef->algorithm_names;
+    int name_id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR);
 
     if (name_id == 0)
         return NULL;
-    return methdata->method_from_dispatch(name_id, fns, prov);
+    return methdata->method_from_dispatch(name_id, algodef->implementation,
+                                          prov);
 }
 
 static void destruct_evp_method(void *method, void *data)
@@ -466,7 +406,8 @@ static void do_one(OSSL_PROVIDER *provider, const OSSL_ALGORITHM *algo,
     struct do_all_data_st *data = vdata;
     OPENSSL_CTX *libctx = ossl_provider_library_context(provider);
     OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
-    int name_id = add_names_to_namemap(namemap, algo->algorithm_names);
+    int name_id = ossl_namemap_add_names(namemap, 0, algo->algorithm_names,
+                                         NAME_SEPARATOR);
     void *method = NULL;
 
     if (name_id != 0)
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 759fb95401..8e6682fff4 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -21,6 +21,7 @@
 #include <openssl/cmac.h>
 #include <openssl/engine.h>
 #include <openssl/params.h>
+#include <openssl/serializer.h>
 #include <openssl/core_names.h>
 
 #include "crypto/asn1.h"
@@ -666,6 +667,18 @@ static int unsup_alg(BIO *out, const EVP_PKEY *pkey, int indent,
 int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey,
                           int indent, ASN1_PCTX *pctx)
 {
+    const char *pq = OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ;
+    OSSL_SERIALIZER_CTX *ctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, pq);
+    int ret = -2;                /* mark as unsupported */
+
+    if (OSSL_SERIALIZER_CTX_get_serializer(ctx) != NULL)
+        ret = OSSL_SERIALIZER_to_bio(ctx, out);
+    OSSL_SERIALIZER_CTX_free(ctx);
+
+    if (ret != -2)
+        return ret;
+
+    /* legacy fallback */
     if (pkey->ameth && pkey->ameth->pub_print)
         return pkey->ameth->pub_print(out, pkey, indent, pctx);
 
@@ -675,6 +688,18 @@ int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey,
 int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
                            int indent, ASN1_PCTX *pctx)
 {
+    const char *pq = OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ;
+    OSSL_SERIALIZER_CTX *ctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, pq);
+    int ret = -2;                /* mark as unsupported */
+
+    if (OSSL_SERIALIZER_CTX_get_serializer(ctx) != NULL)
+        ret = OSSL_SERIALIZER_to_bio(ctx, out);
+    OSSL_SERIALIZER_CTX_free(ctx);
+
+    if (ret != -2)
+        return ret;
+
+    /* legacy fallback */
     if (pkey->ameth && pkey->ameth->priv_print)
         return pkey->ameth->priv_print(out, pkey, indent, pctx);
 
@@ -684,8 +709,21 @@ int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey,
 int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey,
                           int indent, ASN1_PCTX *pctx)
 {
+    const char *pq = OSSL_SERIALIZER_Parameters_TO_TEXT_PQ;
+    OSSL_SERIALIZER_CTX *ctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, pq);
+    int ret = -2;                /* mark as unsupported */
+
+    if (OSSL_SERIALIZER_CTX_get_serializer(ctx) != NULL)
+        ret = OSSL_SERIALIZER_to_bio(ctx, out);
+    OSSL_SERIALIZER_CTX_free(ctx);
+
+    if (ret != -2)
+        return ret;
+
+    /* legacy fallback */
     if (pkey->ameth && pkey->ameth->param_print)
         return pkey->ameth->param_print(out, pkey, indent, pctx);
+
     return unsup_alg(out, pkey, indent, "Parameters");
 }
 
diff --git a/crypto/pem/pem_all.c b/crypto/pem/pem_all.c
index 975e62e9f7..ba98371d46 100644
--- a/crypto/pem/pem_all.c
+++ b/crypto/pem/pem_all.c
@@ -17,6 +17,7 @@
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
 #include <openssl/dh.h>
+#include "pem_local.h"
 
 #ifndef OPENSSL_NO_RSA
 static RSA *pkey_get_rsa(EVP_PKEY *key, RSA **rsa);
@@ -33,6 +34,7 @@ IMPLEMENT_PEM_rw(X509_REQ, X509_REQ, PEM_STRING_X509_REQ, X509_REQ)
 
 IMPLEMENT_PEM_write(X509_REQ_NEW, X509_REQ, PEM_STRING_X509_REQ_OLD, X509_REQ)
 IMPLEMENT_PEM_rw(X509_CRL, X509_CRL, PEM_STRING_X509_CRL, X509_CRL)
+IMPLEMENT_PEM_rw(X509_PUBKEY, X509_PUBKEY, PEM_STRING_PUBLIC, X509_PUBKEY)
 IMPLEMENT_PEM_rw(PKCS7, PKCS7, PEM_STRING_PKCS7, PKCS7)
 
 IMPLEMENT_PEM_rw(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE,
@@ -172,4 +174,5 @@ EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
 IMPLEMENT_PEM_write(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
 IMPLEMENT_PEM_write(DHxparams, DH, PEM_STRING_DHXPARAMS, DHxparams)
 #endif
-IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
+IMPLEMENT_PEM_provided_write(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
+IMPLEMENT_PEM_read(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
diff --git a/crypto/pem/pem_local.h b/crypto/pem/pem_local.h
new file mode 100644
index 0000000000..3b501abde7
--- /dev/null
+++ b/crypto/pem/pem_local.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * TODO(v3.0): the IMPLEMENT macros in include/openssl/pem.h should be
+ * moved here.
+ */
+
+#include <openssl/pem.h>
+#include <openssl/serializer.h>
+
+/* Alternative IMPLEMENT macros for provided serializers */
+
+# define IMPLEMENT_PEM_provided_write_body_vars(type, asn1)             \
+    int ret = 0;                                                        \
+    const char *pq = OSSL_SERIALIZER_##asn1##_TO_PEM_PQ;                \
+    OSSL_SERIALIZER_CTX *ctx = OSSL_SERIALIZER_CTX_new_by_##type(x, pq); \
+                                                                        \
+    if (ctx != NULL && OSSL_SERIALIZER_CTX_get_serializer(ctx) == NULL) { \
+        OSSL_SERIALIZER_CTX_free(ctx);                                  \
+        goto legacy;                                                    \
+    }
+# define IMPLEMENT_PEM_provided_write_body_pass()                       \
+    ret = 1;                                                            \
+    if (kstr == NULL && cb == NULL) {                                   \
+        if (u != NULL) {                                                \
+            kstr = u;                                                   \
+            klen = strlen(u);                                           \
+        } else {                                                        \
+            cb = PEM_def_callback;                                      \
+        }                                                               \
+    }                                                                   \
+    if (enc != NULL) {                                                  \
+        ret = 0;                                                        \
+        if (OSSL_SERIALIZER_CTX_set_cipher(ctx, EVP_CIPHER_name(enc),   \
+                                           NULL)) {                     \
+            ret = 1;                                                    \
+            if (kstr != NULL                                            \
+                && !OSSL_SERIALIZER_CTX_set_passphrase(ctx, kstr, klen)) \
+                ret = 0;                                                \
+            else if (cb != NULL                                         \
+                     && !OSSL_SERIALIZER_CTX_set_passphrase_cb(ctx, 1,  \
+                                                               cb, u))  \
+                ret = 0;                                                \
+        }                                                               \
+    }                                                                   \
+    if (!ret) {                                                         \
+        OSSL_SERIALIZER_CTX_free(ctx);                                  \
+        return 0;                                                       \
+    }
+# define IMPLEMENT_PEM_provided_write_body_main(type, outtype)          \
+    ret = OSSL_SERIALIZER_to_##outtype(ctx, out);                       \
+    OSSL_SERIALIZER_CTX_free(ctx);                                      \
+    return ret
+# define IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,          \
+                                                    writename)          \
+    legacy:                                                             \
+    return PEM_ASN1_##writename((i2d_of_void *)i2d_##asn1, str, out,    \
+                                  x, NULL, NULL, 0, NULL, NULL)
+# define IMPLEMENT_PEM_provided_write_body_fallback_cb(str, asn1,       \
+                                                       writename)       \
+    legacy:                                                             \
+    return PEM_ASN1_##writename((i2d_of_void *)i2d_##asn1, str, out,    \
+                                x, enc, kstr, klen, cb, u)
+
+# define IMPLEMENT_PEM_provided_write_to(name, type, str, asn1,         \
+                                         OUTTYPE, outtype, writename)   \
+    PEM_write_fnsig(name, type, OUTTYPE, writename)                     \
+    {                                                                   \
+        IMPLEMENT_PEM_provided_write_body_vars(type, asn1);             \
+        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
+        IMPLEMENT_PEM_provided_write_body_fallback(str, asn1,           \
+                                                   writename);          \
+    }
+
+
+# define IMPLEMENT_PEM_provided_write_cb_to(name, type, str, asn1,      \
+                                            OUTTYPE, outtype, writename) \
+    PEM_write_cb_fnsig(name, type, OUTTYPE, writename)                  \
+    {                                                                   \
+        IMPLEMENT_PEM_provided_write_body_vars(type, asn1);             \
+        IMPLEMENT_PEM_provided_write_body_pass();                       \
+        IMPLEMENT_PEM_provided_write_body_main(type, outtype);          \
+        IMPLEMENT_PEM_provided_write_body_fallback_cb(str, asn1,        \
+                                                      writename);       \
+    }
+
+# ifdef OPENSSL_NO_STDIO
+
+#  define IMPLEMENT_PEM_provided_write_fp(name, type, str, asn1)
+#  define IMPLEMENT_PEM_provided_write_cb_fp(name, type, str, asn1)
+
+# else
+
+#  define IMPLEMENT_PEM_provided_write_fp(name, type, str, asn1)        \
+    IMPLEMENT_PEM_provided_write_to(name, type, str, asn1, FILE, fp, write)
+#  define IMPLEMENT_PEM_provided_write_cb_fp(name, type, str, asn1)     \
+    IMPLEMENT_PEM_provided_write_cb_to(name, type, str, asn1, FILE, fp, write)
+
+# endif
+
+# define IMPLEMENT_PEM_provided_write_bio(name, type, str, asn1)        \
+    IMPLEMENT_PEM_provided_write_to(name, type, str, asn1, BIO, bio, write_bio)
+# define IMPLEMENT_PEM_provided_write_cb_bio(name, type, str, asn1)     \
+    IMPLEMENT_PEM_provided_write_cb_to(name, type, str, asn1, BIO, bio, write_bio)
+
+# define IMPLEMENT_PEM_provided_write(name, type, str, asn1)    \
+    IMPLEMENT_PEM_provided_write_bio(name, type, str, asn1)     \
+    IMPLEMENT_PEM_provided_write_fp(name, type, str, asn1)
+
+# define IMPLEMENT_PEM_provided_write_cb(name, type, str, asn1) \
+    IMPLEMENT_PEM_provided_write_cb_bio(name, type, str, asn1)  \
+    IMPLEMENT_PEM_provided_write_cb_fp(name, type, str, asn1)
+
+# define IMPLEMENT_PEM_provided_rw(name, type, str, asn1) \
+    IMPLEMENT_PEM_read(name, type, str, asn1)                   \
+    IMPLEMENT_PEM_provided_write(name, type, str, asn1)
+
+# define IMPLEMENT_PEM_provided_rw_cb(name, type, str, asn1) \
+    IMPLEMENT_PEM_read(name, type, str, asn1)                   \
+    IMPLEMENT_PEM_provided_write_cb(name, type, str, asn1)
+
diff --git a/crypto/pem/pem_pkey.c b/crypto/pem/pem_pkey.c
index 6f89759f1e..c619cc11b4 100644
--- a/crypto/pem/pem_pkey.c
+++ b/crypto/pem/pem_pkey.c
@@ -19,9 +19,11 @@
 #include <openssl/dh.h>
 #include <openssl/store.h>
 #include <openssl/ui.h>
+#include <openssl/serializer.h>
 #include "crypto/store.h"
 #include "crypto/asn1.h"
 #include "crypto/evp.h"
+#include "pem_local.h"
 
 int pem_check_suffix(const char *pem_str, const char *suffix);
 
@@ -64,15 +66,18 @@ EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb,
     return ret;
 }
 
-int PEM_write_bio_PrivateKey(BIO *bp, const EVP_PKEY *x,
-                             const EVP_CIPHER *enc,
-                             const unsigned char *kstr, int klen,
-                             pem_password_cb *cb, void *u)
+PEM_write_cb_fnsig(PrivateKey, EVP_PKEY, BIO, write_bio)
 {
+    IMPLEMENT_PEM_provided_write_body_vars(EVP_PKEY, PrivateKey);
+
+    IMPLEMENT_PEM_provided_write_body_pass();
+    IMPLEMENT_PEM_provided_write_body_main(EVP_PKEY, bio);
+
+ legacy:
     if (x->ameth == NULL || x->ameth->priv_encode != NULL)
-        return PEM_write_bio_PKCS8PrivateKey(bp, x, enc,
+        return PEM_write_bio_PKCS8PrivateKey(out, x, enc,
                                              (const char *)kstr, klen, cb, u);
-    return PEM_write_bio_PrivateKey_traditional(bp, x, enc, kstr, klen, cb, u);
+    return PEM_write_bio_PrivateKey_traditional(out, x, enc, kstr, klen, cb, u);
 }
 
 int PEM_write_bio_PrivateKey_traditional(BIO *bp, const EVP_PKEY *x,
@@ -112,15 +117,20 @@ EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x)
     return ret;
 }
 
-int PEM_write_bio_Parameters(BIO *bp, const EVP_PKEY *x)
+PEM_write_fnsig(Parameters, EVP_PKEY, BIO, write_bio)
 {
     char pem_str[80];
+    IMPLEMENT_PEM_provided_write_body_vars(EVP_PKEY, Parameters);
+
+    IMPLEMENT_PEM_provided_write_body_main(EVP_PKEY, bio);
+
+ legacy:
     if (!x->ameth || !x->ameth->param_encode)
         return 0;
 
     BIO_snprintf(pem_str, 80, "%s PARAMETERS", x->ameth->pem_str);
     return PEM_ASN1_write_bio((i2d_of_void *)x->ameth->param_encode,
-                              pem_str, bp, x, NULL, NULL, 0, 0, NULL);
+                              pem_str, out, x, NULL, NULL, 0, 0, NULL);
 }
 
 #ifndef OPENSSL_NO_STDIO
diff --git a/crypto/property/property_local.h b/crypto/property/property_local.h
index 25cfde4649..950a0c6c07 100644
--- a/crypto/property/property_local.h
+++ b/crypto/property/property_local.h
@@ -11,7 +11,6 @@
 #include <openssl/crypto.h>
 #include "internal/property.h"
 
-typedef struct ossl_property_list_st OSSL_PROPERTY_LIST;
 typedef int OSSL_PROPERTY_IDX;
 
 /* Property string functions */
@@ -23,17 +22,9 @@ OSSL_PROPERTY_IDX ossl_property_value(OPENSSL_CTX *ctx, const char *s,
 /* Property list functions */
 void ossl_property_free(OSSL_PROPERTY_LIST *p);
 int ossl_property_has_optional(const OSSL_PROPERTY_LIST *query);
-int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
-                              const OSSL_PROPERTY_LIST *defn);
 OSSL_PROPERTY_LIST *ossl_property_merge(const OSSL_PROPERTY_LIST *a,
                                         const OSSL_PROPERTY_LIST *b);
 
-/* Property definition functions */
-OSSL_PROPERTY_LIST *ossl_parse_property(OPENSSL_CTX *ctx, const char *s);
-
-/* Property query functions */
-OSSL_PROPERTY_LIST *ossl_parse_query(OPENSSL_CTX *ctx, const char *s);
-
 /* Property definition cache functions */
 OSSL_PROPERTY_LIST *ossl_prop_defn_get(OPENSSL_CTX *ctx, const char *prop);
 int ossl_prop_defn_set(OPENSSL_CTX *ctx, const char *prop,
diff --git a/crypto/property/property_parse.c b/crypto/property/property_parse.c
index a16bcd6dba..f6377e7714 100644
--- a/crypto/property/property_parse.c
+++ b/crypto/property/property_parse.c
@@ -568,6 +568,8 @@ int ossl_property_parse_init(OPENSSL_CTX *ctx)
         "version",      /* Version number of this provider */
         "fips",         /* FIPS supporting provider */
         "engine",       /* An old style engine masquerading as a provider */
+        "format",       /* output format for serializers */
+        "type",         /* output type for serializers */
     };
     size_t i;
 
diff --git a/crypto/provider_core.c b/crypto/provider_core.c
index 9e92e96b10..c7c579a1c8 100644
--- a/crypto/provider_core.c
+++ b/crypto/provider_core.c
@@ -873,6 +873,7 @@ static const OSSL_DISPATCH core_dispatch_[] = {
     { OSSL_FUNC_BIO_NEW_MEMBUF, (void (*)(void))BIO_new_mem_buf },
     { OSSL_FUNC_BIO_READ_EX, (void (*)(void))BIO_read_ex },
     { OSSL_FUNC_BIO_FREE, (void (*)(void))BIO_free },
+    { OSSL_FUNC_BIO_VPRINTF, (void (*)(void))BIO_vprintf },
 #endif
 
     { OSSL_FUNC_CRYPTO_MALLOC, (void (*)(void))CRYPTO_malloc },
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index 71e4b78306..bf47540b45 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -584,6 +584,11 @@ const BIGNUM *RSA_get0_iqmp(const RSA *r)
     return r->iqmp;
 }
 
+const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *r)
+{
+    return r->pss;
+}
+
 void RSA_clear_flags(RSA *r, int flags)
 {
     r->flags &= ~flags;
diff --git a/crypto/serializer/build.info b/crypto/serializer/build.info
new file mode 100644
index 0000000000..551319ed59
--- /dev/null
+++ b/crypto/serializer/build.info
@@ -0,0 +1,2 @@
+SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c \
+        serializer_err.c
diff --git a/crypto/serializer/serializer_err.c b/crypto/serializer/serializer_err.c
new file mode 100644
index 0000000000..0f0fc5fa80
--- /dev/null
+++ b/crypto/serializer/serializer_err.c
@@ -0,0 +1,31 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/serializererr.h>
+
+#ifndef OPENSSL_NO_ERR
+
+static const ERR_STRING_DATA OSSL_SERIALIZER_str_reasons[] = {
+    {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY),
+    "incorrect property query"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_OSSL_SERIALIZER_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+    if (ERR_reason_error_string(OSSL_SERIALIZER_str_reasons[0].error) == NULL)
+        ERR_load_strings_const(OSSL_SERIALIZER_str_reasons);
+#endif
+    return 1;
+}
diff --git a/crypto/serializer/serializer_lib.c b/crypto/serializer/serializer_lib.c
new file mode 100644
index 0000000000..932ef1e3ae
--- /dev/null
+++ b/crypto/serializer/serializer_lib.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/bio.h>
+#include <openssl/serializer.h>
+#include "serializer_local.h"
+
+int OSSL_SERIALIZER_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
+{
+    return ctx->do_output(ctx, out);
+}
+
+#ifndef OPENSSL_NO_STDIO
+static BIO *bio_from_file(FILE *fp)
+{
+    BIO *b;
+
+    if ((b = BIO_new(BIO_s_file())) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_BUF_LIB);
+        return NULL;
+    }
+    BIO_set_fp(b, fp, BIO_NOCLOSE);
+    return b;
+}
+
+int OSSL_SERIALIZER_to_fp(OSSL_SERIALIZER_CTX *ctx, FILE *fp)
+{
+    BIO *b = bio_from_file(fp);
+    int ret = 0;
+
+    if (b != NULL)
+        ret = OSSL_SERIALIZER_to_bio(ctx, b);
+
+    BIO_free(b);
+    return ret;
+}
+#endif
diff --git a/crypto/serializer/serializer_local.h b/crypto/serializer/serializer_local.h
new file mode 100644
index 0000000000..9ee0eb8244
--- /dev/null
+++ b/crypto/serializer/serializer_local.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/types.h>
+#include "internal/cryptlib.h"
+#include "internal/refcount.h"
+
+struct ossl_serializer_st {
+    OSSL_PROVIDER *prov;
+    int id;
+    const char *propdef;
+
+    CRYPTO_REF_COUNT refcnt;
+    CRYPTO_RWLOCK *lock;
+
+    OSSL_OP_serializer_newctx_fn *newctx;
+    OSSL_OP_serializer_freectx_fn *freectx;
+    OSSL_OP_serializer_set_ctx_params_fn *set_ctx_params;
+    OSSL_OP_serializer_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_OP_serializer_serialize_data_fn *serialize_data;
+    OSSL_OP_serializer_serialize_object_fn *serialize_object;
+};
+
+struct ossl_serializer_ctx_st {
+    OSSL_SERIALIZER *ser;
+    void *serctx;
+
+    /*
+     * |object| is the libcrypto object to handle.
+     * |do_output| must have intimate knowledge of this object.
+     */
+    const void *object;
+    int (*do_output)(OSSL_SERIALIZER_CTX *ctx, BIO *out);
+
+    /* For any function that needs a passphrase reader */
+    const UI_METHOD *ui_method;
+    void *ui_data;
+    /*
+     * if caller used OSSL_SERIALIZER_CTX_set_passphrase_cb(), we need
+     * intermediary storage.
+     */
+    UI_METHOD *allocated_ui_method;
+};
diff --git a/crypto/serializer/serializer_meth.c b/crypto/serializer/serializer_meth.c
new file mode 100644
index 0000000000..145d58921f
--- /dev/null
+++ b/crypto/serializer/serializer_meth.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_numbers.h>
+#include <openssl/serializer.h>
+#include <openssl/ui.h>
+#include "internal/core.h"
+#include "internal/namemap.h"
+#include "internal/property.h"
+#include "internal/provider.h"
+#include "crypto/serializer.h"
+#include "serializer_local.h"
+
+/*
+ * Serializer can have multiple names, separated with colons in a name string
+ */
+#define NAME_SEPARATOR ':'
+
+/* Simple method structure constructor and destructor */
+static OSSL_SERIALIZER *ossl_serializer_new(void)
+{
+    OSSL_SERIALIZER *ser = NULL;
+
+    if ((ser = OPENSSL_zalloc(sizeof(*ser))) == NULL
+        || (ser->lock = CRYPTO_THREAD_lock_new()) == NULL) {
+        OSSL_SERIALIZER_free(ser);
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ser->refcnt = 1;
+
+    return ser;
+}
+
+int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *ser)
+{
+    int ref = 0;
+
+    CRYPTO_UP_REF(&ser->refcnt, &ref, ser->lock);
+    return 1;
+}
+
+void OSSL_SERIALIZER_free(OSSL_SERIALIZER *ser)
+{
+    int ref = 0;
+
+    if (ser == NULL)
+        return;
+
+    CRYPTO_DOWN_REF(&ser->refcnt, &ref, ser->lock);
+    if (ref > 0)
+        return;
+    ossl_provider_free(ser->prov);
+    CRYPTO_THREAD_lock_free(ser->lock);
+    OPENSSL_free(ser);
+}
+
+/* Permanent serializer method store, constructor and destructor */
+static void serializer_store_free(void *vstore)
+{
+    ossl_method_store_free(vstore);
+}
+
+static void *serializer_store_new(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+
+static const OPENSSL_CTX_METHOD serializer_store_method = {
+    serializer_store_new,
+    serializer_store_free,
+};
+
+/* Data to be passed through ossl_method_construct() */
+struct serializer_data_st {
+    OPENSSL_CTX *libctx;
+    OSSL_METHOD_CONSTRUCT_METHOD *mcm;
+    int id;                      /* For get_serializer_from_store() */
+    const char *names;           /* For get_serializer_from_store() */
+    const char *propquery;       /* For get_serializer_from_store() */
+};
+
+/*
+ * Generic routines to fetch / create SERIALIZER methods with
+ * ossl_method_construct()
+ */
+
+/* Temporary serializer method store, constructor and destructor */
+static void *alloc_tmp_serializer_store(OPENSSL_CTX *ctx)
+{
+    return ossl_method_store_new(ctx);
+}
+
+ static void dealloc_tmp_serializer_store(void *store)
+{
+    if (store != NULL)
+        ossl_method_store_free(store);
+}
+
+/* Get the permanent serializer store */
+static OSSL_METHOD_STORE *get_serializer_store(OPENSSL_CTX *libctx)
+{
+    return openssl_ctx_get_data(libctx, OPENSSL_CTX_SERIALIZER_STORE_INDEX,
+                                &serializer_store_method);
+}
+
+/* Get serializer methods from a store, or put one in */
+static void *get_serializer_from_store(OPENSSL_CTX *libctx, void *store,
+                                       void *data)
+{
+    struct serializer_data_st *methdata = data;
+    void *method = NULL;
+    int id;
+
+    if ((id = methdata->id) == 0) {
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        id = ossl_namemap_name2num(namemap, methdata->names);
+    }
+
+    if (store == NULL
+        && (store = get_serializer_store(libctx)) == NULL)
+        return NULL;
+
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+        return NULL;
+    return method;
+}
+
+static int put_serializer_in_store(OPENSSL_CTX *libctx, void *store,
+                                   void *method, const OSSL_PROVIDER *prov,
+                                   int operation_id, const char *names,
+                                   const char *propdef, void *unused)
+{
+    OSSL_NAMEMAP *namemap;
+    int id;
+
+    if ((namemap = ossl_namemap_stored(libctx)) == NULL
+        || (id = ossl_namemap_name2num(namemap, names)) == 0)
+        return 0;
+
+    if (store == NULL && (store = get_serializer_store(libctx)) == NULL)
+        return 0;
+
+    return ossl_method_store_add(store, prov, id, propdef, method,
+                                 (int (*)(void *))OSSL_SERIALIZER_up_ref,
+                                 (void (*)(void *))OSSL_SERIALIZER_free);
+}
+
+/* Create and populate a serializer method */
+static void *serializer_from_dispatch(int id, const OSSL_ALGORITHM *algodef,
+                                      OSSL_PROVIDER *prov)
+{
+    OSSL_SERIALIZER *ser = NULL;
+    const OSSL_DISPATCH *fns = algodef->implementation;
+
+    if ((ser = ossl_serializer_new()) == NULL)
+        return NULL;
+    ser->id = id;
+    ser->propdef = algodef->property_definition;
+
+    for (; fns->function_id != 0; fns++) {
+        switch (fns->function_id) {
+        case OSSL_FUNC_SERIALIZER_NEWCTX:
+            if (ser->newctx == NULL)
+                ser->newctx =
+                    OSSL_get_OP_serializer_newctx(fns);
+            break;
+        case OSSL_FUNC_SERIALIZER_FREECTX:
+            if (ser->freectx == NULL)
+                ser->freectx =
+                    OSSL_get_OP_serializer_freectx(fns);
+            break;
+        case OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS:
+            if (ser->set_ctx_params == NULL)
+                ser->set_ctx_params =
+                    OSSL_get_OP_serializer_set_ctx_params(fns);
+            break;
+        case OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS:
+            if (ser->settable_ctx_params == NULL)
+                ser->settable_ctx_params =
+                    OSSL_get_OP_serializer_settable_ctx_params(fns);
+            break;
+        case OSSL_FUNC_SERIALIZER_SERIALIZE_DATA:
+            if (ser->serialize_data == NULL)
+                ser->serialize_data =
+                    OSSL_get_OP_serializer_serialize_data(fns);
+            break;
+        case OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT:
+            if (ser->serialize_object == NULL)
+                ser->serialize_object =
+                    OSSL_get_OP_serializer_serialize_object(fns);
+            break;
+        }
+    }
+    /*
+     * Try to check that the method is sensible.
+     * If you have a constructor, you must have a destructor and vice versa.
+     * You must have at least one of the serializing driver functions.
+     */
+    if (!((ser->newctx == NULL && ser->freectx == NULL)
+          || (ser->newctx != NULL && ser->freectx != NULL))
+        || (ser->serialize_data == NULL && ser->serialize_object == NULL)) {
+        OSSL_SERIALIZER_free(ser);
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INVALID_PROVIDER_FUNCTIONS);
+        return NULL;
+    }
+
+    if (prov != NULL && !ossl_provider_up_ref(prov)) {
+        OSSL_SERIALIZER_free(ser);
+        return NULL;
+    }
+
+    ser->prov = prov;
+    return ser;
+}
+
+
+/*
+ * The core fetching functionality passes the names of the implementation.
+ * This function is responsible to getting an identity number for them,
+ * then call serializer_from_dispatch() with that identity number.
+ */
+static void *construct_serializer(const OSSL_ALGORITHM *algodef,
+                                  OSSL_PROVIDER *prov, void *unused)
+{
+    /*
+     * This function is only called if get_serializer_from_store() returned
+     * NULL, so it's safe to say that of all the spots to create a new
+     * namemap entry, this is it.  Should the name already exist there, we
+     * know that ossl_namemap_add() will return its corresponding number.
+     */
+    OPENSSL_CTX *libctx = ossl_provider_library_context(prov);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    const char *names = algodef->algorithm_names;
+    int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR);
+    void *method = NULL;
+
+    if (id != 0)
+        method = serializer_from_dispatch(id, algodef, prov);
+
+    return method;
+}
+
+/* Intermediary function to avoid ugly casts, used below */
+static void destruct_serializer(void *method, void *data)
+{
+    OSSL_SERIALIZER_free(method);
+}
+
+static int up_ref_serializer(void *method)
+{
+    return OSSL_SERIALIZER_up_ref(method);
+}
+
+static void free_serializer(void *method)
+{
+    OSSL_SERIALIZER_free(method);
+}
+
+/* Fetching support.  Can fetch by numeric identity or by name */
+static OSSL_SERIALIZER *inner_ossl_serializer_fetch(OPENSSL_CTX *libctx,
+                                                    int id, const char *name,
+                                                    const char *properties)
+{
+    OSSL_METHOD_STORE *store = get_serializer_store(libctx);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    void *method = NULL;
+
+    if (store == NULL || namemap == NULL)
+        return NULL;
+
+    /*
+     * If we have been passed neither a name_id or a name, we have an
+     * internal programming error.
+     */
+    if (!ossl_assert(id != 0 || name != NULL))
+        return NULL;
+
+    if (id == 0)
+        id = ossl_namemap_name2num(namemap, name);
+
+    if (id == 0
+        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        OSSL_METHOD_CONSTRUCT_METHOD mcm = {
+            alloc_tmp_serializer_store,
+            dealloc_tmp_serializer_store,
+            get_serializer_from_store,
+            put_serializer_in_store,
+            construct_serializer,
+            destruct_serializer
+        };
+        struct serializer_data_st mcmdata;
+
+        mcmdata.libctx = libctx;
+        mcmdata.mcm = &mcm;
+        mcmdata.id = id;
+        mcmdata.names = name;
+        mcmdata.propquery = properties;
+        if ((method = ossl_method_construct(libctx, OSSL_OP_SERIALIZER,
+                                            0 /* !force_cache */,
+                                            &mcm, &mcmdata)) != NULL) {
+            /*
+             * If construction did create a method for us, we know that
+             * there is a correct name_id and meth_id, since those have
+             * already been calculated in get_serializer_from_store() and
+             * put_serializer_in_store() above.
+             */
+            if (id == 0)
+                id = ossl_namemap_name2num(namemap, name);
+            ossl_method_store_cache_set(store, id, properties, method,
+                                        up_ref_serializer, free_serializer);
+        }
+    }
+
+    return method;
+}
+
+OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *libctx, const char *name,
+                                       const char *properties)
+{
+    return inner_ossl_serializer_fetch(libctx, 0, name, properties);
+}
+
+OSSL_SERIALIZER *ossl_serializer_fetch_by_number(OPENSSL_CTX *libctx, int id,
+                                                 const char *properties)
+{
+    return inner_ossl_serializer_fetch(libctx, id, NULL, properties);
+}
+
+/*
+ * Library of basic method functions
+ */
+
+const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER *ser)
+{
+    if (!ossl_assert(ser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return ser->prov;
+}
+
+const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser)
+{
+    if (!ossl_assert(ser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return ser->propdef;
+}
+
+int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser)
+{
+    if (!ossl_assert(ser != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return ser->id;
+}
+
+int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *ser, const char *name)
+{
+    if (ser->prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        return ossl_namemap_name2num(namemap, name) == ser->id;
+    }
+    return 0;
+}
+
+struct serializer_do_all_data_st {
+    void (*user_fn)(void *method, void *arg);
+    void *user_arg;
+};
+
+static void serializer_do_one(OSSL_PROVIDER *provider,
+                              const OSSL_ALGORITHM *algodef,
+                              int no_store, void *vdata)
+{
+    struct serializer_do_all_data_st *data = vdata;
+    OPENSSL_CTX *libctx = ossl_provider_library_context(provider);
+    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+    const char *names = algodef->algorithm_names;
+    int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR);
+    void *method = NULL;
+
+    if (id != 0)
+        method =
+            serializer_from_dispatch(id, algodef, provider);
+
+    if (method != NULL) {
+        data->user_fn(method, data->user_arg);
+        OSSL_SERIALIZER_free(method);
+    }
+}
+
+void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                     void (*fn)(OSSL_SERIALIZER *ser,
+                                                void *arg),
+                                     void *arg)
+{
+    struct serializer_do_all_data_st data;
+
+    data.user_fn = (void (*)(void *, void *))fn;
+    data.user_arg = arg;
+    ossl_algorithm_do_all(libctx, OSSL_OP_SERIALIZER, NULL,
+                          serializer_do_one, &data);
+}
+
+void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *ser,
+                                  void (*fn)(const char *name, void *data),
+                                  void *data)
+{
+    if (ser == NULL)
+        return;
+
+    if (ser->prov != NULL) {
+        OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov);
+        OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
+
+        ossl_namemap_doall_names(namemap, ser->id, fn, data);
+    }
+}
+
+const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser)
+{
+    if (ser != NULL && ser->settable_ctx_params != NULL)
+        return ser->settable_ctx_params();
+    return NULL;
+}
+
+/*
+ * Serializer context support
+ */
+
+/*
+ * |ser| value NULL is valid, and signifies that there is no serializer.
+ * This is useful to provide fallback mechanisms.
+ *  Funtions that want to verify if there is a serializer can do so with
+ * OSSL_SERIALIZER_CTX_get_serializer()
+ */
+OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser)
+{
+    OSSL_SERIALIZER_CTX *ctx;
+
+    if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ctx->ser = ser;
+    if (ser != NULL && ser->newctx != NULL) {
+        const OSSL_PROVIDER *prov = OSSL_SERIALIZER_provider(ser);
+        void *provctx = ossl_provider_ctx(prov);
+
+        if (OSSL_SERIALIZER_up_ref(ser)) {
+            ctx->serctx = ser->newctx(provctx);
+        } else {
+            OSSL_SERIALIZER_free(ser);
+            OPENSSL_free(ctx);
+            ctx = NULL;
+        }
+    }
+
+    return ctx;
+}
+
+const OSSL_SERIALIZER *
+OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    return ctx->ser;
+}
+
+
+int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx,
+                                   const OSSL_PARAM params[])
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if (ctx->ser != NULL && ctx->ser->set_ctx_params != NULL)
+        return ctx->ser->set_ctx_params(ctx->serctx, params);
+    return 0;
+}
+
+void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx)
+{
+    if (ctx != NULL) {
+        if (ctx->ser != NULL && ctx->ser->freectx != NULL)
+            ctx->ser->freectx(ctx->serctx);
+        OSSL_SERIALIZER_free(ctx->ser);
+        UI_destroy_method(ctx->allocated_ui_method);
+        OPENSSL_free(ctx);
+    }
+}
diff --git a/crypto/serializer/serializer_pkey.c b/crypto/serializer/serializer_pkey.c
new file mode 100644
index 0000000000..d3c3362f66
--- /dev/null
+++ b/crypto/serializer/serializer_pkey.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/ui.h>
+#include <openssl/params.h>
+#include <openssl/serializer.h>
+#include <openssl/core_names.h>
+#include "internal/provider.h"
+#include "internal/property.h"
+#include "crypto/evp.h"
+#include "serializer_local.h"
+
+int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx,
+                                   const char *cipher_name,
+                                   const char *propquery)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] =
+        OSSL_PARAM_construct_utf8_string(OSSL_SERIALIZER_PARAM_CIPHER,
+                                         (void *)cipher_name, 0);
+    params[1] =
+        OSSL_PARAM_construct_utf8_string(OSSL_SERIALIZER_PARAM_PROPERTIES,
+                                         (void *)propquery, 0);
+
+    return OSSL_SERIALIZER_CTX_set_params(ctx, params);
+}
+
+int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx,
+                                       const unsigned char *kstr,
+                                       size_t klen)
+{
+    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+    params[0] = OSSL_PARAM_construct_octet_string(OSSL_SERIALIZER_PARAM_PASS,
+                                                  (void *)kstr, klen);
+
+    return OSSL_SERIALIZER_CTX_set_params(ctx, params);
+}
+
+static void serializer_ctx_reset_passphrase_ui(OSSL_SERIALIZER_CTX *ctx)
+{
+    UI_destroy_method(ctx->allocated_ui_method);
+    ctx->allocated_ui_method = NULL;
+    ctx->ui_method = NULL;
+    ctx->ui_data = NULL;
+}
+
+int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
+                                          const UI_METHOD *ui_method,
+                                          void *ui_data)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    serializer_ctx_reset_passphrase_ui(ctx);
+    ctx->ui_method = ui_method;
+    ctx->ui_data = ui_data;
+    return 1;
+}
+
+int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+                                          pem_password_cb *cb, void *cbarg)
+{
+    if (!ossl_assert(ctx != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    serializer_ctx_reset_passphrase_ui(ctx);
+    if (cb == NULL)
+        return 1;
+    ctx->ui_method =
+        ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, enc);
+    ctx->ui_data = cbarg;
+
+    return ctx->ui_method != NULL;
+}
+
+/*
+ * Support for OSSL_SERIALIZER_CTX_new_by_TYPE:
+ * finding a suitable serializer
+ */
+
+struct selected_serializer_st {
+    OPENSSL_CTX *libctx;
+    const OSSL_PROVIDER *desired_provider;
+    const char *propquery;
+
+    /*
+     * When selecting serializers, we need to check the intended use.
+     * This is governed by the |domainparams| flag in the EVP_PKEY,
+     * we must just make sure to filter on 'type=domainparams' accordingly.
+     */
+    int want_domainparams;
+
+    /*
+     * Serializers offer two functions, one that handles object data in
+     * the form of a OSSL_PARAM array, and one that directly handles a
+     * provider side object.  The latter requires that the serializer
+     * is offered by the same provider that holds that object, but is
+     * more desirable because it usually provides faster serialization.
+     *
+     * When looking up possible serializers, we save the first that can
+     * handle an OSSL_PARAM array in |first|, and the first that can
+     * handle a provider side object in |desired|.
+     */
+    OSSL_SERIALIZER *first;
+    OSSL_SERIALIZER *desired;
+};
+
+static void select_serializer(const char *name, void *data)
+{
+    struct selected_serializer_st *d = data;
+    OSSL_SERIALIZER *s = NULL;
+    OSSL_PROPERTY_LIST *check =
+        d->want_domainparams
+        ? ossl_parse_query(d->libctx, "type=domainparams")
+        : NULL;
+
+    /* No need to look further if we already have the more desirable option */
+    if (d->desired != NULL)
+        return;
+
+    if ((s = OSSL_SERIALIZER_fetch(d->libctx, name, d->propquery)) != NULL) {
+        /*
+         * Extra check if domain parameters are explicitly specified:
+         * only accept serializers that have the "type=domainparams"
+         * property.
+         *
+         * For data that isn't marked as domain parameters, a domain
+         * parameters serializer is still acceptable, because a key
+         * may hold domain parameters too.
+         */
+        if (d->want_domainparams) {
+            OSSL_PROPERTY_LIST *current_props =
+                ossl_parse_property(d->libctx, OSSL_SERIALIZER_properties(s));
+            int check_cnt = ossl_property_match_count(check, current_props);
+
+            if (check_cnt == 0) {
+                OSSL_SERIALIZER_free(s);
+                return;
+            }
+        }
+
+        if (d->first == NULL && s->serialize_data != NULL) {
+            d->first = s;
+        } else if (OSSL_SERIALIZER_provider(s) == d->desired_provider
+                   && s->serialize_object != NULL) {
+            OSSL_SERIALIZER_free(d->first);
+            d->first = NULL;
+            d->desired = s;
+        } else {
+            OSSL_SERIALIZER_free(s);
+        }
+    }
+}
+
+/*
+ * Support for OSSL_SERIALIZER_CTX_new_by_TYPE and OSSL_SERIALIZER_to_bio:
+ * Passphrase callbacks
+ */
+
+/*
+ * First, we define the generic passphrase function that supports both
+ * outgoing (with passphrase verify) and incoming (without passphrase verify)
+ * passphrase reading.
+ */
+static int serializer_passphrase(char *pass, size_t pass_size,
+                                 size_t *pass_len, int verify,
+                                 const OSSL_PARAM params[], void *arg)
+{
+    OSSL_SERIALIZER_CTX *ctx = arg;
+    const OSSL_PARAM *p;
+    const char *prompt_info = NULL;
+    char *prompt = NULL, *vpass = NULL;
+    int prompt_idx = -1, verify_idx = -1;
+    UI *ui = NULL;
+    int ret = 0;
+
+    if (!ossl_assert(ctx != NULL && pass != NULL
+                    && pass_size != 0 && pass_len != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params,
+                                     OSSL_PASSPHRASE_PARAM_INFO)) != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        prompt_info = p->data;
+    }
+
+    if ((ui = UI_new()) == NULL) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    UI_set_method(ui, ctx->ui_method);
+    UI_add_user_data(ui, ctx->ui_data);
+
+    /* Get an application constructed prompt */
+    prompt = UI_construct_prompt(ui, "pass phrase", prompt_info);
+   if (prompt == NULL) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+        goto end;
+    }
+
+    prompt_idx = UI_add_input_string(ui, prompt,
+                                     UI_INPUT_FLAG_DEFAULT_PWD,
+                                     pass, 0, pass_size - 1) - 1;
+    if (prompt_idx < 0) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
+        goto end;
+    }
+
+    if (verify) {
+        /* Get a buffer for verification prompt */
+        vpass = OPENSSL_zalloc(pass_size);
+        if (vpass == NULL) {
+            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE);
+            goto end;
+        }
+        verify_idx = UI_add_verify_string(ui, prompt,
+                                          UI_INPUT_FLAG_DEFAULT_PWD,
+                                          vpass, 0, pass_size - 1,
+                                          pass) - 1;
+        if (verify_idx < 0) {
+            ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
+            goto end;
+        }
+    }
+
+    switch (UI_process(ui)) {
+    case -2:
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INTERRUPTED_OR_CANCELLED);
+        break;
+    case -1:
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB);
+        break;
+    default:
+        *pass_len = (size_t)UI_get_result_length(ui, prompt_idx);
+        ret = 1;
+        break;
+    }
+
+ end:
+    OPENSSL_free(vpass);
+    OPENSSL_free(prompt);
+    UI_free(ui);
+    return ret;
+}
+
+/* Ensure correct function definition for outgoing passphrase reader */
+static OSSL_PASSPHRASE_CALLBACK serializer_passphrase_out_cb;
+static int serializer_passphrase_out_cb(char *pass, size_t pass_size,
+                                        size_t *pass_len,
+                                        const OSSL_PARAM params[], void *arg)
+{
+    return serializer_passphrase(pass, pass_size, pass_len, 1, params, arg);
+}
+
+/*
+ * Support for OSSL_SERIALIZER_to_bio:
+ * writing callback for the OSSL_PARAM (the implementation doesn't have
+ * intimate knowledge of the provider side object)
+ */
+
+struct serializer_write_data_st {
+    OSSL_SERIALIZER_CTX *ctx;
+    BIO *out;
+};
+
+static int serializer_write_cb(const OSSL_PARAM params[], void *arg)
+{
+    struct serializer_write_data_st *write_data = arg;
+    OSSL_SERIALIZER_CTX *ctx = write_data->ctx;
+    BIO *out = write_data->out;
+
+    return ctx->ser->serialize_data(ctx->serctx, params, out,
+                                    serializer_passphrase_out_cb, ctx);
+}
+
+/*
+ * Support for OSSL_SERIALIZER_to_bio:
+ * Perform the actual output.
+ */
+
+static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out)
+{
+    const EVP_PKEY *pkey = ctx->object;
+    void *provdata = pkey->pkeys[0].provdata;
+    int domainparams = pkey->pkeys[0].domainparams;
+    EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
+
+    /*
+     * OSSL_SERIALIZER_CTX_new() creates a context, even when the
+     * serializer it's given is NULL.  Callers can detect the lack
+     * of serializer with OSSL_SERIALIZER_CTX_get_serializer() and
+     * should take precautions, possibly call a fallback instead of
+     * OSSL_SERIALIZER_to_bio() / OSSL_SERIALIZER_to_fp().  If it's
+     * come this far, we return an error.
+     */
+    if (ctx->ser == NULL)
+        return 0;
+
+    if (ctx->ser->serialize_object == NULL) {
+        struct serializer_write_data_st write_data;
+
+        write_data.ctx = ctx;
+        write_data.out = out;
+
+        if (domainparams)
+            return evp_keymgmt_exportdomparams(keymgmt, provdata,
+                                               serializer_write_cb,
+                                               &write_data);
+        return evp_keymgmt_exportkey(keymgmt, provdata,
+                                     serializer_write_cb, &write_data);
+    }
+
+    return ctx->ser->serialize_object(ctx->serctx, provdata, out,
+                                      serializer_passphrase_out_cb, ctx);
+}
+
+/*
+ * OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() returns a ctx with no serializer if
+ * it couldn't find a suitable serializer.  This allows a caller to detect if
+ * a suitable serializer was found, with OSSL_SERIALIZER_CTX_get_serializer(),
+ * and to use fallback methods if the result is NULL.
+ */
+OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                                         const char *propquery)
+{
+    OSSL_SERIALIZER_CTX *ctx = NULL;
+    OSSL_SERIALIZER *ser = NULL;
+    EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt;
+
+    if (!ossl_assert(pkey != NULL && propquery != NULL)) {
+        ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER);
+        return NULL;
+    }
+
+    if (keymgmt != NULL) {
+        const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt);
+        OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov);
+        struct selected_serializer_st sel_data;
+
+        memset(&sel_data, 0, sizeof(sel_data));
+        sel_data.libctx = libctx;
+        sel_data.desired_provider = desired_prov;
+        sel_data.propquery = propquery;
+        sel_data.want_domainparams = pkey->pkeys[0].domainparams;
+        EVP_KEYMGMT_names_do_all(keymgmt, select_serializer, &sel_data);
+
+        if (sel_data.desired != NULL) {
+            ser = sel_data.desired;
+            sel_data.desired = NULL;
+        } else if (sel_data.first != NULL) {
+            ser = sel_data.first;
+            sel_data.first = NULL;
+        }
+        OSSL_SERIALIZER_free(sel_data.first);
+        OSSL_SERIALIZER_free(sel_data.desired);
+    }
+
+    ctx = OSSL_SERIALIZER_CTX_new(ser); /* refcnt(ser)++ */
+    OSSL_SERIALIZER_free(ser);          /* refcnt(ser)-- */
+
+    if (ctx != NULL) {
+        /* Setup for OSSL_SERIALIZE_to_bio() */
+        ctx->object = pkey;
+        ctx->do_output = serializer_EVP_PKEY_to_bio;
+    }
+
+    return ctx;
+}
+
diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c
index 9517169b8b..fbdb100c00 100644
--- a/crypto/x509/x_all.c
+++ b/crypto/x509/x_all.c
@@ -589,6 +589,30 @@ int i2d_PKCS8_bio(BIO *bp, const X509_SIG *p8)
     return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8);
 }
 
+#ifndef OPENSSL_NO_STDIO
+X509_PUBKEY *d2i_X509_PUBKEY_fp(FILE *fp, X509_PUBKEY **xpk)
+{
+    return ASN1_d2i_fp_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY,
+                          fp, xpk);
+}
+
+int i2d_X509_PUBKEY_fp(FILE *fp, const X509_PUBKEY *xpk)
+{
+    return ASN1_i2d_fp_of(X509_PUBKEY, i2d_X509_PUBKEY, fp, xpk);
+}
+#endif
+
+X509_PUBKEY *d2i_X509_PUBKEY_bio(BIO *bp, X509_PUBKEY **xpk)
+{
+    return ASN1_d2i_bio_of(X509_PUBKEY, X509_PUBKEY_new, d2i_X509_PUBKEY,
+                           bp, xpk);
+}
+
+int i2d_X509_PUBKEY_bio(BIO *bp, const X509_PUBKEY *xpk)
+{
+    return ASN1_i2d_bio_of(X509_PUBKEY, i2d_X509_PUBKEY, bp, xpk);
+}
+
 #ifndef OPENSSL_NO_STDIO
 PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
                                                 PKCS8_PRIV_KEY_INFO **p8inf)
diff --git a/doc/internal/man3/ossl_namemap_new.pod b/doc/internal/man3/ossl_namemap_new.pod
index b66fd91957..e041510c4a 100644
--- a/doc/internal/man3/ossl_namemap_new.pod
+++ b/doc/internal/man3/ossl_namemap_new.pod
@@ -3,7 +3,7 @@
 =head1 NAME
 
 ossl_namemap_new, ossl_namemap_free, ossl_namemap_stored, ossl_namemap_empty,
-ossl_namemap_add, ossl_namemap_add_n,
+ossl_namemap_add_name, ossl_namemap_add_name_n, ossl_namemap_add_names,
 ossl_namemap_name2num, ossl_namemap_name2num_n,
 ossl_namemap_doall_names
 - internal number E<lt>-E<gt> name map
@@ -18,9 +18,9 @@ ossl_namemap_doall_names
  void ossl_namemap_free(OSSL_NAMEMAP *namemap);
  int ossl_namemap_empty(OSSL_NAMEMAP *namemap);
 
- int ossl_namemap_add(OSSL_NAMEMAP *namemap, int number, const char *name);
- int ossl_namemap_add_n(OSSL_NAMEMAP *namemap, int number,
-                        const char *name, size_t name_len);
+ int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, const char *name);
+ int ossl_namemap_add_name_n(OSSL_NAMEMAP *namemap, int number,
+                             const char *name, size_t name_len);
 
  int ossl_namemap_name2num(const OSSL_NAMEMAP *namemap, const char *name);
  int ossl_namemap_name2num_n(const OSSL_NAMEMAP *namemap,
@@ -29,6 +29,9 @@ ossl_namemap_doall_names
                                void (*fn)(const char *name, void *data),
                                void *data);
 
+ int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number,
+                            const char *names, const char separator);
+
 =head1 DESCRIPTION
 
 A B<OSSL_NAMEMAP> is a one-to-many number E<lt>-E<gt> names map, which
@@ -49,7 +52,7 @@ given library context.
 The returned B<OSSL_NAMEMAP> can't be destructed using
 ossl_namemap_free().
 
-ossl_namemap_add() adds a new name to the namemap if it's not already
+ossl_namemap_add_name() adds a new name to the namemap if it's not already
 present.
 If the given I<number> is zero, a new number will be allocated to
 identify this I<name>.
@@ -59,18 +62,23 @@ names already associated with that number.
 ossl_namemap_name2num() finds the number corresponding to the given
 I<name>.
 
-ossl_namemap_add_n() and ossl_namemap_name2num_n() do the same thing
-as ossl_namemap_add() and ossl_namemap_name2num(), but take a string
+ossl_namemap_add_name_n() and ossl_namemap_name2num_n() do the same thing
+as ossl_namemap_add_name() and ossl_namemap_name2num(), but take a string
 length I<name_len> as well, allowing the caller to use a fragment of
 a string as a name.
 
-
 ossl_namemap_doall_names() walks through all names associated with
 I<number> in the given I<namemap> and calls the function I<fn> for
 each of them.
 I<fn> is also passed the I<data> argument, which allows any caller to
 pass extra data for that function to use.
 
+ossl_namemap_add_names() divides up a set of names given in I<names>,
+separated by I<separator>, and adds each to the I<namemap>, all with
+the same number.  If some of them already exist in the I<namemap>,
+they must all have the same associated number, which will be adopted
+for any name that doesn't exist yet.
+
 =head1 RETURN VALUES
 
 ossl_namemap_new() and ossl_namemap_stored() return the pointer to a
@@ -79,8 +87,8 @@ B<OSSL_NAMEMAP>, or NULL on error.
 ossl_namemap_empty() returns 1 if the B<OSSL_NAMEMAP> is NULL or
 empty, or 0 if it's not empty.
 
-ossl_namemap_add() and ossl_namemap_add_n() return the number associated
-with the added string, or zero on error.
+ossl_namemap_add_name() and ossl_namemap_add_name_n() return the number
+associated with the added string, or zero on error.
 
 ossl_namemap_num2names() returns a pointer to a NULL-terminated list of
 pointers to the names corresponding to the given number, or NULL if
@@ -90,6 +98,9 @@ ossl_namemap_name2num() and ossl_namemap_name2num_n() return the number
 corresponding to the given name, or 0 if it's undefined in the given
 B<OSSL_NAMEMAP>.
 
+ossl_namemap_add_names() returns the number associated with the added
+names, or zero on error.
+
 =head1 NOTES
 
 The result from ossl_namemap_num2names() isn't thread safe, other threads
diff --git a/doc/man3/OSSL_SERIALIZER.pod b/doc/man3/OSSL_SERIALIZER.pod
new file mode 100644
index 0000000000..05b889bf60
--- /dev/null
+++ b/doc/man3/OSSL_SERIALIZER.pod
@@ -0,0 +1,129 @@
+=pod
+
+=head1 NAME
+
+OSSL_SERIALIZER,
+OSSL_SERIALIZER_fetch,
+OSSL_SERIALIZER_up_ref,
+OSSL_SERIALIZER_free,
+OSSL_SERIALIZER_provider,
+OSSL_SERIALIZER_properties,
+OSSL_SERIALIZER_is_a,
+OSSL_SERIALIZER_number,
+OSSL_SERIALIZER_do_all_provided,
+OSSL_SERIALIZER_names_do_all
+- Serializer method routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/serializer.h>
+
+ typedef struct ossl_serializer_st OSSL_SERIALIZER;
+
+ OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *ctx, const char *name,
+                                        const char *properties);
+ int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *serializer);
+ void OSSL_SERIALIZER_free(OSSL_SERIALIZER *serializer);
+ const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER
+                                               *serializer);
+ const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser);
+ int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *serializer,
+                          const char *name);
+ int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *serializer);
+ void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                      void (*fn)(OSSL_SERIALIZER *serializer,
+                                                 void *arg),
+                                      void *arg);
+ void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *serializer,
+                                   void (*fn)(const char *name, void *data),
+                                   void *data);
+
+=head1 DESCRIPTION
+
+=for comment Future development should also talk about deserialization
+
+B<OSSL_SERIALIZER> is a method for serializers, which know how to
+serialize an object of some kind to a serialized form, such as PEM,
+DER, or even human readable text.
+
+OSSL_SERIALIZER_fetch() looks for an algorithm within the provider that
+has been loaded into the B<OPENSSL_CTX> given by I<ctx>, having the
+name given by I<name> and the properties given by I<properties>.
+The I<name> determines what type of object the fetched serializer
+method is expected to be able to serialize, and the properties are
+used to determine the expected output type.
+For known properties and the values they may have, please have a look
+in L<provider-serializer(7)/Names and properties>.
+
+OSSL_SERIALIZER_up_ref() increments the reference count for the given
+I<serializer>.
+
+OSSL_SERIALIZER_free() decrements the reference count for the given
+I<serializer>, and when the count reaches zero, frees it.
+
+OSSL_SERIALIZER_provider() returns the provider of the given
+I<serializer>.
+
+OSSL_SERIALIZER_provider() returns the property definition associated
+with the given I<serializer>.
+
+OSSL_SERIALIZER_is_a() checks if I<serializer> is an implementation of an
+algorithm that's identifiable with I<name>.
+
+OSSL_SERIALIZER_number() returns the internal dynamic number assigned to
+the given I<serializer>.
+
+OSSL_SERIALIZER_names_do_all() traverses all names for the given
+I<serializer>, and calls I<fn> with each name and I<data>.
+
+OSSL_SERIALIZER_do_all_provided() traverses all serializer
+implementations by all activated providers in the library context
+I<libctx>, and for each of the implementations, calls I<fn> with the
+implementation method and I<data> as arguments.
+
+=head1 NOTES
+
+OSSL_SERIALIZER_fetch() may be called implicitly by other fetching
+functions, using the same library context and properties.
+Any other API that uses keys will typically do this.
+
+=head1 RETURN VALUES
+
+OSSL_SERIALIZER_fetch() returns a pointer to the key management
+implementation represented by an OSSL_SERIALIZER object, or NULL on
+error.
+
+OSSL_SERIALIZER_up_ref() returns 1 on success, or 0 on error.
+
+OSSL_SERIALIZER_free() doesn't return any value.
+
+OSSL_SERIALIZER_provider() returns a pointer to a provider object, or
+NULL on error.
+
+OSSL_SERIALIZER_properties() returns a pointer to a property
+definition string, or NULL on error.
+
+OSSL_SERIALIZER_is_a() returns 1 of I<serializer> was identifiable,
+otherwise 0.
+
+OSSL_SERIALIZER_number() returns an integer.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_SERIALIZER_CTX(3)>, L<OSSL_SERIALIZER_to_bio(3)>,
+L<OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(3)>, L<OPENSSL_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_SERIALIZER_CTX.pod b/doc/man3/OSSL_SERIALIZER_CTX.pod
new file mode 100644
index 0000000000..d446b842d0
--- /dev/null
+++ b/doc/man3/OSSL_SERIALIZER_CTX.pod
@@ -0,0 +1,94 @@
+=pod
+
+=head1 NAME
+
+OSSL_SERIALIZER_CTX,
+OSSL_SERIALIZER_CTX_new,
+OSSL_SERIALIZER_CTX_get_serializer,
+OSSL_SERIALIZER_settable_ctx_params,
+OSSL_SERIALIZER_CTX_set_params,
+OSSL_SERIALIZER_CTX_free
+- Serializer context routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/serializer.h>
+
+ typedef struct ossl_serializer_ctx_st OSSL_SERIALIZER_CTX;
+
+ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser);
+ const OSSL_SERIALIZER *
+ OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx);
+ const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser);
+ int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx,
+                                    const OSSL_PARAM params[]);
+ void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx);
+
+=head1 DESCRIPTION
+
+B<OSSL_SERIALIZER_CTX> is a context with which B<OSSL_SERIALIZER>
+operations are performed.  The context typically holds values, both
+internal and supplied by the application, which are useful for the
+implementations supplied by providers.
+
+OSSL_SERIALIZER_CTX_new() creates a B<OSSL_SERIALIZER_CTX> associated
+with the serializer I<ser>.  NULL is a valid I<ser>, the context will
+be created anyway, it's just not very useful.  This is intentional, to
+distinguish between errors in allocating the context or assigning it
+values on one hand, and the lack of serializer support on the other.
+
+=begin comment
+
+The above distinction makes it possible for other routines to sense if
+they need to report an error or fall back on other methods to
+serialize.
+
+=end comment
+
+OSSL_SERIALIZER_CTX_get_serializer() gets the serializer method
+currently associated with the context I<ctx>.
+
+OSSL_SERIALIZER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
+array of parameter descriptors.
+
+OSSL_SERIALIZER_CTX_set_params() attempts to set parameters specified
+with an L<OSSL_PARAM(3)> array I<params>.  Parameters that the
+implementation doesn't recognise should be ignored.
+
+OSSL_SERIALIZER_CTX_free() frees the given context I<ctx>.
+
+=head1 RETURN VALUES
+
+OSSL_SERIALIZER_CTX_new() returns a pointer to a
+B<OSSL_SERIALIZER_CTX>, or NULL if the context structure couldn't be
+allocated.
+
+OSSL_SERIALIZER_CTX_get_serializer() returns a pointer to the
+serializer method associated with I<ctx>.  NULL is a valid return
+value and signifies that there is no associated serializer method.
+
+OSSL_SERIALIZER_settable_ctx_params() returns an L<OSSL_PARAM(3)>
+array, or NULL if none is available.
+
+OSSL_SERIALIZER_CTX_set_params() returns 1 if all recognised
+parameters were valid, or 0 if one of them was invalid or caused some
+other failure in the implementation.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_SERIALIZER(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
new file mode 100644
index 0000000000..caa9294bcc
--- /dev/null
+++ b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod
@@ -0,0 +1,134 @@
+=pod
+
+=head1 NAME
+
+OSSL_SERIALIZER_CTX_new_by_EVP_PKEY,
+OSSL_SERIALIZER_CTX_set_cipher,
+OSSL_SERIALIZER_CTX_set_passphrase,
+OSSL_SERIALIZER_CTX_set_passphrase_cb,
+OSSL_SERIALIZER_CTX_set_passphrase_ui,
+OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ,
+OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ,
+OSSL_SERIALIZER_Parameters_TO_PEM_PQ,
+OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ,
+OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ,
+OSSL_SERIALIZER_Parameters_TO_TEXT_PQ
+- Serializer routines to serialize EVP_PKEYs
+
+=head1 SYNOPSIS
+
+ #include <openssl/serializer.h>
+
+ OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                                          const char *propquery);
+
+ int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx,
+                                    const char *cipher_name,
+                                    const char *propquery);
+ int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx,
+                                        const unsigned char *kstr,
+                                        size_t klen);
+ int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+                                           pem_password_cb *cb, void *cbarg);
+ int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
+                                           const UI_METHOD *ui_method,
+                                           void *ui_data);
+
+ #define OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ "format=pem,type=public"
+ #define OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ "format=pem,type=private"
+ #define OSSL_SERIALIZER_Parameters_TO_PEM_PQ "format=pem,type=domainparams"
+
+ #define OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ "format=text,type=public"
+ #define OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ "format=text,type=private"
+ #define OSSL_SERIALIZER_Parameters_TO_TEXT_PQ "format=text,type=domainparams"
+
+=head1 DESCRIPTION
+
+OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() creates a B<OSSL_SERIALIZER_CTX>
+with a suitable attached output routine for B<EVP_PKEY>s.  It will
+search for a serializer implementation that matches the algorithm of
+the B<EVP_PKEY> and the property query given with I<propquery>.  It
+will prefer to find a serializer from the same provider as the key
+data of the B<EVP_PKEY> itself, but failing that, it will choose the
+first serializer that supplies a generic serializing function.
+
+If no suitable serializer was found, OSSL_SERIALIZER_CTX_new_by_EVP_PKEY()
+still creates a B<OSSL_SERIALIZER_CTX>, but with no associated
+serializer (L<OSSL_SERIALIZER_CTX_get_serializer(3)> returns NULL).
+This helps the caller distinguish between an error when creating
+the B<OSSL_SERIALIZER_CTX>, and the lack the serializer support and
+act accordingly.
+
+OSSL_SERIALIZER_CTX_set_cipher() tells the implementation what cipher
+should be used to encrypt serialized keys.  The cipher is given by
+name I<cipher_name>.  The interpretation of that I<cipher_name> is
+implementation dependent.  The implementation may implement the digest
+directly itself or by other implementations, or it may choose to fetch
+it.  If the implementation supports fetching the cipher, then it may
+use I<propquery> as properties to be queried for when fetching.
+I<cipher_name> may also be NULL, which will result in unencrypted
+serialization.
+
+OSSL_SERIALIZER_CTX_set_passphrase() gives the implementation a
+pass phrase to use when encrypting the serialized private key.
+Alternatively, a pass phrase callback may be specified with the
+following functions.
+
+OSSL_SERIALIZER_CTX_set_passphrase_cb() and
+OSSL_SERIALIZER_CTX_set_passphrase_ui() sets up a callback method that
+the implementation can use to prompt for a pass phrase.
+
+=for comment Note that the callback method is called indirectly,
+through an internal B<OSSL_PASSPHRASE_CALLBACK> function.
+
+The macros B<OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ>,
+B<OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ>,
+B<OSSL_SERIALIZER_Parameters_TO_PEM_PQ>,
+B<OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ>,
+B<OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ>,
+B<OSSL_SERIALIZER_Parameters_TO_TEXT_PQ> are convenience macros with
+property queries to serialize the B<EVP_PKEY> as a public key, private
+key or parameters to B<PEM>, or to text.
+
+=head1 RETURN VALUES
+
+OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() returns a pointer to a
+B<OSSL_SERIALIZER_CTX>, or NULL if it couldn't be created.
+
+OSSL_SERIALIZER_CTX_set_cipher(),
+OSSL_SERIALIZER_CTX_set_passphrase(),
+OSSL_SERIALIZER_CTX_set_passphrase_cb(), and
+OSSL_SERIALIZER_CTX_set_passphrase_ui() all return 1 on success, or 0
+on failure.
+
+=head1 NOTES
+
+Parts of the function and macro names are made to match already
+existing OpenSSL names.
+
+B<EVP_PKEY> in OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() matches the type
+name, thus making for the naming pattern
+B<OSSL_SERIALIZER_CTX_new_by_I<TYPE>>() when new types are handled.
+
+B<PUBKEY>, B<PrivateKey> and B<Parameters> in the macro names match
+the B<I<TYPE>> part of of B<PEM_write_bio_I<TYPE>> functions as well
+as B<i2d_I<TYPE>_bio> functions.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_SERIALIZER(3)>, L<OSSL_SERIALIZER_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_SERIALIZER_to_bio.pod b/doc/man3/OSSL_SERIALIZER_to_bio.pod
new file mode 100644
index 0000000000..3ed68a17ed
--- /dev/null
+++ b/doc/man3/OSSL_SERIALIZER_to_bio.pod
@@ -0,0 +1,59 @@
+=pod
+
+=head1 NAME
+
+OSSL_SERIALIZER_to_bio,
+OSSL_SERIALIZER_to_fp
+- Serializer file output routines
+
+=head1 SYNOPSIS
+
+ #include <openssl/serializer.h>
+
+ int OSSL_SERIALIZER_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out);
+ int OSSL_SERIALIZER_to_fp(OSSL_SERIALIZER_CTX *ctx, FILE *fp);
+
+Feature availability macros:
+
+=over 4
+
+=item OSSL_SERIALIZER_to_fp() is only available when B<OPENSSL_NO_STDIO>
+is undefined.
+
+=back
+
+=head1 DESCRIPTION
+
+OSSL_SERIALIZER_to_bio() runs the serialization process for the
+context I<ctx>, with the output going to the B<BIO> I<out>.  The
+application is required to set up the B<BIO> properly, for example to
+have it in text or binary mode if that's appropriate.
+
+=for comment Know your serializer!
+
+OSSL_SERIALIZER_to_fp() does the same thing as OSSL_SERIALIZER_to_bio(),
+except that the output is going to the B<FILE> I<fp>.
+
+=head1 RETURN VALUES
+
+OSSL_SERIALIZER_to_bio() and OSSL_SERIALIZER_to_fp() return 1 on
+success, or 0 on failure.
+
+=head1 SEE ALSO
+
+L<provider(7)>, L<OSSL_SERIALIZER_CTX(3)>
+
+=head1 HISTORY
+
+The functions described here were added in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/PEM_read_CMS.pod b/doc/man3/PEM_read_CMS.pod
index 5cc3251753..58c357516e 100644
--- a/doc/man3/PEM_read_CMS.pod
+++ b/doc/man3/PEM_read_CMS.pod
@@ -35,7 +35,11 @@ PEM_write_bio_PKCS8_PRIV_KEY_INFO,
 PEM_read_SSL_SESSION,
 PEM_read_bio_SSL_SESSION,
 PEM_write_SSL_SESSION,
-PEM_write_bio_SSL_SESSION
+PEM_write_bio_SSL_SESSION,
+PEM_read_X509_PUBKEY,
+PEM_read_bio_X509_PUBKEY,
+PEM_write_X509_PUBKEY,
+PEM_write_bio_X509_PUBKEY
 - PEM object encoding routines
 
 =head1 SYNOPSIS
diff --git a/doc/man3/RSA_get0_key.pod b/doc/man3/RSA_get0_key.pod
index 8515f6edfb..a947c39643 100644
--- a/doc/man3/RSA_get0_key.pod
+++ b/doc/man3/RSA_get0_key.pod
@@ -5,7 +5,7 @@
 RSA_set0_key, RSA_set0_factors, RSA_set0_crt_params, RSA_get0_key,
 RSA_get0_factors, RSA_get0_crt_params,
 RSA_get0_n, RSA_get0_e, RSA_get0_d, RSA_get0_p, RSA_get0_q,
-RSA_get0_dmp1, RSA_get0_dmq1, RSA_get0_iqmp,
+RSA_get0_dmp1, RSA_get0_dmq1, RSA_get0_iqmp, RSA_get0_pss_params,
 RSA_clear_flags,
 RSA_test_flags, RSA_set_flags, RSA_get0_engine, RSA_get_multi_prime_extra_count,
 RSA_get0_multi_prime_factors, RSA_get0_multi_prime_crt_params,
@@ -33,6 +33,7 @@ RSA_set0_multi_prime_params, RSA_get_version
  const BIGNUM *RSA_get0_dmp1(const RSA *r);
  const BIGNUM *RSA_get0_dmq1(const RSA *r);
  const BIGNUM *RSA_get0_iqmp(const RSA *r);
+ const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *r);
  void RSA_clear_flags(RSA *r, int flags);
  int RSA_test_flags(const RSA *r, int flags);
  void RSA_set_flags(RSA *r, int flags);
@@ -98,6 +99,8 @@ retrieved separately by the corresponding function
 RSA_get0_n(), RSA_get0_e(), RSA_get0_d(), RSA_get0_p(), RSA_get0_q(),
 RSA_get0_dmp1(), RSA_get0_dmq1(), and RSA_get0_iqmp(), respectively.
 
+RSA_get0_pss_params() is used to retrieve the RSA-PSS parameters.
+
 RSA_set_flags() sets the flags in the B<flags> parameter on the RSA
 object. Multiple flags can be passed in one go (bitwise ORed together).
 Any flags that are already set are left set. RSA_test_flags() tests to
@@ -136,6 +139,9 @@ RSA_get0_n(), RSA_get0_e(), RSA_get0_d(), RSA_get0_p(), RSA_get0_q(),
 RSA_get0_dmp1(), RSA_get0_dmq1(), and RSA_get0_iqmp()
 return the respective value.
 
+RSA_get0_pss_params() returns a B<RSA_PSS_PARAMS> pointer, or NULL if
+there is none.
+
 RSA_get0_multi_prime_factors() and RSA_get0_multi_prime_crt_params() return
 1 on success or 0 on failure.
 
diff --git a/doc/man3/d2i_X509.pod b/doc/man3/d2i_X509.pod
index 62e6b245a7..8bb89e0332 100644
--- a/doc/man3/d2i_X509.pod
+++ b/doc/man3/d2i_X509.pod
@@ -184,6 +184,8 @@ d2i_X509_EXTENSIONS,
 d2i_X509_NAME,
 d2i_X509_NAME_ENTRY,
 d2i_X509_PUBKEY,
+d2i_X509_PUBKEY_bio,
+d2i_X509_PUBKEY_fp,
 d2i_X509_REQ,
 d2i_X509_REQ_INFO,
 d2i_X509_REQ_bio,
@@ -376,6 +378,8 @@ i2d_X509_EXTENSIONS,
 i2d_X509_NAME,
 i2d_X509_NAME_ENTRY,
 i2d_X509_PUBKEY,
+i2d_X509_PUBKEY_bio,
+i2d_X509_PUBKEY_fp,
 i2d_X509_REQ,
 i2d_X509_REQ_INFO,
 i2d_X509_REQ_bio,
diff --git a/doc/man7/openssl-core.h.pod b/doc/man7/openssl-core.h.pod
index b5323e9d21..28307a97d4 100644
--- a/doc/man7/openssl-core.h.pod
+++ b/doc/man7/openssl-core.h.pod
@@ -76,7 +76,7 @@ B<OSSL_PARAM> is further described in L<OSSL_PARAM(3)>
 
 =item B<OSSL_CALLBACK>
 
-This is a function type for a generic callback function:
+This is a function type for a generic feedback callback function:
 
     typedef int (OSSL_CALLBACK)(const OSSL_PARAM params[], void *arg);
 
@@ -86,6 +86,27 @@ expected to build an B<OSSL_PARAM> array of data it wants or is
 expected to pass back, and pass that as I<params>, as well as
 the caller data pointer it received, as I<arg>.
 
+=item B<OSSL_PASSPHRASE_CALLBACK>
+
+This is a function type for a generic pass phrase callback function:
+
+    typedef int (OSSL_PASSPHRASE_CALLBACK)(char *pass, size_t pass_size,
+                                           size_t *pass_len,
+                                           const OSSL_PARAM params[],
+                                           void *arg);
+
+This callback can be used to prompt the user for a passphrase.  When
+calling it, a buffer to store the pass phrase needs to be given with
+I<pass>, and its size with I<pass_size>.  The length of the prompted
+pass phrase will be given back in I<*pass_len>.
+
+Additional parameters can be passed with the B<OSSL_PARAM> array
+I<params>.
+
+A function that takes a pointer of this type should also take a
+pointer to caller data, which should be passed as I<arg> to this
+callback.
+
 =back
 
 =head1 SEE ALSO
diff --git a/doc/man7/provider-base.pod b/doc/man7/provider-base.pod
index 08d807f572..bb82132185 100644
--- a/doc/man7/provider-base.pod
+++ b/doc/man7/provider-base.pod
@@ -113,6 +113,7 @@ provider):
  BIO_new_mem_buf                OSSL_FUNC_BIO_NEW_MEMBUF
  BIO_read_ex                    OSSL_FUNC_BIO_READ_EX
  BIO_free                       OSSL_FUNC_BIO_FREE
+ BIO_vprintf                    OSSL_FUNC_BIO_VPRINTF
  OPENSSL_cleanse                OSSL_FUNC_OPENSSL_CLEANSE
  OPENSSL_hexstr2buf             OSSL_FUNC_OPENSSL_HEXSTR2BUF
 
@@ -184,8 +185,8 @@ CRYPTO_realloc(), CRYPTO_clear_realloc(), CRYPTO_secure_malloc(),
 CRYPTO_secure_zalloc(), CRYPTO_secure_free(),
 CRYPTO_secure_clear_free(), CRYPTO_secure_allocated(), CRYPTO_mem_ctrl(),
 BIO_new_file(), BIO_new_mem_buf(), BIO_read_ex(), BIO_free(),
-OPENSSL_cleanse(), and OPENSSL_hexstr2buf() correspond exactly to the
-public functions with the same name.
+BIO_vprintf(), OPENSSL_cleanse(), and OPENSSL_hexstr2buf()
+correspond exactly to the public functions with the same name.
 As a matter of fact, the pointers in the B<OSSL_DISPATCH> array are
 direct pointers to those public functions.
 
diff --git a/doc/man7/provider-serializer.pod b/doc/man7/provider-serializer.pod
new file mode 100644
index 0000000000..b23a8b64e2
--- /dev/null
+++ b/doc/man7/provider-serializer.pod
@@ -0,0 +1,276 @@
+=pod
+
+=head1 NAME
+
+provider-serializer - The SERIALIZER library E<lt>-E<gt> provider functions
+
+=head1 SYNOPSIS
+
+=begin comment
+
+Future development will also include deserializing functions.
+
+=end comment
+
+ #include <openssl/core_numbers.h>
+
+ /*
+  * None of these are actual functions, but are displayed like this for
+  * the function signatures for functions that are offered as function
+  * pointers in OSSL_DISPATCH arrays.
+  */
+
+ /* Functions to construct / destruct / manipulate the serializer context */
+ void *OP_serializer_newctx(void *provctx);
+ void OP_serializer_freectx(void *ctx);
+ int OP_serializer_set_ctx_params(void *ctx, const OSSL_PARAM params[]);
+ const OSSL_PARAM *OP_serializer_settable_ctx_params(void)
+
+ /* Functions to serialize object data */
+ int OP_serializer_serialize_data(void *ctx, const OSSL_PARAM *data,
+                                  BIO *out,
+                                  OSSL_PASSPHRASE_CALLBACK *cb,
+                                  void *cbarg);
+ int OP_serializer_serialize_object(void *ctx, void *obj, BIO *out,
+                                    OSSL_PASSPHRASE_CALLBACK *cb,
+                                    void *cbarg);
+
+=head1 DESCRIPTION
+
+The SERIALIZER is a generic method to serialize any set of object data
+in L<OSSL_PARAM(3)> array form, or any provider side object into
+serialized form, and write it to the given BIO.  If the caller wants
+to get the serialized stream to memory, it should provide a
+L<BIO_s_membuf(3)>.
+
+The serializer doesn't need to know more about the B<BIO> pointer than
+being able to pass it to the appropriate BIO upcalls (see
+L<provider-base(7)/Core functions>).
+
+The serialization using the L<OSSL_PARAM(3)> array form allows a
+serializer to be used for data that's been exported from another
+provider, and thereby allow them to exist independently of each
+other.
+
+The serialization using a provider side object can only be safely used
+with provider data coming from the same provider, for example keys
+with the L<KEYMGMT|provider-keymgmt(7)> provider.
+
+All "functions" mentioned here are passed as function pointers between
+F<libcrypto> and the provider in B<OSSL_DISPATCH> arrays via
+B<OSSL_ALGORITHM> arrays that are returned by the provider's
+provider_query_operation() function
+(see L<provider-base(7)/Provider Functions>).
+
+All these "functions" have a corresponding function type definition
+named B<OSSL_{name}_fn>, and a helper function to retrieve the
+function pointer from a B<OSSL_DISPATCH> element named
+B<OSSL_get_{name}>.
+For example, the "function" OP_serializer_serialize_data() has these:
+
+ typedef int
+     (OSSL_OP_serializer_serialize_data_fn)(void *provctx,
+                                            const OSSL_PARAM params[],
+                                            BIO *out);
+ static ossl_inline OSSL_OP_serializer_serialize_data_fn
+     OSSL_get_OP_serializer_serialize_data(const OSSL_DISPATCH *opf);
+
+B<OSSL_DISPATCH> arrays are indexed by numbers that are provided as
+macros in L<openssl-core_numbers.h(7)>, as follows:
+
+ OP_serializer_newctx              OSSL_FUNC_SERIALIZER_NEWCTX
+ OP_serializer_freectx             OSSL_FUNC_SERIALIZER_FREECTX
+ OP_serializer_set_ctx_params      OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS
+ OP_serializer_settable_ctx_params OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS
+
+ OP_serializer_serialize_data      OSSL_FUNC_SERIALIZER_SERIALIZE_DATA
+ OP_serializer_serialize_object    OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT
+
+=head2 Names and properties
+
+The name of an implementation should match the type of object it
+handles.  For example, an implementation that serializes an RSA key
+should be named accordingly.
+
+To be able to specify exactly what serialization format and what type
+of data a serializer implementation is expected to handle, two
+additional properties may be given:
+
+=over 4
+
+=item format
+
+This property is used to specify what kind of output format the
+implementation produces.  Currently known formats are:
+
+=over 4
+
+=item text
+
+An implementation with that format property value outputs human
+readable text, making that implementation suitable for C<-text> output
+in diverse L<openssl(1)> commands.
+
+=item pem
+
+An implementation with that format property value outputs PEM
+formatted data.
+
+=item der
+
+An implementation with that format property value outputs DER
+formatted data.
+
+=back
+
+=item type
+
+With objects that have multiple purposes, this can be used to specify
+the purpose type.  The currently known use cases are asymmetric keys
+and domain parameters, where the type can be one of:
+
+=over 4
+
+=item private
+
+An implementation with that format property value outputs a private
+key.
+
+=item public
+
+An implementation with that format property value outputs a public
+key.
+
+=item domainparams
+
+An implementation with that format property value outputs domain
+parameters.
+
+=back
+
+=back
+
+The possible values of both these properties is open ended.  A
+provider may very well specify other formats that libcrypto doesn't
+know anything about.
+
+=head2 Context functions
+
+OP_serializer_newctx() returns a context to be used with the rest of
+the functions.
+
+OP_serializer_freectx() frees the given I<ctx>, if it was created by
+OP_serializer_newctx().
+
+OP_serializer_set_ctx_params() sets context data according to
+parameters from I<params> that it recognises.  Unrecognised parameters
+should be ignored.
+
+OP_serializer_settable_ctx_params() returns a constant B<OSSL_PARAM>
+array describing the parameters that OP_serializer_set_ctx_params()
+can handle.
+
+See L<OSSL_PARAM(3)> for further details on the parameters structure used
+by OP_serializer_set_ctx_params() and OP_serializer_settable_ctx_params().
+
+=head2 Serializing functions
+
+=for comment There will be a "Deserializing functions" title as well
+
+OP_serializer_serialize_data() should take an array of B<OSSL_PARAM>,
+I<data>, and if it contains the data necessary for the object type
+that the implementation handles, it should output the object in
+serialized form to the B<BIO>.
+
+OP_serializer_serialize_object() should take a pointer to an object
+that it knows intimately, and output that object in serialized form to
+the B<BIO>.  The caller I<must> ensure that this function is called
+with a pointer that the provider of this function is familiar with.
+It is not suitable to use with object pointers coming from other
+providers.
+
+Both serialization functions also take an B<OSSL_PASSPHRASE_CALLBACK>
+function pointer along with a pointer to application data I<cbarg>,
+which should be used when a pass phrase prompt is needed.
+
+=head2 Serializer parameters
+
+Parameters currently recognised by built-in serializers are as
+follows:
+
+=over 4
+
+=item "cipher" (B<OSSL_SERIALIZER_PARAM_CIPHER>) <UTF8 string>
+
+The name of the encryption cipher to be used when generating encrypted
+serialization.  This is used when serializing private keys, as well as
+other objects that need protection.
+
+If this name is invalid for the serialization implementation, the
+implementation should refuse to perform the serialization, i.e.
+OP_serializer_serialize_data() and OP_serializer_serialize_object()
+should return an error.
+
+=item "properties" (B<OSSL_SERIALIZER_PARAM_PROPERTIES>) <UTF8 string>
+
+The properties to be queried when trying to fetch the algorithm given
+with the "cipher" parameter.
+This must be given together with the "cipher" parameter to be
+considered valid.
+
+The serialization implementation isn't obligated to use this value.
+However, it is recommended that implementations that do not handle
+property strings return an error on receiving this parameter unless
+its value NULL or the empty string.
+
+=item "passphrase" (B<OSSL_SERIALIZER_PARAM_PASS>) <octet string>
+
+A pass phrase provided by the application.  When this is given, the
+built-in serializers will not attempt to use the passphrase callback.
+
+=back
+
+Parameters currently recognised by the built-in pass phrase callback:
+
+=over 4
+
+=item "info" (B<OSSL_PASSPHRASE_PARAM_INFO>) <UTF8 string>
+
+A string of information that will become part of the pass phrase
+prompt.  This could be used to give the user information on what kind
+of object it's being prompted for.
+
+=back
+
+=head1 RETURN VALUES
+
+OP_serializer_newctx() returns a pointer to a context, or NULL on
+failure.
+
+OP_serializer_set_ctx_params() returns 1, unless a recognised
+parameters was invalid or caused an error, for which 0 is returned.
+
+OP_serializer_settable_ctx_params() returns a pointer to an array of
+constant B<OSSL_PARAM> elements.
+
+OP_serializer_serialize_data() and OP_serializer_serialize_object()
+return 1 on success, or 0 on failure.
+
+=head1 SEE ALSO
+
+L<provider(7)>
+
+=head1 HISTORY
+
+The SERIALIZER interface was introduced in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/provider.pod b/doc/man7/provider.pod
index c3405b6f22..b6c5e49f50 100644
--- a/doc/man7/provider.pod
+++ b/doc/man7/provider.pod
@@ -147,6 +147,14 @@ The number for this operation is B<OSSL_OP_KEYEXCH>.
 The functions the provider can offer are described in
 L<provider-keyexch(7)>
 
+=item Serialization
+
+In the OpenSSL libraries, the corresponding method object is
+B<OSSL_SERIALIZER>.
+The number for this operation is B<OSSL_OP_SERIALIZER>.
+The functions the provider can offer are described in
+L<provider-serializer(7)>
+
 =back
 
 =head2 Fetching algorithms
diff --git a/include/crypto/serializer.h b/include/crypto/serializer.h
new file mode 100644
index 0000000000..c40788f78b
--- /dev/null
+++ b/include/crypto/serializer.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/types.h>
+
+OSSL_SERIALIZER *ossl_serializer_fetch_by_number(OPENSSL_CTX *libctx, int id,
+                                                 const char *properties);
diff --git a/include/internal/core.h b/include/internal/core.h
index d2229e173b..ca04333486 100644
--- a/include/internal/core.h
+++ b/include/internal/core.h
@@ -38,8 +38,8 @@ typedef struct ossl_method_construct_method_st {
                const OSSL_PROVIDER *prov, int operation_id, const char *name,
                const char *propdef, void *data);
     /* Construct a new method */
-    void *(*construct)(const char *name, const OSSL_DISPATCH *fns,
-                       OSSL_PROVIDER *prov, void *data);
+    void *(*construct)(const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov,
+                       void *data);
     /* Destruct a method */
     void (*destruct)(void *method, void *data);
 } OSSL_METHOD_CONSTRUCT_METHOD;
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index 7428453a35..8be3861d4f 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -154,7 +154,8 @@ typedef struct ossl_ex_data_global_st {
 # define OPENSSL_CTX_RAND_CRNGT_INDEX               7
 # define OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX     8
 # define OPENSSL_CTX_FIPS_PROV_INDEX                9
-# define OPENSSL_CTX_MAX_INDEXES                   10
+# define OPENSSL_CTX_SERIALIZER_STORE_INDEX        10
+# define OPENSSL_CTX_MAX_INDEXES                   11
 
 typedef struct openssl_ctx_method {
     void *(*new_func)(OPENSSL_CTX *ctx);
diff --git a/include/internal/namemap.h b/include/internal/namemap.h
index f977606ca6..d33f4b0d4e 100644
--- a/include/internal/namemap.h
+++ b/include/internal/namemap.h
@@ -17,9 +17,9 @@ OSSL_NAMEMAP *ossl_namemap_new(void);
 void ossl_namemap_free(OSSL_NAMEMAP *namemap);
 int ossl_namemap_empty(OSSL_NAMEMAP *namemap);
 
-int ossl_namemap_add(OSSL_NAMEMAP *namemap, int number, const char *name);
-int ossl_namemap_add_n(OSSL_NAMEMAP *namemap, int number,
-                       const char *name, size_t name_len);
+int ossl_namemap_add_name(OSSL_NAMEMAP *namemap, int number, const char *name);
+int ossl_namemap_add_name_n(OSSL_NAMEMAP *namemap, int number,
+                            const char *name, size_t name_len);
 
 /*
  * The number<->name relationship is 1<->many
@@ -34,3 +34,10 @@ const char *ossl_namemap_num2name(const OSSL_NAMEMAP *namemap, int number,
 void ossl_namemap_doall_names(const OSSL_NAMEMAP *namemap, int number,
                               void (*fn)(const char *name, void *data),
                               void *data);
+
+/*
+ * A utility that handles several names in a string, divided by a given
+ * separator.
+ */
+int ossl_namemap_add_names(OSSL_NAMEMAP *namemap, int number,
+                           const char *names, const char separator);
diff --git a/include/internal/property.h b/include/internal/property.h
index 7ffd588073..1c40351e83 100644
--- a/include/internal/property.h
+++ b/include/internal/property.h
@@ -14,10 +14,19 @@
 #include "internal/cryptlib.h"
 
 typedef struct ossl_method_store_st OSSL_METHOD_STORE;
+typedef struct ossl_property_list_st OSSL_PROPERTY_LIST;
 
 /* Initialisation */
 int ossl_property_parse_init(OPENSSL_CTX *ctx);
 
+/* Property definition parser */
+OSSL_PROPERTY_LIST *ossl_parse_property(OPENSSL_CTX *ctx, const char *defn);
+/* Property query parser */
+OSSL_PROPERTY_LIST *ossl_parse_query(OPENSSL_CTX *ctx, const char *s);
+/* Property checker of query vs definition */
+int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
+                              const OSSL_PROPERTY_LIST *defn);
+
 /* Implementation store functions */
 OSSL_METHOD_STORE *ossl_method_store_new(OPENSSL_CTX *ctx);
 void ossl_method_store_free(OSSL_METHOD_STORE *store);
diff --git a/include/openssl/core.h b/include/openssl/core.h
index bed580c811..5959a31880 100644
--- a/include/openssl/core.h
+++ b/include/openssl/core.h
@@ -202,6 +202,16 @@ extern OSSL_provider_init_fn OSSL_provider_init;
  */
 typedef int (OSSL_CALLBACK)(const OSSL_PARAM params[], void *arg);
 
+/*
+ * Passphrase callback function signature
+ *
+ * This is similar to the generic callback function above, but adds a
+ * result parameter.
+ */
+typedef int (OSSL_PASSPHRASE_CALLBACK)(char *pass, size_t pass_size,
+                                       size_t *pass_len,
+                                       const OSSL_PARAM params[], void *arg);
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 1e8b764fb4..053432e0f0 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -202,6 +202,17 @@ extern "C" {
 #define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL           "oaep-label"
 #define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL_LEN       "oaep-label-len"
 
+/*
+ * Serializer parameters
+ */
+/* The passphrase may be passed as a utf8 string or an octet string */
+#define OSSL_SERIALIZER_PARAM_CIPHER            OSSL_ALG_PARAM_CIPHER
+#define OSSL_SERIALIZER_PARAM_PROPERTIES        OSSL_ALG_PARAM_PROPERTIES
+#define OSSL_SERIALIZER_PARAM_PASS              "passphrase"
+
+/* Passphrase callback parameters */
+#define OSSL_PASSPHRASE_PARAM_INFO              "info"
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h
index d07ef556d9..2d9c0f2f1c 100644
--- a/include/openssl/core_numbers.h
+++ b/include/openssl/core_numbers.h
@@ -127,12 +127,15 @@ OSSL_CORE_MAKE_FUNC(int, CRYPTO_mem_ctrl, (int mode))
 #define OSSL_FUNC_BIO_NEW_MEMBUF              24
 #define OSSL_FUNC_BIO_READ_EX                 25
 #define OSSL_FUNC_BIO_FREE                    26
+#define OSSL_FUNC_BIO_VPRINTF                 27
 
 OSSL_CORE_MAKE_FUNC(BIO *, BIO_new_file, (const char *filename, const char *mode))
 OSSL_CORE_MAKE_FUNC(BIO *, BIO_new_membuf, (const void *buf, int len))
 OSSL_CORE_MAKE_FUNC(int, BIO_read_ex, (BIO *bio, void *data, size_t data_len,
                                        size_t *bytes_read))
 OSSL_CORE_MAKE_FUNC(int, BIO_free, (BIO *bio))
+OSSL_CORE_MAKE_FUNC(int, BIO_vprintf, (BIO *bio, const char *format,
+                                       va_list args))
 
 /* Functions provided by the provider to the Core, reserved numbers 1024-1535 */
 # define OSSL_FUNC_PROVIDER_TEARDOWN         1024
@@ -160,8 +163,10 @@ OSSL_CORE_MAKE_FUNC(const OSSL_ITEM *,provider_get_reason_strings,
 # define OSSL_OP_KEYEXCH                            11
 # define OSSL_OP_SIGNATURE                          12
 # define OSSL_OP_ASYM_CIPHER                        13
+/* New section for non-EVP operations */
+# define OSSL_OP_SERIALIZER                         20
 /* Highest known operation number */
-# define OSSL_OP__HIGHEST                           13
+# define OSSL_OP__HIGHEST                           20
 
 /* Digests */
 
@@ -534,6 +539,27 @@ OSSL_CORE_MAKE_FUNC(int, OP_asym_cipher_set_ctx_params,
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_asym_cipher_settable_ctx_params,
                     (void))
 
+/* Serializers */
+# define OSSL_FUNC_SERIALIZER_NEWCTX                1
+# define OSSL_FUNC_SERIALIZER_FREECTX               2
+# define OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS        3
+# define OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS   4
+# define OSSL_FUNC_SERIALIZER_SERIALIZE_DATA       10
+# define OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT     11
+OSSL_CORE_MAKE_FUNC(void *, OP_serializer_newctx, (void *provctx))
+OSSL_CORE_MAKE_FUNC(void, OP_serializer_freectx, (void *ctx))
+OSSL_CORE_MAKE_FUNC(int, OP_serializer_set_ctx_params,
+                    (void *ctx, const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_serializer_settable_ctx_params,
+                    (void))
+
+OSSL_CORE_MAKE_FUNC(int, OP_serializer_serialize_data,
+                    (void *ctx, const OSSL_PARAM[], BIO *out,
+                     OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg))
+OSSL_CORE_MAKE_FUNC(int, OP_serializer_serialize_object,
+                    (void *ctx, void *obj, BIO *out,
+                     OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg))
+
 # ifdef __cplusplus
 }
 # endif
diff --git a/include/openssl/cryptoerr.h b/include/openssl/cryptoerr.h
index 43b7aa5a22..ae146c4079 100644
--- a/include/openssl/cryptoerr.h
+++ b/include/openssl/cryptoerr.h
@@ -83,6 +83,8 @@ int ERR_load_CRYPTO_strings(void);
 /*
  * CRYPTO reason codes.
  */
+# define CRYPTO_R_BAD_ALGORITHM_NAME                      117
+# define CRYPTO_R_CONFLICTING_NAMES                       118
 # define CRYPTO_R_FIPS_MODE_NOT_SUPPORTED                 101
 # define CRYPTO_R_ILLEGAL_HEX_DIGIT                       102
 # define CRYPTO_R_INSUFFICIENT_DATA_SPACE                 106
diff --git a/include/openssl/err.h b/include/openssl/err.h
index 9244bb84b1..96b60882f0 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -112,6 +112,7 @@ struct err_state_st {
 # define ERR_LIB_CRMF            56
 # define ERR_LIB_PROV            57
 # define ERR_LIB_CMP             58
+# define ERR_LIB_OSSL_SERIALIZER 59
 
 # define ERR_LIB_USER            128
 
@@ -231,6 +232,8 @@ struct err_state_st {
 # define ERR_R_INIT_FAIL                         (6|ERR_R_FATAL)
 # define ERR_R_PASSED_INVALID_ARGUMENT           (7)
 # define ERR_R_OPERATION_FAIL                    (8|ERR_R_FATAL)
+# define ERR_R_INVALID_PROVIDER_FUNCTIONS        (9|ERR_R_FATAL)
+# define ERR_R_INTERRUPTED_OR_CANCELLED          (10)
 
 /*
  * 99 is the maximum possible ERR_R_... code, higher values are reserved for
diff --git a/include/openssl/pem.h b/include/openssl/pem.h
index ef79c1a46a..e48d247087 100644
--- a/include/openssl/pem.h
+++ b/include/openssl/pem.h
@@ -66,6 +66,14 @@ extern "C" {
  * IMPLEMENT_PEM_rw_cb(...)
  */
 
+# define PEM_write_fnsig(name, type, OUTTYPE, writename)        \
+    int PEM_##writename##_##name(OUTTYPE *out, const type *x)
+# define PEM_write_cb_fnsig(name, type, OUTTYPE, writename)             \
+    int PEM_##writename##_##name(OUTTYPE *out, const type *x,           \
+                                 const EVP_CIPHER *enc,                 \
+                                 const unsigned char *kstr, int klen,   \
+                                 pem_password_cb *cb, void *u)
+
 # ifdef OPENSSL_NO_STDIO
 
 #  define IMPLEMENT_PEM_read_fp(name, type, str, asn1) /**/
@@ -87,9 +95,9 @@ extern "C" {
     }
 
 #  define IMPLEMENT_PEM_write_fp(name, type, str, asn1)                 \
-    int PEM_write_##name(FILE *fp, const type *x)                       \
+    PEM_write_fnsig(name, type, FILE, write)                            \
     {                                                                   \
-        return PEM_ASN1_write((i2d_of_void *)i2d_##asn1, str, fp,       \
+        return PEM_ASN1_write((i2d_of_void *)i2d_##asn1, str, out,      \
                               x, NULL, NULL, 0, NULL, NULL);            \
     }
 
@@ -99,12 +107,9 @@ extern "C" {
 #  endif
 
 #  define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1)              \
-    int PEM_write_##name(FILE *fp, const type *x,                       \
-                         const EVP_CIPHER *enc,                         \
-                         const unsigned char *kstr, int klen,           \
-                         pem_password_cb *cb, void *u)                  \
+    PEM_write_cb_fnsig(name, type, FILE, write)                         \
     {                                                                   \
-        return PEM_ASN1_write((i2d_of_void *)i2d_##asn1, str, fp,       \
+        return PEM_ASN1_write((i2d_of_void *)i2d_##asn1, str, out,      \
                               x, enc, kstr, klen, cb, u);               \
     }
 
@@ -123,9 +128,9 @@ extern "C" {
     }
 
 # define IMPLEMENT_PEM_write_bio(name, type, str, asn1)                 \
-    int PEM_write_bio_##name(BIO *bp, const type *x)                    \
+    PEM_write_fnsig(name, type, BIO, write_bio)                         \
     {                                                                   \
-        return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1, str, bp,   \
+        return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1, str, out,  \
                                   x, NULL,NULL,0,NULL,NULL);            \
     }
 
@@ -135,12 +140,9 @@ extern "C" {
 # endif
 
 # define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1)              \
-    int PEM_write_bio_##name(BIO *bp, const type *x,                    \
-                             const EVP_CIPHER *enc,                     \
-                             const unsigned char *kstr, int klen,       \
-                             pem_password_cb *cb, void *u)              \
+    PEM_write_cb_fnsig(name, type, BIO, write_bio)                      \
     {                                                                   \
-        return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1, str, bp,   \
+        return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1, str, out,  \
                                   x, enc, kstr, klen, cb, u);           \
     }
 
@@ -203,18 +205,15 @@ extern "C" {
     type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u);
 
 #  define DECLARE_PEM_write_fp(name, type)              \
-    int PEM_write_##name(FILE *fp, const type *x);
+    PEM_write_fnsig(name, type, FILE, write);
 
 #  ifndef OPENSSL_NO_DEPRECATED_3_0
-#   define DECLARE_PEM_write_fp_const(name, type)        \
-    DECLARE_PEM_write_fp(name, type)
+#   define DECLARE_PEM_write_fp_const(name, type)       \
+    PEM_write_fnsig(name, type, FILE, write);
 #  endif
 
-#  define DECLARE_PEM_write_cb_fp(name, type)                   \
-    int PEM_write_##name(FILE *fp, const type *x,               \
-                         const EVP_CIPHER *enc,                 \
-                         const unsigned char *kstr, int klen,   \
-                         pem_password_cb *cb, void *u);
+#  define DECLARE_PEM_write_cb_fp(name, type)           \
+    PEM_write_cb_fnsig(name, type, FILE, write);
 
 # endif
 
@@ -223,18 +222,15 @@ extern "C" {
                               pem_password_cb *cb, void *u);
 
 #  define DECLARE_PEM_write_bio(name, type)             \
-    int PEM_write_bio_##name(BIO *bp, const type *x);
+    PEM_write_fnsig(name, type, BIO, write_bio);
 
 #  ifndef OPENSSL_NO_DEPRECATED_3_0
-#   define DECLARE_PEM_write_bio_const(name, type)       \
-    DECLARE_PEM_write_bio(name, type)
+#   define DECLARE_PEM_write_bio_const(name, type)      \
+    PEM_write_fnsig(name, type, BIO, write_bio);
 #  endif
 
-#  define DECLARE_PEM_write_cb_bio(name, type)                          \
-    int PEM_write_bio_##name(BIO *bp, const type *x,                    \
-                             const EVP_CIPHER *enc,                     \
-                             const unsigned char *kstr, int klen,       \
-                             pem_password_cb *cb, void *u);
+#  define DECLARE_PEM_write_cb_bio(name, type)          \
+    PEM_write_cb_fnsig(name, type, BIO, write_bio);
 
 # define DECLARE_PEM_write(name, type) \
         DECLARE_PEM_write_bio(name, type) \
@@ -261,7 +257,6 @@ extern "C" {
 # define DECLARE_PEM_rw_cb(name, type) \
         DECLARE_PEM_read(name, type) \
         DECLARE_PEM_write_cb(name, type)
-typedef int pem_password_cb (char *buf, int size, int rwflag, void *userdata);
 
 int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher);
 int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *len,
@@ -327,6 +322,7 @@ DECLARE_PEM_rw(X509_AUX, X509)
 DECLARE_PEM_rw(X509_REQ, X509_REQ)
 DECLARE_PEM_write(X509_REQ_NEW, X509_REQ)
 DECLARE_PEM_rw(X509_CRL, X509_CRL)
+DECLARE_PEM_rw(X509_PUBKEY, X509_PUBKEY)
 DECLARE_PEM_rw(PKCS7, PKCS7)
 DECLARE_PEM_rw(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE)
 DECLARE_PEM_rw(PKCS8, X509_SIG)
diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h
index 852981dfcf..02a02160b3 100644
--- a/include/openssl/rsa.h
+++ b/include/openssl/rsa.h
@@ -224,6 +224,7 @@ const BIGNUM *RSA_get0_q(const RSA *d);
 const BIGNUM *RSA_get0_dmp1(const RSA *r);
 const BIGNUM *RSA_get0_dmq1(const RSA *r);
 const BIGNUM *RSA_get0_iqmp(const RSA *r);
+const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *r);
 void RSA_clear_flags(RSA *r, int flags);
 int RSA_test_flags(const RSA *r, int flags);
 void RSA_set_flags(RSA *r, int flags);
@@ -279,14 +280,14 @@ int RSA_pkey_ctx_ctrl(EVP_PKEY_CTX *ctx, int optype, int cmd, int p1, void *p2);
 DECLARE_ASN1_ENCODE_FUNCTIONS_name(RSA, RSAPublicKey)
 DECLARE_ASN1_ENCODE_FUNCTIONS_name(RSA, RSAPrivateKey)
 
-typedef struct rsa_pss_params_st {
+struct rsa_pss_params_st {
     X509_ALGOR *hashAlgorithm;
     X509_ALGOR *maskGenAlgorithm;
     ASN1_INTEGER *saltLength;
     ASN1_INTEGER *trailerField;
     /* Decoded hash algorithm from maskGenAlgorithm */
     X509_ALGOR *maskHash;
-} RSA_PSS_PARAMS;
+};
 
 DECLARE_ASN1_FUNCTIONS(RSA_PSS_PARAMS)
 
diff --git a/include/openssl/serializer.h b/include/openssl/serializer.h
new file mode 100644
index 0000000000..2629a13ccd
--- /dev/null
+++ b/include/openssl/serializer.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_SERIALIZER_H
+# define OPENSSL_SERIALIZER_H
+# pragma once
+
+# include <openssl/opensslconf.h>
+
+# ifndef OPENSSL_NO_STDIO
+#  include <stdio.h>
+# endif
+# include <stdarg.h>
+# include <stddef.h>
+# include <openssl/serializererr.h>
+# include <openssl/types.h>
+# include <openssl/core.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *libctx,
+                                       const char *name,
+                                       const char *properties);
+int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *ser);
+void OSSL_SERIALIZER_free(OSSL_SERIALIZER *ser);
+
+const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER *ser);
+const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser);
+int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser);
+int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *ser,
+                         const char *name);
+
+void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx,
+                                     void (*fn)(OSSL_SERIALIZER *ser,
+                                                void *arg),
+                                     void *arg);
+void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *ser,
+                                  void (*fn)(const char *name, void *data),
+                                  void *data);
+
+const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser);
+OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser);
+const OSSL_SERIALIZER *
+OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx);
+int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx,
+                                   const OSSL_PARAM params[]);
+void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx);
+
+/* Utilities that help set specific parameters */
+int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx,
+                                   const char *cipher_name,
+                                   const char *propquery);
+int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx,
+                                       const unsigned char *kstr,
+                                       size_t klen);
+int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc,
+                                          pem_password_cb *cb, void *cbarg);
+int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx,
+                                          const UI_METHOD *ui_method,
+                                          void *ui_data);
+
+/* Utilities to output the object to serialize */
+int OSSL_SERIALIZER_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out);
+#ifndef OPENSSL_NO_STDIO
+int OSSL_SERIALIZER_to_fp(OSSL_SERIALIZER_CTX *ctx, FILE *fp);
+#endif
+
+/*
+ * Create the OSSL_SERIALIZER_CTX with an associated type.  This will perform
+ * an implicit OSSL_SERIALIZER_fetch(), suitable for the object of that type.
+ * This is more useful than calling OSSL_SERIALIZER_CTX_new().
+ */
+OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
+                                                         const char *propquery);
+
+/*
+ * These macros define the last argument to pass to
+ * OSSL_SERIALIZER_CTX_new_by_TYPE().
+ */
+# define OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ "format=pem,type=public"
+# define OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ "format=pem,type=private"
+# define OSSL_SERIALIZER_Parameters_TO_PEM_PQ "format=pem,type=domainparams"
+
+/* Corresponding macros for text output */
+# define OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ "format=text,type=public"
+# define OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ "format=text,type=private"
+# define OSSL_SERIALIZER_Parameters_TO_TEXT_PQ "format=text,type=domainparams"
+
+# ifdef __cplusplus
+}
+# endif
+#endif
diff --git a/include/openssl/serializererr.h b/include/openssl/serializererr.h
new file mode 100644
index 0000000000..4eff9deab6
--- /dev/null
+++ b/include/openssl/serializererr.h
@@ -0,0 +1,34 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OPENSSL_OSSL_SERIALIZERERR_H
+# define OPENSSL_OSSL_SERIALIZERERR_H
+
+# include <openssl/opensslconf.h>
+# include <openssl/symhacks.h>
+
+
+# ifdef  __cplusplus
+extern "C"
+# endif
+int ERR_load_OSSL_SERIALIZER_strings(void);
+
+/*
+ * OSSL_SERIALIZER function codes.
+ */
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+# endif
+
+/*
+ * OSSL_SERIALIZER reason codes.
+ */
+# define OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY       100
+
+#endif
diff --git a/include/openssl/types.h b/include/openssl/types.h
index 151e3f1713..9ac8549062 100644
--- a/include/openssl/types.h
+++ b/include/openssl/types.h
@@ -130,6 +130,7 @@ typedef struct dsa_method DSA_METHOD;
 
 typedef struct rsa_st RSA;
 typedef struct rsa_meth_st RSA_METHOD;
+typedef struct rsa_pss_params_st RSA_PSS_PARAMS;
 
 typedef struct ec_key_st EC_KEY;
 typedef struct ec_key_method_st EC_KEY_METHOD;
@@ -203,6 +204,11 @@ typedef struct ossl_item_st OSSL_ITEM;
 typedef struct ossl_algorithm_st OSSL_ALGORITHM;
 typedef struct ossl_param_st OSSL_PARAM;
 
+typedef int pem_password_cb (char *buf, int size, int rwflag, void *userdata);
+
+typedef struct ossl_serializer_st OSSL_SERIALIZER;
+typedef struct ossl_serializer_ctx_st OSSL_SERIALIZER_CTX;
+
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \
     defined(INTMAX_MAX) && defined(UINTMAX_MAX)
 typedef intmax_t ossl_intmax_t;
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 116b30205c..e4de10e6f9 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -424,6 +424,8 @@ int i2d_ECPrivateKey_fp(FILE *fp, const EC_KEY *eckey);
 #  endif
 X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8);
 int i2d_PKCS8_fp(FILE *fp, const X509_SIG *p8);
+X509_PUBKEY *d2i_X509_PUBKEY_fp(FILE *fp, X509_PUBKEY **xpk);
+int i2d_X509_PUBKEY_fp(FILE *fp, const X509_PUBKEY *xpk);
 PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
                                                 PKCS8_PRIV_KEY_INFO **p8inf);
 int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, const PKCS8_PRIV_KEY_INFO *p8inf);
@@ -462,6 +464,8 @@ int i2d_ECPrivateKey_bio(BIO *bp, const EC_KEY *eckey);
 #  endif
 X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8);
 int i2d_PKCS8_bio(BIO *bp, const X509_SIG *p8);
+X509_PUBKEY *d2i_X509_PUBKEY_bio(BIO *bp, X509_PUBKEY **xpk);
+int i2d_X509_PUBKEY_bio(BIO *bp, const X509_PUBKEY *xpk);
 PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
                                                  PKCS8_PRIV_KEY_INFO **p8inf);
 int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, const PKCS8_PRIV_KEY_INFO *p8inf);
diff --git a/providers/common/bio_prov.c b/providers/common/bio_prov.c
new file mode 100644
index 0000000000..7b44004399
--- /dev/null
+++ b/providers/common/bio_prov.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include "prov/bio.h"
+
+static OSSL_BIO_new_file_fn *c_bio_new_file = NULL;
+static OSSL_BIO_new_membuf_fn *c_bio_new_membuf = NULL;
+static OSSL_BIO_read_ex_fn *c_bio_read_ex = NULL;
+static OSSL_BIO_free_fn *c_bio_free = NULL;
+static OSSL_BIO_vprintf_fn *c_bio_vprintf = NULL;
+
+int ossl_prov_bio_from_dispatch(const OSSL_DISPATCH *fns)
+{
+    for (; fns->function_id != 0; fns++) {
+        switch (fns->function_id) {
+        case OSSL_FUNC_BIO_NEW_FILE:
+            if (c_bio_new_file == NULL)
+                c_bio_new_file = OSSL_get_BIO_new_file(fns);
+            break;
+        case OSSL_FUNC_BIO_NEW_MEMBUF:
+            if (c_bio_new_membuf == NULL)
+                c_bio_new_membuf = OSSL_get_BIO_new_membuf(fns);
+            break;
+        case OSSL_FUNC_BIO_READ_EX:
+            if (c_bio_read_ex == NULL)
+                c_bio_read_ex = OSSL_get_BIO_read_ex(fns);
+            break;
+        case OSSL_FUNC_BIO_FREE:
+            if (c_bio_free == NULL)
+                c_bio_free = OSSL_get_BIO_free(fns);
+            break;
+        case OSSL_FUNC_BIO_VPRINTF:
+            if (c_bio_vprintf == NULL)
+                c_bio_vprintf = OSSL_get_BIO_vprintf(fns);
+            break;
+        }
+    }
+
+    return 1;
+}
+
+BIO *ossl_prov_bio_new_file(const char *filename, const char *mode)
+{
+    if (c_bio_new_file == NULL)
+        return NULL;
+    return c_bio_new_file(filename, mode);
+}
+
+BIO *ossl_prov_bio_new_membuf(const char *filename, int len)
+{
+    if (c_bio_new_membuf == NULL)
+        return NULL;
+    return c_bio_new_membuf(filename, len);
+}
+
+int ossl_prov_bio_read_ex(BIO *bio, void *data, size_t data_len,
+                          size_t *bytes_read)
+{
+    if (c_bio_read_ex == NULL)
+        return 0;
+    return c_bio_read_ex(bio, data, data_len, bytes_read);
+}
+
+int ossl_prov_bio_free(BIO *bio)
+{
+    if (c_bio_free == NULL)
+        return 0;
+    return c_bio_free(bio);
+}
+
+int ossl_prov_bio_vprintf(BIO *bio, const char *format, va_list ap)
+{
+    if (c_bio_vprintf == NULL)
+        return -1;
+    return c_bio_vprintf(bio, format, ap);
+}
+
+int ossl_prov_bio_printf(BIO *bio, const char *format, ...)
+{
+    va_list ap;
+    int ret;
+
+    va_start(ap, format);
+    ret = ossl_prov_bio_vprintf(bio, format, ap);
+    va_end(ap);
+
+    return ret;
+}
+
diff --git a/providers/common/build.info b/providers/common/build.info
index df05c49cd2..3f20fb3c09 100644
--- a/providers/common/build.info
+++ b/providers/common/build.info
@@ -1,6 +1,6 @@
 SUBDIRS=digests ciphers
 
-SOURCE[../libcommon.a]=provider_err.c
+SOURCE[../libcommon.a]=provider_err.c bio_prov.c
 $FIPSCOMMON=provider_util.c
 SOURCE[../libnonfips.a]=$FIPSCOMMON nid_to_name.c
 SOURCE[../libfips.a]=$FIPSCOMMON
diff --git a/providers/common/include/prov/bio.h b/providers/common/include/prov/bio.h
new file mode 100644
index 0000000000..63f9d4ec3a
--- /dev/null
+++ b/providers/common/include/prov/bio.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdarg.h>
+#include <openssl/bio.h>
+#include <openssl/core.h>
+
+int ossl_prov_bio_from_dispatch(const OSSL_DISPATCH *fns);
+
+BIO *ossl_prov_bio_new_file(const char *filename, const char *mode);
+BIO *ossl_prov_bio_new_membuf(const char *filename, int len);
+int ossl_prov_bio_read_ex(BIO *bio, void *data, size_t data_len,
+                          size_t *bytes_read);
+int ossl_prov_bio_free(BIO *bio);
+int ossl_prov_bio_vprintf(BIO *bio, const char *format, va_list ap);
+int ossl_prov_bio_printf(BIO *bio, const char *format, ...);
diff --git a/providers/common/include/prov/providercommonerr.h b/providers/common/include/prov/providercommonerr.h
index 503f0270c5..85e5856d30 100644
--- a/providers/common/include/prov/providercommonerr.h
+++ b/providers/common/include/prov/providercommonerr.h
@@ -53,6 +53,7 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_BAD_DECRYPT                               100
 # define PROV_R_BAD_ENCODING                              141
 # define PROV_R_BAD_LENGTH                                142
+# define PROV_R_BN_ERROR                                  160
 # define PROV_R_BOTH_MODE_AND_MODE_INT                    127
 # define PROV_R_CIPHER_OPERATION_FAILED                   102
 # define PROV_R_FAILED_TO_GENERATE_KEY                    121
@@ -95,6 +96,7 @@ int ERR_load_PROV_strings(void);
 # define PROV_R_NOT_XOF_OR_INVALID_LENGTH                 113
 # define PROV_R_NO_KEY_SET                                114
 # define PROV_R_OUTPUT_BUFFER_TOO_SMALL                   106
+# define PROV_R_READ_KEY                                  159
 # define PROV_R_TAG_NOTSET                                119
 # define PROV_R_TAG_NOT_NEEDED                            120
 # define PROV_R_UNABLE_TO_LOAD_SHA1                       143
diff --git a/providers/common/provider_err.c b/providers/common/provider_err.c
index 2b7bac037d..7cae969c79 100644
--- a/providers/common/provider_err.c
+++ b/providers/common/provider_err.c
@@ -19,6 +19,7 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_DECRYPT), "bad decrypt"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_ENCODING), "bad encoding"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BAD_LENGTH), "bad length"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BN_ERROR), "bn error"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_BOTH_MODE_AND_MODE_INT),
     "both mode and mode int"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_CIPHER_OPERATION_FAILED),
@@ -77,6 +78,7 @@ static const ERR_STRING_DATA PROV_str_reasons[] = {
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_NO_KEY_SET), "no key set"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_OUTPUT_BUFFER_TOO_SMALL),
     "output buffer too small"},
+    {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_READ_KEY), "read key"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_TAG_NOTSET), "tag notset"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_TAG_NOT_NEEDED), "tag not needed"},
     {ERR_PACK(ERR_LIB_PROV, 0, PROV_R_UNABLE_TO_LOAD_SHA1),
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 354c7a4a6d..b8e96f9c21 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -14,6 +14,7 @@
 #include <openssl/core_numbers.h>
 #include <openssl/core_names.h>
 #include <openssl/params.h>
+#include "prov/bio.h"
 #include "prov/implementations.h"
 
 /* Functions provided by the core */
@@ -384,6 +385,42 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
     { NULL, NULL, NULL }
 };
 
+static const OSSL_ALGORITHM deflt_serializer[] = {
+    { "RSA", "default=yes,format=text,type=private",
+      rsa_priv_text_serializer_functions },
+    { "RSA", "default=yes,format=text,type=public",
+      rsa_pub_text_serializer_functions },
+    { "RSA", "default=yes,format=der,type=private",
+      rsa_priv_der_serializer_functions },
+    { "RSA", "default=yes,format=der,type=public",
+      rsa_pub_der_serializer_functions },
+    { "RSA", "default=yes,format=pem,type=private",
+      rsa_priv_pem_serializer_functions },
+    { "RSA", "default=yes,format=pem,type=public",
+      rsa_pub_pem_serializer_functions },
+
+    { "DH", "default=yes,format=text,type=private",
+      dh_priv_text_serializer_functions },
+    { "DH", "default=yes,format=text,type=public",
+      dh_pub_text_serializer_functions },
+    { "DH", "default=yes,format=text,type=domainparams",
+      dh_param_text_serializer_functions },
+    { "DH", "default=yes,format=der,type=private",
+      dh_priv_der_serializer_functions },
+    { "DH", "default=yes,format=der,type=public",
+      dh_pub_der_serializer_functions },
+    { "DH", "default=yes,format=der,type=domainparams",
+      dh_param_der_serializer_functions },
+    { "DH", "default=yes,format=pem,type=private",
+      dh_priv_pem_serializer_functions },
+    { "DH", "default=yes,format=pem,type=public",
+      dh_pub_pem_serializer_functions },
+    { "DH", "default=yes,format=pem,type=domainparams",
+      dh_param_pem_serializer_functions },
+
+    { NULL, NULL, NULL }
+};
+
 static const OSSL_ALGORITHM *deflt_query(OSSL_PROVIDER *prov,
                                          int operation_id,
                                          int *no_cache)
@@ -406,6 +443,8 @@ static const OSSL_ALGORITHM *deflt_query(OSSL_PROVIDER *prov,
         return deflt_signature;
     case OSSL_OP_ASYM_CIPHER:
         return deflt_asym_cipher;
+    case OSSL_OP_SERIALIZER:
+        return deflt_serializer;
     }
     return NULL;
 }
@@ -427,6 +466,8 @@ int ossl_default_provider_init(const OSSL_PROVIDER *provider,
 {
     OSSL_core_get_library_context_fn *c_get_libctx = NULL;
 
+    if (!ossl_prov_bio_from_dispatch(in))
+        return 0;
     for (; in->function_id != 0; in++) {
         switch (in->function_id) {
         case OSSL_FUNC_CORE_GETTABLE_PARAMS:
diff --git a/providers/implementations/build.info b/providers/implementations/build.info
index a2fbf45a22..e4cab9bd2e 100644
--- a/providers/implementations/build.info
+++ b/providers/implementations/build.info
@@ -1 +1,2 @@
-SUBDIRS=digests ciphers macs kdfs exchange keymgmt signature asymciphers
+SUBDIRS=digests ciphers macs kdfs exchange keymgmt signature asymciphers \
+        serializers
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 490843c544..75c20a096e 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -261,3 +261,29 @@ extern const OSSL_DISPATCH dsa_signature_functions[];
 
 /* Asym Cipher */
 extern const OSSL_DISPATCH rsa_asym_cipher_functions[];
+
+/* Serializers */
+extern const OSSL_DISPATCH rsa_priv_text_serializer_functions[];
+extern const OSSL_DISPATCH rsa_pub_text_serializer_functions[];
+extern const OSSL_DISPATCH rsa_priv_der_serializer_functions[];
+extern const OSSL_DISPATCH rsa_pub_der_serializer_functions[];
+extern const OSSL_DISPATCH rsa_priv_pem_serializer_functions[];
+extern const OSSL_DISPATCH rsa_pub_pem_serializer_functions[];
+extern const OSSL_DISPATCH dh_priv_text_serializer_functions[];
+extern const OSSL_DISPATCH dh_pub_text_serializer_functions[];
+extern const OSSL_DISPATCH dh_param_text_serializer_functions[];
+extern const OSSL_DISPATCH dh_priv_der_serializer_functions[];
+extern const OSSL_DISPATCH dh_pub_der_serializer_functions[];
+extern const OSSL_DISPATCH dh_param_der_serializer_functions[];
+extern const OSSL_DISPATCH dh_priv_pem_serializer_functions[];
+extern const OSSL_DISPATCH dh_pub_pem_serializer_functions[];
+extern const OSSL_DISPATCH dh_param_pem_serializer_functions[];
+extern const OSSL_DISPATCH dsa_priv_text_serializer_functions[];
+extern const OSSL_DISPATCH dsa_pub_text_serializer_functions[];
+extern const OSSL_DISPATCH dsa_param_text_serializer_functions[];
+extern const OSSL_DISPATCH dsa_priv_der_serializer_functions[];
+extern const OSSL_DISPATCH dsa_pub_der_serializer_functions[];
+extern const OSSL_DISPATCH dsa_param_der_serializer_functions[];
+extern const OSSL_DISPATCH dsa_priv_pem_serializer_functions[];
+extern const OSSL_DISPATCH dsa_pub_pem_serializer_functions[];
+extern const OSSL_DISPATCH dsa_param_pem_serializer_functions[];
diff --git a/providers/implementations/serializers/build.info b/providers/implementations/serializers/build.info
new file mode 100644
index 0000000000..43bdee6b77
--- /dev/null
+++ b/providers/implementations/serializers/build.info
@@ -0,0 +1,12 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$SERIALIZER_GOAL=../../libimplementations.a
+$RSA_GOAL=../../libimplementations.a
+$DH_GOAL=../../libimplementations.a
+$DSA_GOAL=../../libimplementations.a
+
+SOURCE[$SERIALIZER_GOAL]=serializer_common.c
+SOURCE[$RSA_GOAL]=serializer_rsa.c serializer_rsa_priv.c serializer_rsa_pub.c
+SOURCE[$DH_GOAL]=serializer_dh.c serializer_dh_priv.c serializer_dh_pub.c serializer_dh_param.c
+SOURCE[$DSA_GOAL]=serializer_dsa.c serializer_dsa_priv.c serializer_dsa_pub.c serializer_dsa_param.c
diff --git a/providers/implementations/serializers/serializer_common.c b/providers/implementations/serializers/serializer_common.c
new file mode 100644
index 0000000000..6aa4e64e7a
--- /dev/null
+++ b/providers/implementations/serializers/serializer_common.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/opensslconf.h> /* SIXTY_FOUR_BIT_LONG, ... */
+#include <openssl/err.h>
+#include <openssl/pem.h>         /* PEM_BUFSIZE */
+#include <openssl/pkcs12.h>      /* PKCS8_encrypt() */
+#include <openssl/types.h>
+#include <openssl/x509.h>        /* i2d_X509_PUBKEY_bio() */
+#include "crypto/bn.h"           /* bn_get_words() */
+#include "prov/bio.h"            /* ossl_prov_bio_printf() */
+#include "prov/implementations.h"
+#include "prov/providercommonerr.h" /* PROV_R_READ_KEY */
+#include "serializer_local.h"
+
+static PKCS8_PRIV_KEY_INFO *
+ossl_prov_p8info_from_obj(const void *obj, int obj_nid,
+                          ASN1_STRING *params,
+                          int params_type,
+                          int (*k2d)(const void *obj,
+                                     unsigned char **pder))
+{
+    /* der, derlen store the key DER output and its length */
+    unsigned char *der = NULL;
+    int derlen;
+    /* The final PKCS#8 info */
+    PKCS8_PRIV_KEY_INFO *p8info = NULL;
+
+
+    if ((p8info = PKCS8_PRIV_KEY_INFO_new()) == NULL
+        || (derlen = k2d(obj, &der)) <= 0
+        || !PKCS8_pkey_set0(p8info, OBJ_nid2obj(obj_nid), 0,
+                            params_type, params, der, derlen)) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        PKCS8_PRIV_KEY_INFO_free(p8info);
+        OPENSSL_free(der);
+        p8info = NULL;
+    }
+
+    return p8info;
+}
+
+static X509_SIG *ossl_prov_encp8_from_p8info(PKCS8_PRIV_KEY_INFO *p8info,
+                                             struct pkcs8_encrypt_ctx_st *ctx)
+{
+    X509_SIG *p8 = NULL;
+    char buf[PEM_BUFSIZE];
+    const void *kstr = ctx->cipher_pass;
+    size_t klen = ctx->cipher_pass_length;
+
+    if (ctx->cipher == NULL)
+        return NULL;
+
+    if (kstr == NULL) {
+        if (!ctx->cb(buf, sizeof(buf), &klen, NULL, ctx->cbarg)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_READ_KEY);
+            return NULL;
+        }
+        kstr = buf;
+    }
+    /* NID == -1 means "standard" */
+    p8 = PKCS8_encrypt(-1, ctx->cipher, kstr, klen, NULL, 0, 0, p8info);
+    if (kstr == buf)
+        OPENSSL_cleanse(buf, klen);
+    return p8;
+}
+
+static X509_SIG *ossl_prov_encp8_from_obj(const void *obj, int obj_nid,
+                                          ASN1_STRING *params,
+                                          int params_type,
+                                          int (*k2d)(const void *obj,
+                                                     unsigned char **pder),
+                                          struct pkcs8_encrypt_ctx_st *ctx)
+{
+    PKCS8_PRIV_KEY_INFO *p8info =
+        ossl_prov_p8info_from_obj(obj, obj_nid, params, params_type, k2d);
+    X509_SIG *p8 = ossl_prov_encp8_from_p8info(p8info, ctx);
+
+    PKCS8_PRIV_KEY_INFO_free(p8info);
+    return p8;
+}
+
+static X509_PUBKEY *ossl_prov_pubkey_from_obj(const void *obj, int obj_nid,
+                                              ASN1_STRING *params,
+                                              int params_type,
+                                              int (*k2d)(const void *obj,
+                                                         unsigned char **pder))
+{
+    /* der, derlen store the key DER output and its length */
+    unsigned char *der = NULL;
+    int derlen;
+    /* The final X509_PUBKEY */
+    X509_PUBKEY *xpk = NULL;
+
+
+    if ((xpk = X509_PUBKEY_new()) == NULL
+        || (derlen = k2d(obj, &der)) <= 0
+        || !X509_PUBKEY_set0_param(xpk, OBJ_nid2obj(obj_nid),
+                                   params_type, params, der, derlen)) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        X509_PUBKEY_free(xpk);
+        OPENSSL_free(der);
+        xpk = NULL;
+    }
+
+    return xpk;
+}
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_importkey(const OSSL_DISPATCH *fns)
+{
+    /* Pilfer the keymgmt dispatch table */
+    for (; fns->function_id != 0; fns++)
+        if (fns->function_id == OSSL_FUNC_KEYMGMT_IMPORTKEY)
+            return OSSL_get_OP_keymgmt_importkey(fns);
+
+    return NULL;
+}
+
+# ifdef SIXTY_FOUR_BIT_LONG
+#  define BN_FMTu "%lu"
+#  define BN_FMTx "%lx"
+# endif
+
+# ifdef SIXTY_FOUR_BIT
+#  define BN_FMTu "%llu"
+#  define BN_FMTx "%llx"
+# endif
+
+# ifdef THIRTY_TWO_BIT
+#  define BN_FMTu "%u"
+#  define BN_FMTx "%x"
+# endif
+
+int ossl_prov_print_labeled_bignum(BIO *out, const char *label,
+                                   const BIGNUM *n)
+{
+    const char *neg;
+    const char *post_label_spc = " ";
+    int bytes;
+    BN_ULONG *words;
+    int i, off;
+
+    if (n == NULL)
+        return 0;
+    if (label == NULL) {
+        label = "";
+        post_label_spc = "";
+    }
+
+    bytes = BN_num_bytes(n);
+    words = bn_get_words(n);
+    neg = BN_is_negative(n) ? "-" : "";
+
+    if (BN_is_zero(n))
+        return ossl_prov_bio_printf(out, "%s%s0\n", label, post_label_spc);
+
+    if (BN_num_bytes(n) <= BN_BYTES)
+        return ossl_prov_bio_printf(out,
+                                    "%s%s%s" BN_FMTu " (%s0x" BN_FMTx ")\n",
+                                    label, post_label_spc, neg, words[0],
+                                    neg, words[0]);
+
+    if (neg[0] == '-')
+        neg = " (Negative)";
+
+    if (ossl_prov_bio_printf(out, "%s%s\n", label, neg) <= 0)
+        return 0;
+
+    /* Skip past the zero bytes in the first word */
+    for (off = 0; off < BN_BYTES; off++) {
+        BN_ULONG l = words[0];
+        int o = 8 * (BN_BYTES - off - 1);
+        int b = ((l & (0xffLU << o)) >> o) & 0xff;
+
+        if (b != 0)
+            break;
+    }
+    /* print 16 hex digits per line, indented with 4 spaces */
+    for (i = 0; i < bytes; i += 16) {
+        int j, step;
+
+        if (ossl_prov_bio_printf(out, "    ") <= 0)
+            return 0;
+
+        for (j = 0; i + j < bytes && j < 16; j += BN_BYTES - step) {
+            int k;
+            BN_ULONG l = words[(i + j + off) / BN_BYTES];
+
+            step = (i + j + off) % BN_BYTES;
+
+            for (k = step; k < BN_BYTES; k++) {
+                int o = 8 * (BN_BYTES - k - 1);
+                int b = ((l & (0xffLU << o)) >> o) & 0xff;
+
+                /* We may have reached the number of bytes prematurely */
+                if (i + j + k - off >= bytes)
+                    break;
+
+                if (ossl_prov_bio_printf(out, "%02x:", b) <= 0)
+                    return 0;
+            }
+        }
+
+        if (ossl_prov_bio_printf(out, "\n") <= 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+/* p2s = param to asn1_string, k2d = key to der */
+int ossl_prov_write_priv_der_from_obj(BIO *out, const void *obj, int obj_nid,
+                                      int (*p2s)(const void *obj, int nid,
+                                                 ASN1_STRING **str,
+                                                 int *strtype),
+                                      int (*k2d)(const void *obj,
+                                                 unsigned char **pder),
+                                      struct pkcs8_encrypt_ctx_st *ctx)
+{
+    int ret = 0;
+    ASN1_STRING *str = NULL;
+    int strtype = 0;
+
+    if (p2s != NULL && !p2s(obj, obj_nid, &str, &strtype))
+        return 0;
+
+    if (ctx->cipher_intent) {
+        X509_SIG *p8 =
+            ossl_prov_encp8_from_obj(obj, obj_nid, str, strtype, k2d, ctx);
+
+        if (p8 != NULL)
+            ret = i2d_PKCS8_bio(out, p8);
+
+        X509_SIG_free(p8);
+    } else {
+        PKCS8_PRIV_KEY_INFO *p8info =
+            ossl_prov_p8info_from_obj(obj, obj_nid, str, strtype, k2d);
+
+        if (p8info != NULL)
+            ret = i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8info);
+
+        PKCS8_PRIV_KEY_INFO_free(p8info);
+    }
+
+    return ret;
+}
+
+int ossl_prov_write_priv_pem_from_obj(BIO *out, const void *obj, int obj_nid,
+                                      int (*p2s)(const void *obj, int nid,
+                                                 ASN1_STRING **str,
+                                                 int *strtype),
+                                      int (*k2d)(const void *obj,
+                                                 unsigned char **pder),
+                                      struct pkcs8_encrypt_ctx_st *ctx)
+{
+    int ret = 0;
+    ASN1_STRING *str = NULL;
+    int strtype = 0;
+
+    if (p2s != NULL && !p2s(obj, obj_nid, &str, &strtype))
+        return 0;
+
+    if (ctx->cipher_intent) {
+        X509_SIG *p8 = ossl_prov_encp8_from_obj(obj, obj_nid, str, strtype,
+                                                k2d, ctx);
+
+        if (p8 != NULL)
+            ret = PEM_write_bio_PKCS8(out, p8);
+
+        X509_SIG_free(p8);
+    } else {
+        PKCS8_PRIV_KEY_INFO *p8info =
+            ossl_prov_p8info_from_obj(obj, obj_nid, str, strtype, k2d);
+
+        if (p8info != NULL)
+            ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8info);
+
+        PKCS8_PRIV_KEY_INFO_free(p8info);
+    }
+
+    return ret;
+}
+
+int ossl_prov_write_pub_der_from_obj(BIO *out, const void *obj, int obj_nid,
+                                     int (*p2s)(const void *obj, int nid,
+                                                ASN1_STRING **str,
+                                                int *strtype),
+                                     int (*k2d)(const void *obj,
+                                                unsigned char **pder))
+{
+    int ret = 0;
+    ASN1_STRING *str = NULL;
+    int strtype = 0;
+    X509_PUBKEY *xpk = NULL;
+
+    if (p2s != NULL && !p2s(obj, obj_nid, &str, &strtype))
+        return 0;
+
+    xpk = ossl_prov_pubkey_from_obj(obj, obj_nid, str, strtype, k2d);
+
+    if (xpk != NULL)
+        ret = i2d_X509_PUBKEY_bio(out, xpk);
+
+    /* Also frees |str| */
+    X509_PUBKEY_free(xpk);
+    return ret;
+}
+
+int ossl_prov_write_pub_pem_from_obj(BIO *out, const void *obj, int obj_nid,
+                                     int (*p2s)(const void *obj, int nid,
+                                                ASN1_STRING **str,
+                                                int *strtype),
+                                     int (*k2d)(const void *obj,
+                                                unsigned char **pder))
+{
+    int ret = 0;
+    ASN1_STRING *str = NULL;
+    int strtype = 0;
+    X509_PUBKEY *xpk = NULL;
+
+    if (p2s != NULL && !p2s(obj, obj_nid, &str, &strtype))
+        return 0;
+
+    xpk = ossl_prov_pubkey_from_obj(obj, obj_nid, str, strtype, k2d);
+
+    if (xpk != NULL)
+        ret = PEM_write_bio_X509_PUBKEY(out, xpk);
+
+    /* Also frees |str| */
+    X509_PUBKEY_free(xpk);
+    return ret;
+}
+
diff --git a/providers/implementations/serializers/serializer_dh.c b/providers/implementations/serializers/serializer_dh.c
new file mode 100644
index 0000000000..313fae0e30
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dh.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include "prov/bio.h"             /* ossl_prov_bio_printf() */
+#include "prov/implementations.h" /* rsa_keymgmt_functions */
+#include "prov/providercommonerr.h" /* PROV_R_BN_ERROR */
+#include "serializer_local.h"
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_dh_importkey(void)
+{
+    return ossl_prov_get_importkey(dh_keymgmt_functions);
+}
+
+int ossl_prov_print_dh(BIO *out, DH *dh, enum dh_print_type type)
+{
+    const char *type_label = NULL;
+    const BIGNUM *priv_key = NULL, *pub_key = NULL;
+    const BIGNUM *p = NULL, *g = NULL;
+
+
+    switch (type) {
+    case dh_print_priv:
+        type_label = "DH Private-Key";
+        break;
+    case dh_print_pub:
+        type_label = "DH Public-Key";
+        break;
+    case dh_print_params:
+        type_label = "DH Parameters";
+        break;
+    }
+
+    if (type == dh_print_priv) {
+        priv_key = DH_get0_priv_key(dh);
+        if (priv_key == NULL)
+            goto null_err;
+    }
+
+    if (type == dh_print_priv || type == dh_print_pub) {
+        pub_key = DH_get0_pub_key(dh);
+        if (pub_key == NULL)
+            goto null_err;
+    }
+
+    p = DH_get0_p(dh);
+    g = DH_get0_p(dh);
+    if (p == NULL || g == NULL)
+        goto null_err;
+
+    /*
+     * TODO(3.0): add printing of:
+     *
+     * - q (label "subgroup order:")
+     * - j (label "subgroup factor:")
+     * - seed (label "seed:")
+     * - counter (label "counter:")
+     *
+     * This can happen as soon as there are DH_get0_ functions for them.
+     */
+
+    if (ossl_prov_bio_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p))
+        <= 0)
+        goto err;
+    if (priv_key != NULL
+        && !ossl_prov_print_labeled_bignum(out, "    private-key:", priv_key))
+        goto err;
+    if (pub_key != NULL
+        && !ossl_prov_print_labeled_bignum(out, "    public-key:", pub_key))
+        goto err;
+    if (p != NULL
+        && !ossl_prov_print_labeled_bignum(out, "    prime:", p))
+        goto err;
+    if (g != NULL
+        && !ossl_prov_print_labeled_bignum(out, "    generator:", g))
+        goto err;
+
+    return 1;
+ err:
+    return 0;
+ null_err:
+    ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+    goto err;
+}
+
+int ossl_prov_prepare_dh_params(const void *dh, int nid,
+                                ASN1_STRING **pstr, int *pstrtype)
+{
+    ASN1_STRING *params = ASN1_STRING_new();
+
+    if (params == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (nid == EVP_PKEY_DHX)
+        params->length = i2d_DHxparams(dh, &params->data);
+    else
+        params->length = i2d_DHparams(dh, &params->data);
+
+    if (params->length <= 0) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        ASN1_STRING_free(params);
+        return 0;
+    }
+    params->type = V_ASN1_SEQUENCE;
+
+    *pstr = params;
+    *pstrtype = V_ASN1_SEQUENCE;
+    return 1;
+}
+
+int ossl_prov_dh_pub_to_der(const void *dh, unsigned char **pder)
+{
+    ASN1_INTEGER *pub_key = BN_to_ASN1_INTEGER(DH_get0_pub_key(dh), NULL);
+    int ret;
+
+    if (pub_key == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+        return 0;
+    }
+
+    ret = i2d_ASN1_INTEGER(pub_key, pder);
+
+    ASN1_STRING_clear_free(pub_key);
+    return ret;
+}
+
+int ossl_prov_dh_priv_to_der(const void *dh, unsigned char **pder)
+{
+    ASN1_INTEGER *priv_key = BN_to_ASN1_INTEGER(DH_get0_priv_key(dh), NULL);
+    int ret;
+
+    if (priv_key == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+        return 0;
+    }
+
+    ret = i2d_ASN1_INTEGER(priv_key, pder);
+
+    ASN1_STRING_clear_free(priv_key);
+    return ret;
+}
+
diff --git a/providers/implementations/serializers/serializer_dh_param.c b/providers/implementations/serializers/serializer_dh_param.c
new file mode 100644
index 0000000000..ca3371e19f
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dh_param.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/pem.h>
+#include <openssl/dh.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "prov/providercommonerr.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dh_param_newctx;
+static OSSL_OP_serializer_freectx_fn dh_param_freectx;
+static OSSL_OP_serializer_serialize_data_fn dh_param_der_data;
+static OSSL_OP_serializer_serialize_object_fn dh_param_der;
+static OSSL_OP_serializer_serialize_data_fn dh_param_pem_data;
+static OSSL_OP_serializer_serialize_object_fn dh_param_pem;
+
+static OSSL_OP_serializer_serialize_data_fn dh_param_print_data;
+static OSSL_OP_serializer_serialize_object_fn dh_param_print;
+
+/* Parameters : context */
+
+/*
+ * There's no specific implementation context, so we use the provider context
+ */
+static void *dh_param_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dh_param_freectx(void *ctx)
+{
+}
+
+/* Public key : DER */
+static int dh_param_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_param_der(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_param_der(void *ctx, void *dh, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return i2d_DHparams_bio(out, dh);
+}
+
+/* Public key : PEM */
+static int dh_param_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_param_pem(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_param_pem(void *ctx, void *dh, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return PEM_write_bio_DHparams(out, dh);
+}
+
+static int dh_param_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                              OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_param_print(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_param_print(void *ctx, void *dh, BIO *out,
+                         OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dh(out, dh, dh_print_params);
+}
+
+const OSSL_DISPATCH dh_param_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_param_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_param_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_param_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_param_pem_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_param_pem },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_param_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_param_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dh_param_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_dh_priv.c b/providers/implementations/serializers/serializer_dh_priv.c
new file mode 100644
index 0000000000..80d87ac738
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dh_priv.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/dh.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dh_priv_newctx;
+static OSSL_OP_serializer_freectx_fn dh_priv_freectx;
+static OSSL_OP_serializer_set_ctx_params_fn dh_priv_set_ctx_params;
+static OSSL_OP_serializer_settable_ctx_params_fn dh_priv_settable_ctx_params;
+static OSSL_OP_serializer_serialize_data_fn dh_priv_der_data;
+static OSSL_OP_serializer_serialize_object_fn dh_priv_der;
+static OSSL_OP_serializer_serialize_data_fn dh_pem_priv_data;
+static OSSL_OP_serializer_serialize_object_fn dh_pem_priv;
+
+static OSSL_OP_serializer_newctx_fn dh_print_newctx;
+static OSSL_OP_serializer_freectx_fn dh_print_freectx;
+static OSSL_OP_serializer_serialize_data_fn dh_priv_print_data;
+static OSSL_OP_serializer_serialize_object_fn dh_priv_print;
+
+ /*
+ * Context used for private key serialization.
+ */
+struct dh_priv_ctx_st {
+    void *provctx;
+
+    struct pkcs8_encrypt_ctx_st sc;
+};
+
+/* Private key : context */
+static void *dh_priv_newctx(void *provctx)
+{
+    struct dh_priv_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+    }
+
+    /* -1 is the "whatever" indicator, i.e. the PKCS8 library default PBE */
+    ctx->sc.pbe_nid = -1;
+
+    return ctx;
+}
+
+static void dh_priv_freectx(void *vctx)
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+
+    EVP_CIPHER_free(ctx->sc.cipher);
+    OPENSSL_free(ctx->sc.cipher_pass);
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *dh_priv_settable_ctx_params(void)
+{
+    static const OSSL_PARAM settables[] = {
+        OSSL_PARAM_utf8_string(OSSL_SERIALIZER_PARAM_CIPHER, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_SERIALIZER_PARAM_PASS, NULL, 0),
+        OSSL_PARAM_END,
+    };
+
+    return settables;
+}
+
+static int dh_priv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_CIPHER))
+        != NULL) {
+        const OSSL_PARAM *propsp =
+            OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PROPERTIES);
+        const char *props = NULL;
+
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        if (propsp != NULL && propsp->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        props = (propsp != NULL ? propsp->data : NULL);
+
+        EVP_CIPHER_free(ctx->sc.cipher);
+        ctx->sc.cipher_intent = p->data != NULL;
+        if (p->data != NULL
+            && ((ctx->sc.cipher = EVP_CIPHER_fetch(NULL, p->data, props))
+                == NULL))
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PASS))
+        != NULL) {
+        OPENSSL_free(ctx->sc.cipher_pass);
+        ctx->sc.cipher_pass = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, &ctx->sc.cipher_pass, 0,
+                                         &ctx->sc.cipher_pass_length))
+            return 0;
+    }
+    return 1;
+}
+
+/* Private key : DER */
+static int dh_priv_der_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx->provctx, params);
+
+        ok = dh_priv_der(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_priv_der(void *vctx, void *dh, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+    int ret;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    ret = ossl_prov_write_priv_der_from_obj(out, dh, EVP_PKEY_DH,
+                                            ossl_prov_prepare_dh_params,
+                                            ossl_prov_dh_priv_to_der,
+                                            &ctx->sc);
+
+    return ret;
+}
+
+/* Private key : PEM */
+static int dh_pem_priv_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params);
+
+        ok = dh_pem_priv(ctx->provctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_pem_priv(void *vctx, void *dh, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dh_priv_ctx_st *ctx = vctx;
+    int ret;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    ret = ossl_prov_write_priv_pem_from_obj(out, dh, EVP_PKEY_DH,
+                                            ossl_prov_prepare_dh_params,
+                                            ossl_prov_dh_priv_to_der,
+                                            &ctx->sc);
+
+    return ret;
+}
+
+/*
+ * There's no specific print context, so we use the provider context
+ */
+static void *dh_print_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dh_print_freectx(void *ctx)
+{
+}
+
+static int dh_priv_print_data(void *provctx, const OSSL_PARAM params[],
+                               BIO *out,
+                               OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(provctx, params); /* ctx == provctx */
+
+        ok = dh_priv_print(provctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_priv_print(void *ctx, void *dh, BIO *out,
+                          OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dh(out, dh, dh_print_priv);
+}
+
+const OSSL_DISPATCH dh_priv_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))dh_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))dh_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_priv_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_priv_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_priv_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))dh_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))dh_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_pem_priv_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_pem_priv },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_priv_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_print_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_print_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_priv_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dh_priv_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_dh_pub.c b/providers/implementations/serializers/serializer_dh_pub.c
new file mode 100644
index 0000000000..73095c8fd1
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dh_pub.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/dh.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dh_pub_newctx;
+static OSSL_OP_serializer_freectx_fn dh_pub_freectx;
+static OSSL_OP_serializer_serialize_data_fn dh_pub_der_data;
+static OSSL_OP_serializer_serialize_object_fn dh_pub_der;
+static OSSL_OP_serializer_serialize_data_fn dh_pub_pem_data;
+static OSSL_OP_serializer_serialize_object_fn dh_pub_pem;
+
+static OSSL_OP_serializer_serialize_data_fn dh_pub_print_data;
+static OSSL_OP_serializer_serialize_object_fn dh_pub_print;
+
+/* Public key : context */
+
+/*
+ * There's no specific implementation context, so we use the provider context
+ */
+static void *dh_pub_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dh_pub_freectx(void *ctx)
+{
+}
+
+/* Public key : DER */
+static int dh_pub_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_pub_der(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_pub_der(void *ctx, void *dh, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_write_pub_der_from_obj(out, dh, EVP_PKEY_DH,
+                                            ossl_prov_prepare_dh_params,
+                                            ossl_prov_dh_pub_to_der);
+}
+
+/* Public key : PEM */
+static int dh_pub_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_pub_pem(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_pub_pem(void *ctx, void *dh, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_write_pub_pem_from_obj(out, dh, EVP_PKEY_DH,
+                                            ossl_prov_prepare_dh_params,
+                                            ossl_prov_dh_pub_to_der);
+
+}
+
+static int dh_pub_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                              OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dh_importkey =
+        ossl_prov_get_dh_importkey();
+    int ok = 0;
+
+    if (dh_importkey != NULL) {
+        DH *dh = dh_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dh_pub_print(ctx, dh, out, cb, cbarg);
+        DH_free(dh);
+    }
+    return ok;
+}
+
+static int dh_pub_print(void *ctx, void *dh, BIO *out,
+                         OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dh(out, dh, 0);
+}
+
+const OSSL_DISPATCH dh_pub_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_pub_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_pub_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_pub_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dh_pub_pem_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_pub_pem },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dh_pub_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dh_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dh_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dh_pub_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dh_pub_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_dsa.c b/providers/implementations/serializers/serializer_dsa.c
new file mode 100644
index 0000000000..7578c4ef2e
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dsa.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include "prov/bio.h"             /* ossl_prov_bio_printf() */
+#include "prov/implementations.h" /* rsa_keymgmt_functions */
+#include "prov/providercommonerr.h" /* PROV_R_BN_ERROR */
+#include "serializer_local.h"
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_dsa_importkey(void)
+{
+    return ossl_prov_get_importkey(dsa_keymgmt_functions);
+}
+
+int ossl_prov_print_dsa(BIO *out, DSA *dsa, enum dsa_print_type type)
+{
+    const char *type_label = NULL;
+    const BIGNUM *priv_key = NULL, *pub_key = NULL;
+    const BIGNUM *p = NULL, *q = NULL, *g = NULL;
+
+
+    switch (type) {
+    case dsa_print_priv:
+        type_label = "Private-Key";
+        break;
+    case dsa_print_pub:
+        type_label = "Public-Key";
+        break;
+    case dsa_print_params:
+        type_label = "DSA-Parameters";
+        break;
+    }
+
+    if (type == dsa_print_priv) {
+        priv_key = DSA_get0_priv_key(dsa);
+        if (priv_key == NULL)
+            goto null_err;
+    }
+
+    if (type == dsa_print_priv || type == dsa_print_pub) {
+        pub_key = DSA_get0_pub_key(dsa);
+        if (pub_key == NULL)
+            goto null_err;
+    }
+
+    p = DSA_get0_p(dsa);
+    q = DSA_get0_q(dsa);
+    g = DSA_get0_p(dsa);
+
+    if (p == NULL || q == NULL || g == NULL)
+        goto null_err;
+
+    if (ossl_prov_bio_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p))
+        <= 0)
+        goto err;
+    if (priv_key != NULL
+        && !ossl_prov_print_labeled_bignum(out, "priv:", priv_key))
+        goto err;
+    if (pub_key != NULL
+        && !ossl_prov_print_labeled_bignum(out, "pub: ", pub_key))
+        goto err;
+    if (!ossl_prov_print_labeled_bignum(out, "P:   ", p))
+        goto err;
+    if (!ossl_prov_print_labeled_bignum(out, "Q:   ", q))
+        goto err;
+    if (!ossl_prov_print_labeled_bignum(out, "G:   ", g))
+        goto err;
+
+    return 1;
+ err:
+    return 0;
+ null_err:
+    ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+    goto err;
+}
+
+int ossl_prov_prepare_dsa_params(const void *dsa, int nid,
+                                ASN1_STRING **pstr, int *pstrtype)
+{
+    ASN1_STRING *params = ASN1_STRING_new();
+
+    if (params == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    params->length = i2d_DSAparams(dsa, &params->data);
+
+    if (params->length <= 0) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        ASN1_STRING_free(params);
+        return 0;
+    }
+
+    *pstrtype = V_ASN1_SEQUENCE;
+    *pstr = params;
+    return 1;
+}
+
+int ossl_prov_prepare_all_dsa_params(const void *dsa, int nid,
+                                     ASN1_STRING **pstr, int *pstrtype)
+{
+    const BIGNUM *p = DSA_get0_p(dsa);
+    const BIGNUM *q = DSA_get0_q(dsa);
+    const BIGNUM *g = DSA_get0_g(dsa);
+
+    if (p != NULL && q != NULL && g != NULL)
+        return ossl_prov_prepare_dsa_params(dsa, nid, pstr, pstrtype);
+
+    *pstr = NULL;
+    *pstrtype = V_ASN1_UNDEF;
+    return 1;
+}
+
+int ossl_prov_dsa_pub_to_der(const void *dsa, unsigned char **pder)
+{
+    ASN1_INTEGER *pub_key = BN_to_ASN1_INTEGER(DSA_get0_pub_key(dsa), NULL);
+    int ret;
+
+    if (pub_key == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+        return 0;
+    }
+
+    ret = i2d_ASN1_INTEGER(pub_key, pder);
+
+    ASN1_STRING_clear_free(pub_key);
+    return ret;
+}
+
+int ossl_prov_dsa_priv_to_der(const void *dsa, unsigned char **pder)
+{
+    ASN1_INTEGER *priv_key = BN_to_ASN1_INTEGER(DSA_get0_priv_key(dsa), NULL);
+    int ret;
+
+    if (priv_key == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+        return 0;
+    }
+
+    ret = i2d_ASN1_INTEGER(priv_key, pder);
+
+    ASN1_STRING_clear_free(priv_key);
+    return ret;
+}
diff --git a/providers/implementations/serializers/serializer_dsa_param.c b/providers/implementations/serializers/serializer_dsa_param.c
new file mode 100644
index 0000000000..3a108f61ad
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dsa_param.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/pem.h>
+#include <openssl/dsa.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "prov/providercommonerr.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dsa_param_newctx;
+static OSSL_OP_serializer_freectx_fn dsa_param_freectx;
+static OSSL_OP_serializer_serialize_data_fn dsa_param_der_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_param_der;
+static OSSL_OP_serializer_serialize_data_fn dsa_param_pem_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_param_pem;
+
+static OSSL_OP_serializer_serialize_data_fn dsa_param_print_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_param_print;
+
+/* Parameters : context */
+
+/*
+ * There's no specific implementation context, so we use the provider context
+ */
+static void *dsa_param_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dsa_param_freectx(void *ctx)
+{
+}
+
+/* Public key : DER */
+static int dsa_param_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_param_der(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_param_der(void *ctx, void *dsa, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return i2d_DSAparams_bio(out, dsa);
+}
+
+/* Public key : PEM */
+static int dsa_param_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_param_pem(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_param_pem(void *ctx, void *dsa, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return PEM_write_bio_DSAparams(out, dsa);
+}
+
+static int dsa_param_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                              OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_param_print(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_param_print(void *ctx, void *dsa, BIO *out,
+                         OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dsa(out, dsa, dsa_print_params);
+}
+
+const OSSL_DISPATCH dsa_param_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_param_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_param_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_param_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_param_pem_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_param_pem },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_param_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_param_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_param_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_param_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dsa_param_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_dsa_priv.c b/providers/implementations/serializers/serializer_dsa_priv.c
new file mode 100644
index 0000000000..603904369a
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dsa_priv.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/dsa.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dsa_priv_newctx;
+static OSSL_OP_serializer_freectx_fn dsa_priv_freectx;
+static OSSL_OP_serializer_set_ctx_params_fn dsa_priv_set_ctx_params;
+static OSSL_OP_serializer_settable_ctx_params_fn dsa_priv_settable_ctx_params;
+static OSSL_OP_serializer_serialize_data_fn dsa_priv_der_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_priv_der;
+static OSSL_OP_serializer_serialize_data_fn dsa_pem_priv_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_pem_priv;
+
+static OSSL_OP_serializer_newctx_fn dsa_print_newctx;
+static OSSL_OP_serializer_freectx_fn dsa_print_freectx;
+static OSSL_OP_serializer_serialize_data_fn dsa_priv_print_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_priv_print;
+
+ /*
+ * Context used for private key serialization.
+ */
+struct dsa_priv_ctx_st {
+    void *provctx;
+
+    struct pkcs8_encrypt_ctx_st sc;
+};
+
+/* Private key : context */
+static void *dsa_priv_newctx(void *provctx)
+{
+    struct dsa_priv_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+    }
+
+    /* -1 is the "whatever" indicator, i.e. the PKCS8 library default PBE */
+    ctx->sc.pbe_nid = -1;
+
+    return ctx;
+}
+
+static void dsa_priv_freectx(void *vctx)
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+
+    EVP_CIPHER_free(ctx->sc.cipher);
+    OPENSSL_free(ctx->sc.cipher_pass);
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *dsa_priv_settable_ctx_params(void)
+{
+    static const OSSL_PARAM settables[] = {
+        OSSL_PARAM_utf8_string(OSSL_SERIALIZER_PARAM_CIPHER, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_SERIALIZER_PARAM_PASS, NULL, 0),
+        OSSL_PARAM_END,
+    };
+
+    return settables;
+}
+
+static int dsa_priv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_CIPHER))
+        != NULL) {
+        const OSSL_PARAM *propsp =
+            OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PROPERTIES);
+        const char *props = NULL;
+
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        if (propsp != NULL && propsp->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        props = (propsp != NULL ? propsp->data : NULL);
+
+        EVP_CIPHER_free(ctx->sc.cipher);
+        ctx->sc.cipher_intent = p->data != NULL;
+        if (p->data != NULL
+            && ((ctx->sc.cipher = EVP_CIPHER_fetch(NULL, p->data, props))
+                == NULL))
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PASS))
+        != NULL) {
+        OPENSSL_free(ctx->sc.cipher_pass);
+        ctx->sc.cipher_pass = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, &ctx->sc.cipher_pass, 0,
+                                         &ctx->sc.cipher_pass_length))
+            return 0;
+    }
+    return 1;
+}
+
+/* Private key : DER */
+static int dsa_priv_der_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx->provctx, params);
+
+        ok = dsa_priv_der(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_priv_der(void *vctx, void *dsa, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    return ossl_prov_write_priv_der_from_obj(out, dsa, EVP_PKEY_DSA,
+                                             ossl_prov_prepare_dsa_params,
+                                             ossl_prov_dsa_priv_to_der,
+                                             &ctx->sc);
+}
+
+/* Private key : PEM */
+static int dsa_pem_priv_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params);
+
+        ok = dsa_pem_priv(ctx->provctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_pem_priv(void *vctx, void *dsa, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct dsa_priv_ctx_st *ctx = vctx;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    return ossl_prov_write_priv_pem_from_obj(out, dsa, EVP_PKEY_DSA,
+                                             ossl_prov_prepare_dsa_params,
+                                             ossl_prov_dsa_priv_to_der,
+                                             &ctx->sc);
+}
+
+/*
+ * There's no specific print context, so we use the provider context
+ */
+static void *dsa_print_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dsa_print_freectx(void *ctx)
+{
+}
+
+static int dsa_priv_print_data(void *provctx, const OSSL_PARAM params[],
+                               BIO *out,
+                               OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(provctx, params); /* ctx == provctx */
+
+        ok = dsa_priv_print(provctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_priv_print(void *ctx, void *dsa, BIO *out,
+                          OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dsa(out, dsa, dsa_print_priv);
+}
+
+const OSSL_DISPATCH dsa_priv_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))dsa_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))dsa_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_priv_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_priv_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_priv_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))dsa_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))dsa_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_pem_priv_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_pem_priv },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_priv_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_print_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_print_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_priv_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dsa_priv_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_dsa_pub.c b/providers/implementations/serializers/serializer_dsa_pub.c
new file mode 100644
index 0000000000..0901718a93
--- /dev/null
+++ b/providers/implementations/serializers/serializer_dsa_pub.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/dsa.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn dsa_pub_newctx;
+static OSSL_OP_serializer_freectx_fn dsa_pub_freectx;
+static OSSL_OP_serializer_serialize_data_fn dsa_pub_der_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_pub_der;
+static OSSL_OP_serializer_serialize_data_fn dsa_pub_pem_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_pub_pem;
+
+static OSSL_OP_serializer_serialize_data_fn dsa_pub_print_data;
+static OSSL_OP_serializer_serialize_object_fn dsa_pub_print;
+
+/* Public key : context */
+
+/*
+ * There's no specific implementation context, so we use the provider context
+ */
+static void *dsa_pub_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void dsa_pub_freectx(void *ctx)
+{
+}
+
+/* Public key : DER */
+static int dsa_pub_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_pub_der(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_pub_der(void *ctx, void *dsa, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    /*
+     * TODO(v3.0) implement setting save_parameters, see dsa_pub_encode()
+     * in crypto/dsa/dsa_ameth.c
+     */
+    int save_parameters = 1;
+
+    return
+        save_parameters
+        ? ossl_prov_write_pub_der_from_obj(out, dsa, EVP_PKEY_DSA,
+                                           ossl_prov_prepare_all_dsa_params,
+                                           ossl_prov_dsa_pub_to_der)
+        : ossl_prov_write_pub_der_from_obj(out, dsa, EVP_PKEY_DSA,
+                                           ossl_prov_prepare_dsa_params,
+                                           ossl_prov_dsa_pub_to_der);
+
+}
+
+/* Public key : PEM */
+static int dsa_pub_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_pub_pem(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_pub_pem(void *ctx, void *dsa, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_write_pub_pem_from_obj(out, dsa, EVP_PKEY_DSA,
+                                            ossl_prov_prepare_dsa_params,
+                                            ossl_prov_dsa_pub_to_der);
+}
+
+static int dsa_pub_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                              OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *dsa_importkey =
+        ossl_prov_get_dsa_importkey();
+    int ok = 0;
+
+    if (dsa_importkey != NULL) {
+        DSA *dsa = dsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = dsa_pub_print(ctx, dsa, out, cb, cbarg);
+        DSA_free(dsa);
+    }
+    return ok;
+}
+
+static int dsa_pub_print(void *ctx, void *dsa, BIO *out,
+                         OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_dsa(out, dsa, 0);
+}
+
+const OSSL_DISPATCH dsa_pub_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_pub_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_pub_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_pub_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))dsa_pub_pem_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_pub_pem },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH dsa_pub_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))dsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))dsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))dsa_pub_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))dsa_pub_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_local.h b/providers/implementations/serializers/serializer_local.h
new file mode 100644
index 0000000000..57365f94a9
--- /dev/null
+++ b/providers/implementations/serializers/serializer_local.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_numbers.h>
+#include <openssl/bn.h>
+#include <openssl/asn1.h>        /* i2d_of_void */
+#include <openssl/x509.h>        /* X509_SIG */
+#include <openssl/types.h>
+
+struct pkcs8_encrypt_ctx_st {
+    /* Set to 1 if intending to encrypt/decrypt, otherwise 0 */
+    int cipher_intent;
+
+    EVP_CIPHER *cipher;
+    int pbe_nid;                 /* For future variation */
+
+    /* Passphrase that was passed by the caller */
+    void *cipher_pass;
+    size_t cipher_pass_length;
+
+    /* This callback is only used of |cipher_pass| is NULL */
+    OSSL_PASSPHRASE_CALLBACK *cb;
+    void *cbarg;
+};
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_importkey(const OSSL_DISPATCH *fns);
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_rsa_importkey(void);
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_dh_importkey(void);
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_dsa_importkey(void);
+
+int ossl_prov_prepare_dh_params(const void *dh, int nid,
+                                ASN1_STRING **pstr, int *pstrtype);
+int ossl_prov_dh_pub_to_der(const void *dh, unsigned char **pder);
+int ossl_prov_dh_priv_to_der(const void *dh, unsigned char **pder);
+
+int ossl_prov_prepare_dsa_params(const void *dsa, int nid,
+                                ASN1_STRING **pstr, int *pstrtype);
+/*
+ * Special variant of ossl_prov_prepare_dsa_params() that requires all
+ * three parameters (P, Q and G) to be set.  This is used when serializing
+ * the public key.
+ */
+int ossl_prov_prepare_all_dsa_params(const void *dsa, int nid,
+                                     ASN1_STRING **pstr, int *pstrtype);
+int ossl_prov_dsa_pub_to_der(const void *dsa, unsigned char **pder);
+int ossl_prov_dsa_priv_to_der(const void *dsa, unsigned char **pder);
+
+int ossl_prov_print_labeled_bignum(BIO *out, const char *label,
+                                   const BIGNUM *n);
+int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv);
+
+enum dh_print_type {
+    dh_print_priv,
+    dh_print_pub,
+    dh_print_params
+};
+
+int ossl_prov_print_dh(BIO *out, DH *dh, enum dh_print_type type);
+
+enum dsa_print_type {
+    dsa_print_priv,
+    dsa_print_pub,
+    dsa_print_params
+};
+
+int ossl_prov_print_dsa(BIO *out, DSA *dsa, enum dsa_print_type type);
+
+int ossl_prov_write_priv_der_from_obj(BIO *out, const void *obj, int obj_nid,
+                                      int (*p2s)(const void *obj, int nid,
+                                                 ASN1_STRING **str,
+                                                 int *strtype),
+                                      int (*k2d)(const void *obj,
+                                                 unsigned char **pder),
+                                      struct pkcs8_encrypt_ctx_st *ctx);
+int ossl_prov_write_priv_pem_from_obj(BIO *out, const void *obj, int obj_nid,
+                                      int (*p2s)(const void *obj, int nid,
+                                                 ASN1_STRING **str,
+                                                 int *strtype),
+                                      int (*k2d)(const void *obj,
+                                                 unsigned char **pder),
+                                      struct pkcs8_encrypt_ctx_st *ctx);
+int ossl_prov_write_pub_der_from_obj(BIO *out, const void *obj, int obj_nid,
+                                     int (*p2s)(const void *obj, int nid,
+                                                ASN1_STRING **str,
+                                                int *strtype),
+                                     int (*k2d)(const void *obj,
+                                                unsigned char **pder));
+int ossl_prov_write_pub_pem_from_obj(BIO *out, const void *obj, int obj_nid,
+                                     int (*p2s)(const void *obj, int nid,
+                                                ASN1_STRING **str,
+                                                int *strtype),
+                                     int (*k2d)(const void *obj,
+                                                unsigned char **pder));
diff --git a/providers/implementations/serializers/serializer_rsa.c b/providers/implementations/serializers/serializer_rsa.c
new file mode 100644
index 0000000000..ac8e4a2bbe
--- /dev/null
+++ b/providers/implementations/serializers/serializer_rsa.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/rsa.h"           /* rsa_get0_all_params() */
+#include "prov/bio.h"             /* ossl_prov_bio_printf() */
+#include "prov/implementations.h" /* rsa_keymgmt_functions */
+#include "serializer_local.h"
+
+DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
+
+OSSL_OP_keymgmt_importkey_fn *ossl_prov_get_rsa_importkey(void)
+{
+    return ossl_prov_get_importkey(rsa_keymgmt_functions);
+}
+
+int ossl_prov_print_rsa(BIO *out, RSA *rsa, int priv)
+{
+    const char *modulus_label;
+    const char *exponent_label;
+    const BIGNUM *rsa_d = NULL, *rsa_n = NULL, *rsa_e = NULL;
+    STACK_OF(BIGNUM_const) *factors = sk_BIGNUM_const_new_null();
+    STACK_OF(BIGNUM_const) *exps = sk_BIGNUM_const_new_null();
+    STACK_OF(BIGNUM_const) *coeffs = sk_BIGNUM_const_new_null();
+    int ret = 0;
+
+    if (rsa == NULL || factors == NULL || exps == NULL || coeffs == NULL)
+        goto err;
+
+    RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
+    rsa_get0_all_params(rsa, factors, exps, coeffs);
+
+    if (priv && rsa_d != NULL) {
+        if (ossl_prov_bio_printf(out, "Private-Key: (%d bit, %d primes)\n",
+                                 BN_num_bits(rsa_n),
+                                 sk_BIGNUM_const_num(factors)) <= 0)
+            goto err;
+        modulus_label = "modulus:";
+        exponent_label = "publicExponent:";
+    } else {
+        if (ossl_prov_bio_printf(out, "Public-Key: (%d bit)\n",
+                                 BN_num_bits(rsa_n)) <= 0)
+            goto err;
+        modulus_label = "Modulus:";
+        exponent_label = "Exponent:";
+    }
+    if (!ossl_prov_print_labeled_bignum(out, modulus_label, rsa_n))
+        goto err;
+    if (!ossl_prov_print_labeled_bignum(out, exponent_label, rsa_e))
+        goto err;
+    if (priv) {
+        int i;
+
+        if (!ossl_prov_print_labeled_bignum(out, "privateExponent:", rsa_d))
+            goto err;
+        if (!ossl_prov_print_labeled_bignum(out, "prime1:",
+                                            sk_BIGNUM_const_value(factors, 0)))
+            goto err;
+        if (!ossl_prov_print_labeled_bignum(out, "prime2:",
+                                            sk_BIGNUM_const_value(factors, 1)))
+            goto err;
+        if (!ossl_prov_print_labeled_bignum(out, "exponent1:",
+                                            sk_BIGNUM_const_value(exps, 0)))
+            goto err;
+        if (!ossl_prov_print_labeled_bignum(out, "exponent2:",
+                                            sk_BIGNUM_const_value(exps, 1)))
+            goto err;
+        if (!ossl_prov_print_labeled_bignum(out, "coefficient:",
+                                            sk_BIGNUM_const_value(coeffs, 0)))
+            goto err;
+        for (i = 2; i < sk_BIGNUM_const_num(factors); i++) {
+            if (ossl_prov_bio_printf(out, "prime%d:", i + 1) <= 0)
+                goto err;
+            if (!ossl_prov_print_labeled_bignum(out, NULL,
+                                                sk_BIGNUM_const_value(factors,
+                                                                      i)))
+                goto err;
+            if (ossl_prov_bio_printf(out, "exponent%d:", i + 1) <= 0)
+                goto err;
+            if (!ossl_prov_print_labeled_bignum(out, NULL,
+                                                sk_BIGNUM_const_value(exps, i)))
+                goto err;
+            if (ossl_prov_bio_printf(out, "coefficient%d:", i + 1) <= 0)
+                goto err;
+            if (!ossl_prov_print_labeled_bignum(out, NULL,
+                                                sk_BIGNUM_const_value(coeffs,
+                                                                      i - 1)))
+                goto err;
+        }
+    }
+    ret = 1;
+ err:
+    sk_BIGNUM_const_free(factors);
+    sk_BIGNUM_const_free(exps);
+    sk_BIGNUM_const_free(coeffs);
+    return ret;
+}
+
diff --git a/providers/implementations/serializers/serializer_rsa_priv.c b/providers/implementations/serializers/serializer_rsa_priv.c
new file mode 100644
index 0000000000..05d9316ba1
--- /dev/null
+++ b/providers/implementations/serializers/serializer_rsa_priv.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include <openssl/safestack.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "prov/providercommonerr.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn rsa_priv_newctx;
+static OSSL_OP_serializer_freectx_fn rsa_priv_freectx;
+static OSSL_OP_serializer_set_ctx_params_fn rsa_priv_set_ctx_params;
+static OSSL_OP_serializer_settable_ctx_params_fn rsa_priv_settable_ctx_params;
+static OSSL_OP_serializer_serialize_data_fn rsa_priv_der_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_priv_der;
+static OSSL_OP_serializer_serialize_data_fn rsa_pem_priv_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_pem_priv;
+
+static OSSL_OP_serializer_newctx_fn rsa_print_newctx;
+static OSSL_OP_serializer_freectx_fn rsa_print_freectx;
+static OSSL_OP_serializer_serialize_data_fn rsa_priv_print_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_priv_print;
+
+ /*
+ * Context used for private key serialization.
+ */
+struct rsa_priv_ctx_st {
+    void *provctx;
+
+    struct pkcs8_encrypt_ctx_st sc;
+};
+
+/* Helper functions to prepare RSA-PSS params for serialization */
+
+static int prepare_rsa_params(const void *rsa, int nid,
+                              ASN1_STRING **pstr, int *pstrtype)
+{
+    const RSA_PSS_PARAMS *pss = RSA_get0_pss_params(rsa);
+    *pstr = NULL;
+
+    /* If RSA it's just NULL type */
+    if (nid != EVP_PKEY_RSA_PSS) {
+        *pstrtype = V_ASN1_NULL;
+        return 1;
+    }
+    /* If no PSS parameters we omit parameters entirely */
+    if (pss == NULL) {
+        *pstrtype = V_ASN1_UNDEF;
+        return 1;
+    }
+    /* Encode PSS parameters */
+    if (ASN1_item_pack((void *)pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), pstr)
+        == NULL)
+        return 0;
+
+    *pstrtype = V_ASN1_SEQUENCE;
+    return 1;
+}
+
+/* Private key : context */
+static void *rsa_priv_newctx(void *provctx)
+{
+    struct rsa_priv_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL) {
+        ctx->provctx = provctx;
+    }
+
+    /* -1 is the "whatever" indicator, i.e. the PKCS8 library default PBE */
+    ctx->sc.pbe_nid = -1;
+
+    return ctx;
+}
+
+static void rsa_priv_freectx(void *vctx)
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+
+    EVP_CIPHER_free(ctx->sc.cipher);
+    OPENSSL_free(ctx->sc.cipher_pass);
+    OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *rsa_priv_settable_ctx_params(void)
+{
+    static const OSSL_PARAM settables[] = {
+        OSSL_PARAM_utf8_string(OSSL_SERIALIZER_PARAM_CIPHER, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_SERIALIZER_PARAM_PASS, NULL, 0),
+        OSSL_PARAM_END,
+    };
+
+    return settables;
+}
+
+static int rsa_priv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+    const OSSL_PARAM *p;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_CIPHER))
+        != NULL) {
+        const OSSL_PARAM *propsp =
+            OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PROPERTIES);
+        const char *props = NULL;
+
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        if (propsp != NULL && propsp->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        props = (propsp != NULL ? propsp->data : NULL);
+
+        EVP_CIPHER_free(ctx->sc.cipher);
+        ctx->sc.cipher_intent = p->data != NULL;
+        if (p->data != NULL
+            && ((ctx->sc.cipher = EVP_CIPHER_fetch(NULL, p->data, props))
+                == NULL))
+            return 0;
+    }
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SERIALIZER_PARAM_PASS))
+        != NULL) {
+        OPENSSL_free(ctx->sc.cipher_pass);
+        ctx->sc.cipher_pass = NULL;
+        if (!OSSL_PARAM_get_octet_string(p, &ctx->sc.cipher_pass, 0,
+                                         &ctx->sc.cipher_pass_length))
+            return 0;
+    }
+    return 1;
+}
+
+/* Private key : DER */
+static int rsa_priv_der_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(ctx->provctx, params);
+
+        ok = rsa_priv_der(vctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_priv_der(void *vctx, void *rsa, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+    int ret;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    ret = ossl_prov_write_priv_der_from_obj(out, rsa, EVP_PKEY_RSA,
+                                            prepare_rsa_params,
+                                            (i2d_of_void *)i2d_RSAPrivateKey,
+                                            &ctx->sc);
+
+    return ret;
+}
+
+/* Private key : PEM */
+static int rsa_pem_priv_data(void *vctx, const OSSL_PARAM params[], BIO *out,
+                             OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(ctx, params);
+
+        ok = rsa_pem_priv(vctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_pem_priv(void *vctx, void *rsa, BIO *out,
+                        OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    struct rsa_priv_ctx_st *ctx = vctx;
+    int ret;
+
+    ctx->sc.cb = cb;
+    ctx->sc.cbarg = cbarg;
+
+    ret = ossl_prov_write_priv_pem_from_obj(out, rsa, EVP_PKEY_RSA,
+                                            prepare_rsa_params,
+                                            (i2d_of_void *)i2d_RSAPrivateKey,
+                                            &ctx->sc);
+
+    return ret;
+}
+
+/*
+ * There's no specific print context, so we use the provider context
+ */
+static void *rsa_print_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void rsa_print_freectx(void *ctx)
+{
+}
+
+static int rsa_priv_print_data(void *provctx, const OSSL_PARAM params[],
+                               BIO *out,
+                               OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(provctx, params); /* ctx == provctx */
+
+        ok = rsa_priv_print(provctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_priv_print(void *ctx, void *rsa, BIO *out,
+                          OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_rsa(out, rsa, 1);
+}
+
+const OSSL_DISPATCH rsa_priv_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))rsa_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))rsa_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))rsa_priv_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_priv_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH rsa_priv_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_priv_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_priv_freectx },
+    { OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS,
+      (void (*)(void))rsa_priv_set_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))rsa_priv_settable_ctx_params },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))rsa_pem_priv_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_pem_priv },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH rsa_priv_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_print_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_print_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_priv_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))rsa_priv_print_data },
+    { 0, NULL }
+};
diff --git a/providers/implementations/serializers/serializer_rsa_pub.c b/providers/implementations/serializers/serializer_rsa_pub.c
new file mode 100644
index 0000000000..c975499d7c
--- /dev/null
+++ b/providers/implementations/serializers/serializer_rsa_pub.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_numbers.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/types.h>
+#include <openssl/params.h>
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "prov/providercommonerr.h"
+#include "serializer_local.h"
+
+static OSSL_OP_serializer_newctx_fn rsa_pub_newctx;
+static OSSL_OP_serializer_freectx_fn rsa_pub_freectx;
+static OSSL_OP_serializer_serialize_data_fn rsa_pub_der_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_pub_der;
+static OSSL_OP_serializer_serialize_data_fn rsa_pub_pem_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_pub_pem;
+
+static OSSL_OP_serializer_serialize_data_fn rsa_pub_print_data;
+static OSSL_OP_serializer_serialize_object_fn rsa_pub_print;
+
+/* Public key : context */
+
+/*
+ * There's no specific implementation context, so we use the provider context
+ */
+static void *rsa_pub_newctx(void *provctx)
+{
+    return provctx;
+}
+
+static void rsa_pub_freectx(void *ctx)
+{
+}
+
+/* Public key : DER */
+static int rsa_pub_der_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = rsa_pub_der(ctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_pub_der(void *ctx, void *rsa, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return i2d_RSA_PUBKEY_bio(out, rsa);
+}
+
+/* Public key : PEM */
+static int rsa_pub_pem_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                            OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = rsa_pub_pem(ctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_pub_pem(void *ctx, void *rsa, BIO *out,
+                       OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return PEM_write_bio_RSA_PUBKEY(out, rsa);
+}
+
+static int rsa_pub_print_data(void *ctx, const OSSL_PARAM params[], BIO *out,
+                              OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    OSSL_OP_keymgmt_importkey_fn *rsa_importkey =
+        ossl_prov_get_rsa_importkey();
+    int ok = 0;
+
+    if (rsa_importkey != NULL) {
+        RSA *rsa = rsa_importkey(ctx, params); /* ctx == provctx */
+
+        ok = rsa_pub_print(ctx, rsa, out, cb, cbarg);
+        RSA_free(rsa);
+    }
+    return ok;
+}
+
+static int rsa_pub_print(void *ctx, void *rsa, BIO *out,
+                         OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+    return ossl_prov_print_rsa(out, rsa, 0);
+}
+
+const OSSL_DISPATCH rsa_pub_der_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))rsa_pub_der_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_pub_der },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH rsa_pub_pem_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA, (void (*)(void))rsa_pub_pem_data },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_pub_pem },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH rsa_pub_text_serializer_functions[] = {
+    { OSSL_FUNC_SERIALIZER_NEWCTX, (void (*)(void))rsa_pub_newctx },
+    { OSSL_FUNC_SERIALIZER_FREECTX, (void (*)(void))rsa_pub_freectx },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT, (void (*)(void))rsa_pub_print },
+    { OSSL_FUNC_SERIALIZER_SERIALIZE_DATA,
+      (void (*)(void))rsa_pub_print_data },
+    { 0, NULL }
+};
diff --git a/test/build.info b/test/build.info
index e17e1b5984..944702128c 100644
--- a/test/build.info
+++ b/test/build.info
@@ -39,7 +39,7 @@ IF[{- !$disabled{tests} -}]
           destest mdc2test \
           dhtest enginetest casttest \
           bftest ssltest_old dsatest dsa_no_digest_size_test exptest rsa_test \
-          evp_fromdata_test evp_test evp_extra_test evp_fetch_prov_test \
+          evp_pkey_provided_test evp_test evp_extra_test evp_fetch_prov_test \
           igetest v3nametest v3ext \
           crltest danetest bad_dtls_test lhash_test sparse_array_test \
           conf_include_test params_api_test params_conversion_test \
@@ -210,9 +210,9 @@ IF[{- !$disabled{tests} -}]
     DEFINE[evp_extra_test]=NO_FIPS_MODULE
   ENDIF
 
-  SOURCE[evp_fromdata_test]=evp_fromdata_test.c
-  INCLUDE[evp_fromdata_test]=../include ../apps/include
-  DEPEND[evp_fromdata_test]=../libcrypto libtestutil.a
+  SOURCE[evp_pkey_provided_test]=evp_pkey_provided_test.c
+  INCLUDE[evp_pkey_provided_test]=../include ../apps/include
+  DEPEND[evp_pkey_provided_test]=../libcrypto libtestutil.a
 
   SOURCE[igetest]=igetest.c
   INCLUDE[igetest]=../include ../apps/include
diff --git a/test/evp_fromdata_test.c b/test/evp_fromdata_test.c
deleted file mode 100644
index 74da50d3d3..0000000000
--- a/test/evp_fromdata_test.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License").  You may not use
- * this file except in compliance with the License.  You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include <openssl/evp.h>
-#include <openssl/provider.h>
-#include <openssl/params.h>
-#include <openssl/core_names.h>
-#include "internal/nelem.h"
-#include "crypto/evp.h"          /* For the internal API */
-#include "testutil.h"
-
-/* Array indexes used in test_fromdata_rsa */
-#define N       0
-#define E       1
-#define D       2
-#define P       3
-#define Q       4
-#define DP      5
-#define DQ      6
-#define QINV    7
-
-static int test_fromdata_rsa(void)
-{
-    int ret = 0;
-    EVP_PKEY_CTX *ctx = NULL;
-    EVP_PKEY *pk = NULL;
-    /*
-     * 32-bit RSA key, extracted from this command,
-     * executed with OpenSSL 1.0.2:
-     *
-     * openssl genrsa 32 | openssl rsa -text
-     */
-    static unsigned long key_numbers[] = {
-        0xbc747fc5,              /* N */
-        0x10001,                 /* E */
-        0x7b133399,              /* D */
-        0xe963,                  /* P */
-        0xceb7,                  /* Q */
-        0x8599,                  /* DP */
-        0xbd87,                  /* DQ */
-        0xcc3b,                  /* QINV */
-    };
-    OSSL_PARAM fromdata_params[] = {
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_N, &key_numbers[N]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_E, &key_numbers[E]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_D, &key_numbers[D]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_FACTOR, &key_numbers[P]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_FACTOR, &key_numbers[Q]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_EXPONENT, &key_numbers[DP]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_EXPONENT, &key_numbers[DQ]),
-        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_COEFFICIENT, &key_numbers[QINV]),
-        OSSL_PARAM_END
-    };
-
-    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_provided(NULL, "RSA", NULL)))
-        goto err;
-
-    if (!TEST_true(EVP_PKEY_key_fromdata_init(ctx))
-        || !TEST_true(EVP_PKEY_fromdata(ctx, &pk, fromdata_params)))
-        goto err;
-
-    /*
-     * TODO(3.0) We can't do much more at this point without using internals,
-     * because RSA functionality is still missing.  When the time comes, it
-     * would be nice to try and do something "useful" with this key, such
-     * as signing a small piece of data.
-     */
-    ret = 1;
-
- err:
-    EVP_PKEY_free(pk);
-    EVP_PKEY_CTX_free(ctx);
-
-    return ret;
-}
-
-int setup_tests(void)
-{
-    ADD_TEST(test_fromdata_rsa);
-    return 1;
-}
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
new file mode 100644
index 0000000000..6dcd0fe701
--- /dev/null
+++ b/test/evp_pkey_provided_test.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/serializer.h>
+#include <openssl/provider.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "internal/nelem.h"
+#include "crypto/evp.h"          /* For the internal API */
+#include "testutil.h"
+
+static int test_print_key_using_pem(const EVP_PKEY *pk)
+{
+    if (!TEST_true(EVP_PKEY_print_private(bio_out, pk, 0, NULL))
+        /* Public key in PEM form */
+        || !TEST_true(PEM_write_bio_PUBKEY(bio_out, pk))
+        /* Unencrypted private key in PEM form */
+        || !TEST_true(PEM_write_bio_PrivateKey(bio_out, pk,
+                                               NULL, NULL, 0, NULL, NULL))
+        /* Encrypted private key in PEM form */
+        || !TEST_true(PEM_write_bio_PrivateKey(bio_out, pk, EVP_aes_256_cbc(),
+                                               (unsigned char *)"pass", 4,
+                                               NULL, NULL)))
+        return 0;
+
+    return 1;
+}
+
+static int test_print_key_using_serializer(const EVP_PKEY *pk)
+{
+    const char *pq = OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ;
+    OSSL_SERIALIZER_CTX *ctx = NULL;
+    int ret = 1;
+
+    /* Make a context, it's valid for several prints */
+    TEST_note("Setting up a OSSL_SERIALIZER context with passphrase");
+    if (!TEST_ptr(ctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pk, pq))
+        /* Check that this operation is supported */
+        || !TEST_ptr(OSSL_SERIALIZER_CTX_get_serializer(ctx))
+        /* Set a passphrase to be used later */
+        || !TEST_true(OSSL_SERIALIZER_CTX_set_passphrase(ctx,
+                                                         (unsigned char *)"pass",
+                                                         4)))
+        goto err;
+
+    /* Use no cipher.  This should give us an unencrypted PEM */
+    TEST_note("Displaying PEM with no encryption");
+    if (!TEST_true(OSSL_SERIALIZER_to_bio(ctx, bio_out)))
+        ret = 0;
+
+    /* Use a valid cipher name */
+    TEST_note("Displaying PEM encrypted with AES-256-CBC");
+    if (!TEST_true(OSSL_SERIALIZER_CTX_set_cipher(ctx, "AES-256-CBC", NULL))
+        || !TEST_true(OSSL_SERIALIZER_to_bio(ctx, bio_out)))
+        ret = 0;
+
+    /* Use an invalid cipher name, which should generate no output */
+    TEST_note("NOT Displaying PEM encrypted with (invalid) FOO");
+    if (!TEST_false(OSSL_SERIALIZER_CTX_set_cipher(ctx, "FOO", NULL))
+        || !TEST_false(OSSL_SERIALIZER_to_bio(ctx, bio_out)))
+        ret = 0;
+
+    /* Clear the cipher.  This should give us an unencrypted PEM again */
+    TEST_note("Displaying PEM with encryption cleared (no encryption)");
+    if (!TEST_true(OSSL_SERIALIZER_CTX_set_cipher(ctx, NULL, NULL))
+        || !TEST_true(OSSL_SERIALIZER_to_bio(ctx, bio_out)))
+        ret = 0;
+
+err:
+    OSSL_SERIALIZER_CTX_free(ctx);
+    return ret;
+}
+
+/* Array indexes used in test_fromdata_rsa */
+#define N       0
+#define E       1
+#define D       2
+#define P       3
+#define Q       4
+#define DP      5
+#define DQ      6
+#define QINV    7
+
+static int test_fromdata_rsa(void)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_PKEY *pk = NULL;
+    /*
+     * 32-bit RSA key, extracted from this command,
+     * executed with OpenSSL 1.0.2:
+     *
+     * openssl genrsa 32 | openssl rsa -text
+     */
+    static unsigned long key_numbers[] = {
+        0xbc747fc5,              /* N */
+        0x10001,                 /* E */
+        0x7b133399,              /* D */
+        0xe963,                  /* P */
+        0xceb7,                  /* Q */
+        0x8599,                  /* DP */
+        0xbd87,                  /* DQ */
+        0xcc3b,                  /* QINV */
+    };
+    OSSL_PARAM fromdata_params[] = {
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_N, &key_numbers[N]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_E, &key_numbers[E]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_D, &key_numbers[D]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_FACTOR, &key_numbers[P]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_FACTOR, &key_numbers[Q]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_EXPONENT, &key_numbers[DP]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_EXPONENT, &key_numbers[DQ]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_RSA_COEFFICIENT, &key_numbers[QINV]),
+        OSSL_PARAM_END
+    };
+
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_provided(NULL, "RSA", NULL)))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_key_fromdata_init(ctx))
+        || !TEST_true(EVP_PKEY_fromdata(ctx, &pk, fromdata_params)))
+        goto err;
+
+    ret = test_print_key_using_pem(pk)
+        | test_print_key_using_serializer(pk);
+
+ err:
+    EVP_PKEY_free(pk);
+    EVP_PKEY_CTX_free(ctx);
+
+    return ret;
+}
+
+/* Array indexes used in test_fromdata_dh */
+#define PRIV_KEY        0
+#define PUB_KEY         1
+#define FFC_P           2
+#define FFC_G           3
+
+static int test_fromdata_dh(void)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_PKEY *pk = NULL;
+    /*
+     * 32-bit DH key, extracted from this command,
+     * executed with OpenSSL 1.0.2:
+     *
+     * openssl dhparam -out dhp.pem 32
+     * openssl genpkey -paramfile dhp.pem | openssl pkey -text
+     */
+    static unsigned long key_numbers[] = {
+        0x666c2b06,              /* priv-key */
+        0x6fa6de50,              /* pub-key */
+        0x8bb45f53,              /* P */
+        0x2,                     /* G */
+    };
+    OSSL_PARAM fromdata_params[] = {
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_DH_PRIV_KEY, &key_numbers[PRIV_KEY]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_DH_PUB_KEY, &key_numbers[PUB_KEY]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_FFC_P, &key_numbers[FFC_P]),
+        OSSL_PARAM_ulong(OSSL_PKEY_PARAM_FFC_G, &key_numbers[FFC_G]),
+        OSSL_PARAM_END
+    };
+
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_provided(NULL, "DH", NULL)))
+        goto err;
+
+    if (!TEST_true(EVP_PKEY_key_fromdata_init(ctx))
+        || !TEST_true(EVP_PKEY_fromdata(ctx, &pk, fromdata_params)))
+        goto err;
+
+    ret = test_print_key_using_pem(pk)
+        | test_print_key_using_serializer(pk);
+
+ err:
+    EVP_PKEY_free(pk);
+    EVP_PKEY_CTX_free(ctx);
+
+    return ret;
+}
+
+int setup_tests(void)
+{
+    ADD_TEST(test_fromdata_rsa);
+    ADD_TEST(test_fromdata_dh);
+    return 1;
+}
diff --git a/test/namemap_internal_test.c b/test/namemap_internal_test.c
index 16bc571265..0b67cb1d3f 100644
--- a/test/namemap_internal_test.c
+++ b/test/namemap_internal_test.c
@@ -18,10 +18,10 @@
 
 static int test_namemap(OSSL_NAMEMAP *nm)
 {
-    int num1 = ossl_namemap_add(nm, 0, NAME1);
-    int num2 = ossl_namemap_add(nm, 0, NAME2);
-    int num3 = ossl_namemap_add(nm, num1, ALIAS1);
-    int num4 = ossl_namemap_add(nm, 0, ALIAS1_UC);
+    int num1 = ossl_namemap_add_name(nm, 0, NAME1);
+    int num2 = ossl_namemap_add_name(nm, 0, NAME2);
+    int num3 = ossl_namemap_add_name(nm, num1, ALIAS1);
+    int num4 = ossl_namemap_add_name(nm, 0, ALIAS1_UC);
     int check1 = ossl_namemap_name2num(nm, NAME1);
     int check2 = ossl_namemap_name2num(nm, NAME2);
     int check3 = ossl_namemap_name2num(nm, ALIAS1);
@@ -66,10 +66,10 @@ static int test_digestbyname(void)
     OSSL_NAMEMAP *nm = ossl_namemap_stored(NULL);
     const EVP_MD *sha256, *foo;
 
-    id = ossl_namemap_add(nm, 0, "SHA256");
+    id = ossl_namemap_add_name(nm, 0, "SHA256");
     if (!TEST_int_ne(id, 0))
         return 0;
-    if (!TEST_int_eq(ossl_namemap_add(nm, id, "foo"), id))
+    if (!TEST_int_eq(ossl_namemap_add_name(nm, id, "foo"), id))
         return 0;
 
     sha256 = EVP_get_digestbyname("SHA256");
@@ -92,10 +92,10 @@ static int test_cipherbyname(void)
     OSSL_NAMEMAP *nm = ossl_namemap_stored(NULL);
     const EVP_CIPHER *aes128, *bar;
 
-    id = ossl_namemap_add(nm, 0, "AES-128-CBC");
+    id = ossl_namemap_add_name(nm, 0, "AES-128-CBC");
     if (!TEST_int_ne(id, 0))
         return 0;
-    if (!TEST_int_eq(ossl_namemap_add(nm, id, "bar"), id))
+    if (!TEST_int_eq(ossl_namemap_add_name(nm, id, "bar"), id))
         return 0;
 
     aes128 = EVP_get_cipherbyname("AES-128-CBC");
diff --git a/test/recipes/30-test_evp_fromdata.t b/test/recipes/30-test_evp_pkey_provided.t
similarity index 87%
rename from test/recipes/30-test_evp_fromdata.t
rename to test/recipes/30-test_evp_pkey_provided.t
index 0662de4cdd..669438caed 100644
--- a/test/recipes/30-test_evp_fromdata.t
+++ b/test/recipes/30-test_evp_pkey_provided.t
@@ -10,4 +10,4 @@
 
 use OpenSSL::Test::Simple;
 
-simple_test("test_evp_fromdata", "evp_fromdata_test");
+simple_test("test_evp_pkey_provided", "evp_pkey_provided_test");
diff --git a/util/find-doc-nits b/util/find-doc-nits
index da6e49f781..8dc258657c 100755
--- a/util/find-doc-nits
+++ b/util/find-doc-nits
@@ -140,6 +140,7 @@ sub name_synopsis {
     foreach my $line ( split /\n+/, $syn ) {
         next unless $line =~ /^\s/;
         my $sym;
+        my $is_prototype = 1;
         $line =~ s/STACK_OF\([^)]+\)/int/g;
         $line =~ s/SPARSE_ARRAY_OF\([^)]+\)/int/g;
         $line =~ s/__declspec\([^)]+\)//;
@@ -154,11 +155,13 @@ sub name_synopsis {
             $sym = $1;
         } elsif ( $line =~ /typedef.* (\S+);/ ) {
             # a simple typedef: typedef ... NAME;
+            $is_prototype = 0;
             $sym = $1;
         } elsif ( $line =~ /enum (\S*) \{/ ) {
             # an enumeration: enum ... {
             $sym = $1;
         } elsif ( $line =~ /#(?:define|undef) ([A-Za-z0-9_]+)/ ) {
+            $is_prototype = 0;
             $sym = $1;
         } elsif ( $line =~ /([A-Za-z0-9_]+)\(/ ) {
             $sym = $1;
@@ -172,7 +175,7 @@ sub name_synopsis {
 
         # Do some sanity checks on the prototype.
         err($id, "prototype missing spaces around commas: $line")
-            if ( $line =~ /[a-z0-9],[^ ]/ );
+            if $is_prototype && $line =~ /[a-z0-9],[^ ]/;
     }
 
     foreach my $n ( keys %names ) {
@@ -644,15 +647,16 @@ sub checkmacros {
             next unless /^#\s*define\s*(\S+)\(/;
             my $macro = $1;
             next if $docced{$macro} || defined $seen{$macro};
-            next if $macro =~ /i2d_/
-                || $macro =~ /d2i_/
-                || $macro =~ /DEPRECATEDIN/
-                || $macro =~ /IMPLEMENT_/
-                || $macro =~ /DECLARE_/;
+            next if $macro =~ /^i2d_/
+                || $macro =~ /^d2i_/
+                || $macro =~ /^DEPRECATEDIN/
+                || $macro =~ /_fnsig$/
+                || $macro =~ /^IMPLEMENT_/
+                || $macro =~ /^_?DECLARE_/;
 
             # Skip macros known to be missing
             next if $opt_v && grep( /^$macro$/, @missing);
-    
+
             err("$f:", "macro $macro undocumented")
                 if $opt_d || $opt_e;
             $count++;
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 0c2dc2c40b..32b502147c 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4878,3 +4878,34 @@ EVP_PKEY_meth_set_digestsign            ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_meth_set_digestverify          ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_meth_get_digestsign            ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_meth_get_digestverify          ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_up_ref                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_free                    ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_fetch                   ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_number                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_is_a                    ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_provider                ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_do_all_provided         ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_names_do_all            ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_settable_ctx_params     ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_new                 ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_get_serializer      ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_set_params          ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_free                ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_properties              ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_to_bio                  ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_to_fp                   ?	3_0_0	EXIST::FUNCTION:STDIO
+OSSL_SERIALIZER_CTX_new_by_EVP_PKEY     ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_set_cipher          ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_set_passphrase      ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_set_passphrase_cb   ?	3_0_0	EXIST::FUNCTION:
+OSSL_SERIALIZER_CTX_set_passphrase_ui   ?	3_0_0	EXIST::FUNCTION:
+ERR_load_OSSL_SERIALIZER_strings        ?	3_0_0	EXIST::FUNCTION:
+PEM_read_X509_PUBKEY                    ?	3_0_0	EXIST::FUNCTION:STDIO
+PEM_write_X509_PUBKEY                   ?	3_0_0	EXIST::FUNCTION:STDIO
+PEM_read_bio_X509_PUBKEY                ?	3_0_0	EXIST::FUNCTION:
+PEM_write_bio_X509_PUBKEY               ?	3_0_0	EXIST::FUNCTION:
+d2i_X509_PUBKEY_fp                      ?	3_0_0	EXIST::FUNCTION:STDIO
+i2d_X509_PUBKEY_fp                      ?	3_0_0	EXIST::FUNCTION:STDIO
+d2i_X509_PUBKEY_bio                     ?	3_0_0	EXIST::FUNCTION:
+i2d_X509_PUBKEY_bio                     ?	3_0_0	EXIST::FUNCTION:
+RSA_get0_pss_params                     ?	3_0_0	EXIST::FUNCTION:RSA
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 00b76abae5..6de82cfc21 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -455,6 +455,7 @@ ERR_load_PKCS12_strings
 ERR_load_PKCS7_strings
 ERR_load_RAND_strings
 ERR_load_RSA_strings
+ERR_load_OSSL_SERIALIZER_strings
 ERR_load_TS_strings
 ERR_load_UI_strings
 ERR_load_X509V3_strings
diff --git a/util/other.syms b/util/other.syms
index c5575ac151..e07471f9ab 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -36,6 +36,8 @@ OPENSSL_CTX                             datatype
 NAMING_AUTHORITY                        datatype
 OSSL_PARAM                              datatype
 OSSL_PROVIDER                           datatype
+OSSL_SERIALIZER                         datatype
+OSSL_SERIALIZER_CTX                     datatype
 OSSL_STORE_CTX                          datatype
 OSSL_STORE_INFO                         datatype
 OSSL_STORE_LOADER                       datatype
@@ -368,6 +370,12 @@ OSSL_PARAM_utf8_string                  define
 OSSL_PARAM_get_TYPE                     generic
 OSSL_PARAM_END                          define
 OSSL_PARAM_set_TYPE                     generic
+OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ        define
+OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ    define
+OSSL_SERIALIZER_Parameters_TO_PEM_PQ    define
+OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ       define
+OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ   define
+OSSL_SERIALIZER_Parameters_TO_TEXT_PQ   define
 PEM_FLAG_EAY_COMPATIBLE                 define
 PEM_FLAG_ONLY_B64                       define
 PEM_FLAG_SECURE                         define


More information about the openssl-commits mailing list