diff options
-rw-r--r-- | crypto/evp/evp_pkey.c | 70 |
1 files changed, 51 insertions, 19 deletions
diff --git a/crypto/evp/evp_pkey.c b/crypto/evp/evp_pkey.c index b5a8f1cd72..766dfb46ec 100644 --- a/crypto/evp/evp_pkey.c +++ b/crypto/evp/evp_pkey.c @@ -12,6 +12,8 @@ #include "internal/cryptlib.h" #include <openssl/x509.h> #include <openssl/rand.h> +#include <openssl/encoder.h> +#include "internal/provider.h" #include "crypto/asn1.h" #include "crypto/evp.h" #include "crypto/x509.h" @@ -69,35 +71,65 @@ EVP_PKEY *EVP_PKCS82PKEY(const PKCS8_PRIV_KEY_INFO *p8) PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(const EVP_PKEY *pkey) { - PKCS8_PRIV_KEY_INFO *p8 = PKCS8_PRIV_KEY_INFO_new(); - if (p8 == NULL) { - EVPerr(EVP_F_EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE); - return NULL; - } + PKCS8_PRIV_KEY_INFO *p8 = NULL; + OSSL_ENCODER_CTX *ctx = NULL; + + /* + * The implementation for provider-native keys is to encode the + * key to a DER encoded PKCS#8 structure, then convert it to a + * PKCS8_PRIV_KEY_INFO with good old d2i functions. + */ + if (evp_pkey_is_provided(pkey)) { + int selection = OSSL_KEYMGMT_SELECT_ALL; + const OSSL_PROVIDER *prov = EVP_KEYMGMT_provider(pkey->keymgmt); + OSSL_LIB_CTX *libctx = ossl_provider_libctx(prov); + unsigned char *der = NULL; + size_t derlen = 0; + const unsigned char *pp; + + if ((ctx = OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, selection, + "DER", "pkcs8", + libctx, NULL)) == NULL + || !OSSL_ENCODER_to_data(ctx, &der, &derlen)) + goto error; - /* Force a key downgrade if that's possible */ - /* TODO(3.0) Is there a better way for provider-native keys? */ - if (EVP_PKEY_get0(pkey) == NULL) - goto error; + pp = der; + p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &pp, (long)derlen); + OPENSSL_free(der); + if (p8 == NULL) + goto error; + } else { + p8 = PKCS8_PRIV_KEY_INFO_new(); + if (p8 == NULL) { + EVPerr(EVP_F_EVP_PKEY2PKCS8, ERR_R_MALLOC_FAILURE); + return NULL; + } - if (pkey->ameth) { - if (pkey->ameth->priv_encode) { - if (!pkey->ameth->priv_encode(p8, pkey)) { - EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_PRIVATE_KEY_ENCODE_ERROR); + if (pkey->ameth != NULL) { + if (pkey->ameth->priv_encode != NULL) { + if (!pkey->ameth->priv_encode(p8, pkey)) { + EVPerr(EVP_F_EVP_PKEY2PKCS8, + EVP_R_PRIVATE_KEY_ENCODE_ERROR); + goto error; + } + } else { + EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_METHOD_NOT_SUPPORTED); goto error; } } else { - EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_METHOD_NOT_SUPPORTED); + EVPerr(EVP_F_EVP_PKEY2PKCS8, + EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM); goto error; } - } else { - EVPerr(EVP_F_EVP_PKEY2PKCS8, EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM); - goto error; } - return p8; + goto end; error: PKCS8_PRIV_KEY_INFO_free(p8); - return NULL; + p8 = NULL; + end: + OSSL_ENCODER_CTX_free(ctx); + return p8; + } /* EVP_PKEY attribute functions */ |