2
0
mirror of https://github.com/openssl/openssl.git synced 2025-04-24 20:51:14 +08:00

Implement i2d_PKCS8PrivateKey

Added `i2d_PKCS8PrivateKey(3)` API to complement `i2d_PrivateKey(3)`,
the former always outputs PKCS#8.

Extended endecoder_test.c to check that `i2d_PKCS8PrivateKey()`
produces the expected PKCS#8 output.

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/27309)
This commit is contained in:
Viktor Dukhovni 2025-04-09 17:55:03 +10:00 committed by Matt Caswell
parent 85a8eba567
commit 8d2e4d6d8c
6 changed files with 89 additions and 15 deletions

@ -91,20 +91,25 @@ int i2d_KeyParams_bio(BIO *bp, const EVP_PKEY *pkey)
return ASN1_i2d_bio_of(EVP_PKEY, i2d_KeyParams, bp, pkey);
}
int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
static int
i2d_PrivateKey_impl(const EVP_PKEY *a, unsigned char **pp, int traditional)
{
if (evp_pkey_is_provided(a)) {
static const struct type_and_structure_st output_info[] = {
static const struct type_and_structure_st trad_output_info[] = {
{ "DER", "type-specific" },
{ "DER", "PrivateKeyInfo" },
{ NULL, }
};
const struct type_and_structure_st *oi = trad_output_info;
return i2d_provided(a, EVP_PKEY_KEYPAIR, output_info, pp);
if (!traditional)
++oi;
return i2d_provided(a, EVP_PKEY_KEYPAIR, oi, pp);
}
if (a->ameth != NULL && a->ameth->old_priv_encode != NULL) {
if (traditional && a->ameth != NULL && a->ameth->old_priv_encode != NULL)
return a->ameth->old_priv_encode(a, pp);
}
if (a->ameth != NULL && a->ameth->priv_encode != NULL) {
PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8(a);
int ret = 0;
@ -119,6 +124,16 @@ int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
return -1;
}
int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp)
{
return i2d_PrivateKey_impl(a, pp, 1);
}
int i2d_PKCS8PrivateKey(const EVP_PKEY *a, unsigned char **pp)
{
return i2d_PrivateKey_impl(a, pp, 0);
}
int i2d_PublicKey(const EVP_PKEY *a, unsigned char **pp)
{
if (evp_pkey_is_provided(a)) {

@ -41,9 +41,12 @@ corresponding B<PEM> function as described in L<PEM_read_PrivateKey(3)>.
These functions are currently the only way to store encrypted private keys using DER format.
Currently all the functions use BIOs or FILE pointers, there are no functions which
work directly on memory: this can be readily worked around by converting the buffers
to memory BIOs, see L<BIO_s_mem(3)> for details.
Currently all the functions use BIOs or FILE pointers, there are no functions
which support password-protection and work directly on memory: this can be
readily worked around by converting the buffers to memory BIOs, see
L<BIO_s_mem(3)> for details.
If password-protection is not required, the L<i2d_PKCS8PrivateKey(3)> function
encodes a private key to an unencrypted DER B<PKCS#8> form.
These functions make no assumption regarding the pass phrase received from the
password callback.
@ -59,9 +62,14 @@ and i2d_PKCS8PrivateKey_nid_fp() return 1 on success or 0 on error.
=head1 SEE ALSO
L<i2d_PKCS8PrivateKey(3)>,
L<PEM_read_PrivateKey(3)>,
L<passphrase-encoding(7)>
=head1 HISTORY
L<i2d_PKCS8PrivateKey(3)> was added in OpenSSL 3.6.
=head1 COPYRIGHT
Copyright 2002-2018 The OpenSSL Project Authors. All Rights Reserved.

@ -3,10 +3,10 @@
=head1 NAME
d2i_PrivateKey_ex, d2i_PrivateKey, d2i_PublicKey, d2i_KeyParams,
d2i_AutoPrivateKey_ex, d2i_AutoPrivateKey, i2d_PrivateKey, i2d_PublicKey,
i2d_KeyParams, i2d_KeyParams_bio, d2i_PrivateKey_ex_bio, d2i_PrivateKey_bio,
d2i_PrivateKey_ex_fp, d2i_PrivateKey_fp, d2i_KeyParams_bio, i2d_PrivateKey_bio,
i2d_PrivateKey_fp
d2i_AutoPrivateKey_ex, d2i_AutoPrivateKey, i2d_PrivateKey,
i2d_PKCS8PrivateKey, i2d_PublicKey, i2d_KeyParams, i2d_KeyParams_bio,
d2i_PrivateKey_ex_bio, d2i_PrivateKey_bio, d2i_PrivateKey_ex_fp,
d2i_PrivateKey_fp, d2i_KeyParams_bio, i2d_PrivateKey_bio, i2d_PrivateKey_fp
- decode and encode functions for reading and saving EVP_PKEY structures
=head1 SYNOPSIS
@ -29,6 +29,7 @@ i2d_PrivateKey_fp
long length);
int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp);
int i2d_PKCS8PrivateKey(const EVP_PKEY *a, unsigned char **pp);
int i2d_PublicKey(const EVP_PKEY *a, unsigned char **pp);
int i2d_KeyParams(const EVP_PKEY *a, unsigned char **pp);
int i2d_KeyParams_bio(BIO *bp, const EVP_PKEY *pkey);
@ -77,6 +78,8 @@ to automatically detect the private key format.
i2d_PrivateKey() encodes I<a>. It uses a key specific format or, if none is
defined for that key type, PKCS#8 unencrypted PrivateKeyInfo format.
i2d_PKCS8PrivateKey() does the same using only the PKCS#8 unencrypted
PrivateKeyInfo format.
i2d_PublicKey() does the same for public keys.
i2d_KeyParams() does the same for key parameters.
These functions are similar to the d2i_X509() functions; see L<d2i_X509(3)>.
@ -106,9 +109,9 @@ d2i_PrivateKey_ex_fp(), d2i_PrivateKey_fp(), d2i_PublicKey(), d2i_KeyParams()
and d2i_KeyParams_bio() functions return a valid B<EVP_PKEY> structure or NULL if
an error occurs. The error code can be obtained by calling L<ERR_get_error(3)>.
i2d_PrivateKey(), i2d_PublicKey() and i2d_KeyParams() return the number of
bytes successfully encoded or a negative value if an error occurs. The error
code can be obtained by calling L<ERR_get_error(3)>.
i2d_PrivateKey(), i2d_PKCS8PrivateKey(), i2d_PublicKey() and i2d_KeyParams()
return the number of bytes successfully encoded or a negative value if an error
occurs. The error code can be obtained by calling L<ERR_get_error(3)>.
i2d_PrivateKey_bio(), i2d_PrivateKey_fp() and i2d_KeyParams_bio() return 1 if
successfully encoded or zero if an error occurs.
@ -123,6 +126,8 @@ L<d2i_PKCS8PrivateKey_bio(3)>
d2i_PrivateKey_ex(), d2i_PrivateKey_ex_bio(), d2i_PrivateKey_ex_fp(), and
d2i_AutoPrivateKey_ex() were added in OpenSSL 3.0.
i2d_PKCS8PrivateKey() was added in OpenSSL 3.6.
=head1 COPYRIGHT
Copyright 2017-2021 The OpenSSL Project Authors. All Rights Reserved.

@ -1457,6 +1457,7 @@ EVP_PKEY *d2i_AutoPrivateKey_ex(EVP_PKEY **a, const unsigned char **pp,
EVP_PKEY *d2i_AutoPrivateKey(EVP_PKEY **a, const unsigned char **pp,
long length);
int i2d_PrivateKey(const EVP_PKEY *a, unsigned char **pp);
int i2d_PKCS8PrivateKey(const EVP_PKEY *a, unsigned char **pp);
int i2d_KeyParams(const EVP_PKEY *a, unsigned char **pp);
EVP_PKEY *d2i_KeyParams(int type, EVP_PKEY **a, const unsigned char **pp,

@ -286,6 +286,34 @@ static int encode_EVP_PKEY_prov(const char *file, const int line,
return ok;
}
static int encode_EVP_PKEY_i2d(const char *unused_file,
const int unused_line,
void **encoded, long *encoded_len,
void *object, int unused_selection,
const char *unused_output_type,
const char *unused_output_structure,
const char *unused_pass,
const char *unused_pcipher)
{
EVP_PKEY *pkey = object;
unsigned char *buf = NULL, *p;
int len, ok = 0;
if (!TEST_int_gt((len = i2d_PKCS8PrivateKey(pkey, NULL)), 0)
|| !TEST_ptr(p = buf = OPENSSL_malloc(len))
|| !TEST_int_eq(i2d_PKCS8PrivateKey(pkey, &p), len)
|| !TEST_int_eq((int)(p - buf), len))
goto end;
*encoded = buf;
*encoded_len = len;
buf = NULL;
ok = 1;
end:
OPENSSL_free(buf);
return ok;
}
static int decode_EVP_PKEY_prov(const char *file, const int line,
void **object, void *encoded, long encoded_len,
const char *input_type,
@ -567,6 +595,17 @@ static int test_unprotected_via_DER(const char *type, EVP_PKEY *key, int fips)
dump_der, fips ? 0 : FLAG_FAIL_IF_FIPS);
}
static int test_unprotected_via_i2d(const char *type, EVP_PKEY *key, int fips)
{
return test_encode_decode(__FILE__, __LINE__, type, key,
OSSL_KEYMGMT_SELECT_KEYPAIR
| OSSL_KEYMGMT_SELECT_ALL_PARAMETERS,
"DER", "PrivateKeyInfo", NULL, NULL,
encode_EVP_PKEY_i2d, decode_EVP_PKEY_prov,
test_mem, check_unprotected_PKCS8_DER,
dump_der, fips ? 0 : FLAG_FAIL_IF_FIPS);
}
static int check_unprotected_PKCS8_PEM(const char *file, const int line,
const char *type,
const void *data, size_t data_len)
@ -906,6 +945,10 @@ static int test_public_via_MSBLOB(const char *type, EVP_PKEY *key)
{ \
return test_unprotected_via_DER(KEYTYPEstr, key_##KEYTYPE, fips); \
} \
static int test_unprotected_##KEYTYPE##_via_i2d(void) \
{ \
return test_unprotected_via_i2d(KEYTYPEstr, key_##KEYTYPE, fips); \
} \
static int test_unprotected_##KEYTYPE##_via_PEM(void) \
{ \
return test_unprotected_via_PEM(KEYTYPEstr, key_##KEYTYPE, fips); \
@ -929,6 +972,7 @@ static int test_public_via_MSBLOB(const char *type, EVP_PKEY *key)
#define ADD_TEST_SUITE(KEYTYPE) \
ADD_TEST(test_unprotected_##KEYTYPE##_via_DER); \
ADD_TEST(test_unprotected_##KEYTYPE##_via_i2d); \
ADD_TEST(test_unprotected_##KEYTYPE##_via_PEM); \
ADD_TEST(test_protected_##KEYTYPE##_via_DER); \
ADD_TEST(test_protected_##KEYTYPE##_via_PEM); \

@ -5925,3 +5925,4 @@ OSSL_AA_DIST_POINT_new ? 3_5_0 EXIST::FUNCTION:
OSSL_AA_DIST_POINT_it ? 3_5_0 EXIST::FUNCTION:
PEM_ASN1_write_bio_ctx ? 3_5_0 EXIST::FUNCTION:
OPENSSL_sk_set_thunks ? 3_6_0 EXIST::FUNCTION:
i2d_PKCS8PrivateKey ? 3_6_0 EXIST::FUNCTION: