]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Implement i2d_PKCS8PrivateKey
authorViktor Dukhovni <openssl-users@dukhovni.org>
Wed, 9 Apr 2025 07:55:03 +0000 (17:55 +1000)
committerMatt Caswell <matt@openssl.org>
Fri, 11 Apr 2025 10:00:27 +0000 (11:00 +0100)
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)

crypto/asn1/i2d_evp.c
doc/man3/d2i_PKCS8PrivateKey_bio.pod
doc/man3/d2i_PrivateKey.pod
include/openssl/evp.h
test/endecode_test.c
util/libcrypto.num

index 106ea152733c504e95b0075f4781215ab91ae302..70af08ab0fc48e1446e04f54df7a2092a6e77fc8 100644 (file)
@@ -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)) {
index 55ec34657504025c7a954c9e4743c2d7f9814626..0408f6d4d90f6c83e28b194cfe898c6d890ef0b6 100644 (file)
@@ -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.
index fe78d5bc6f131615dda64b6079fdb5a8ad475715..492a5cc2e4e2819a75ce8421aae99a4a07e9bbd4 100644 (file)
@@ -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.
index e5da1e64151bdff9f222f560acb6e49766d36306..8e38ab29ea365b3e5545694f37f79ecaf6cb6eed 100644 (file)
@@ -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,
index 028deb4ed134a9dde7bf8daa1292e80f86139e4a..d6ee5a59b916e643f239fdc30ff861ffd20aa797 100644 (file)
@@ -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);               \
index 322c7b42d61d4e3d9e2b3382b35471d4008ce999..0242023c01f17220513421345ec0f5e2d95c481e 100644 (file)
@@ -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: