diff --git a/apps/pkcs8.c b/apps/pkcs8.c index e3932245f3..7b5e79966b 100644 --- a/apps/pkcs8.c +++ b/apps/pkcs8.c @@ -1,5 +1,5 @@ /* - * Copyright 1999-2021 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1999-2023 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 @@ -17,6 +17,9 @@ #include #include +#define STR(a) XSTR(a) +#define XSTR(a) #a + typedef enum OPTION_choice { OPT_COMMON, OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_IN, OPT_OUT, @@ -26,6 +29,7 @@ typedef enum OPTION_choice { #endif OPT_V2, OPT_V1, OPT_V2PRF, OPT_ITER, OPT_PASSIN, OPT_PASSOUT, OPT_TRADITIONAL, + OPT_SALTLEN, OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; @@ -53,7 +57,8 @@ const OPTIONS pkcs8_options[] = { {"traditional", OPT_TRADITIONAL, '-', "use traditional format private key"}, {"iter", OPT_ITER, 'p', "Specify the iteration count"}, {"noiter", OPT_NOITER, '-', "Use 1 as iteration count"}, - + {"saltlen", OPT_SALTLEN, 'p', "Specify the salt length (in bytes)"}, + {OPT_MORE_STR, 0, 0, "Default: 8 (For PBE1) or 16 (for PBE2)"}, #ifndef OPENSSL_NO_SCRYPT OPT_SECTION("Scrypt"), {"scrypt", OPT_SCRYPT, '-', "Use scrypt algorithm"}, @@ -88,6 +93,7 @@ int pkcs8_main(int argc, char **argv) #ifndef OPENSSL_NO_SCRYPT long scrypt_N = 0, scrypt_r = 0, scrypt_p = 0; #endif + int saltlen = 0; /* A value of zero chooses the default */ prog = opt_init(argc, argv, pkcs8_options); while ((o = opt_next()) != OPT_EOF) { @@ -189,6 +195,10 @@ int pkcs8_main(int argc, char **argv) goto opthelp; break; #endif + case OPT_SALTLEN: + if (!opt_int(opt_arg(), &saltlen)) + goto opthelp; + break; } } @@ -245,14 +255,14 @@ int pkcs8_main(int argc, char **argv) if (cipher) { #ifndef OPENSSL_NO_SCRYPT if (scrypt_N && scrypt_r && scrypt_p) - pbe = PKCS5_pbe2_set_scrypt(cipher, NULL, 0, NULL, + pbe = PKCS5_pbe2_set_scrypt(cipher, NULL, saltlen, NULL, scrypt_N, scrypt_r, scrypt_p); else #endif - pbe = PKCS5_pbe2_set_iv(cipher, iter, NULL, 0, NULL, + pbe = PKCS5_pbe2_set_iv(cipher, iter, NULL, saltlen, NULL, pbe_nid); } else { - pbe = PKCS5_pbe_set(pbe_nid, iter, NULL, 0); + pbe = PKCS5_pbe_set(pbe_nid, iter, NULL, saltlen); } if (pbe == NULL) { BIO_printf(bio_err, "Error setting PBE algorithm\n"); diff --git a/doc/man1/openssl-pkcs8.pod.in b/doc/man1/openssl-pkcs8.pod.in index 2af61203e9..bf87c590c7 100644 --- a/doc/man1/openssl-pkcs8.pod.in +++ b/doc/man1/openssl-pkcs8.pod.in @@ -27,6 +27,7 @@ B B [B<-scrypt_N> I] [B<-scrypt_r> I] [B<-scrypt_p> I

] +[B<-saltlen> I] {- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_engine_synopsis -}{- $OpenSSL::safe::opt_provider_synopsis -} @@ -148,6 +149,12 @@ B<-scrypt_p> and B<-v2> options. Sets the scrypt I, I or I

parameters. +=item B<-saltlen> + +Sets the length (in bytes) of the salt to use for the PBE algorithm. +If this value is not specified, the default for PBES2 is 16 (128 bits) +and 8 (64 bits) for PBES1. + {- $OpenSSL::safe::opt_r_item -} {- $OpenSSL::safe::opt_engine_item -} diff --git a/test/recipes/25-test_pkcs8.t b/test/recipes/25-test_pkcs8.t index 7a06be19e4..2f7ea1e1ae 100644 --- a/test/recipes/25-test_pkcs8.t +++ b/test/recipes/25-test_pkcs8.t @@ -15,7 +15,7 @@ use OpenSSL::Test qw/:DEFAULT srctop_file ok_nofips is_nofips/; setup("test_pkcs8"); -plan tests => 9; +plan tests => 15; ok(run(app(([ 'openssl', 'pkcs8', '-topk8', '-in', srctop_file('test', 'certs', 'pc5-key.pem'), @@ -31,7 +31,7 @@ ok(run(app(([ 'openssl', 'asn1parse', "Check the default size of the PBKDF2 PARAM 'salt length' is 16"); SKIP: { - skip "scrypt is not supported by this OpenSSL build", 2 + skip "scrypt is not supported by this OpenSSL build", 4 if disabled("scrypt"); ok(run(app(([ 'openssl', 'pkcs8', '-topk8', @@ -47,10 +47,25 @@ SKIP: { '-in', 'scrypt_default_saltlen.pem', '-offset', '34', '-length', '18']))), "Check the default size of the SCRYPT PARAM 'salt length' = 16"); + + ok(run(app(([ 'openssl', 'pkcs8', '-topk8', + '-in', srctop_file('test', 'certs', 'pc5-key.pem'), + '-scrypt', + '-saltlen', '8', + '-out', 'scrypt_64bit_saltlen.pem', + '-passout', 'pass:password']))), + "Convert a private key to PKCS5 v2.0 format using scrypt with a salt length of 8 bytes"); + +# We expect the output to be of the form "0:d=0 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:3C1147976A2B61CA" +# i.e. 2 byte header + 8 byte salt. + ok(run(app(([ 'openssl', 'asn1parse', + '-in', 'scrypt_64bit_saltlen.pem', + '-offset', '34', '-length', '10']))), + "Check the size of the SCRYPT PARAM 'salt length' is 8"); } SKIP: { - skip "legacy provider is not supported by this OpenSSL build", 2 + skip "legacy provider is not supported by this OpenSSL build", 4 if disabled('legacy') || disabled("des"); ok(run(app(([ 'openssl', 'pkcs8', '-topk8', @@ -66,8 +81,39 @@ SKIP: { '-in', 'pbe1.pem', '-offset', '19', '-length', '10']))), "Check the default size of the PBE PARAM 'salt length' = 8"); + + ok(run(app(([ 'openssl', 'pkcs8', '-topk8', + '-in', srctop_file('test', 'certs', 'pc5-key.pem'), + '-v1', "PBE-MD5-DES", + '-saltlen', '16', + '-provider', 'legacy', + '-provider', 'default', + '-out', 'pbe1_128bitsalt.pem', + '-passout', 'pass:password']))), + "Convert a private key to PKCS5 v1.5 format using pbeWithMD5AndDES-CBC with the 16 byte saltlen"); + + ok(run(app(([ 'openssl', 'asn1parse', + '-in', 'pbe1_128bitsalt.pem', + '-offset', '19', '-length', '18']))), + "Check the size of the PBE PARAM 'salt length' = 16"); }; + +ok(run(app(([ 'openssl', 'pkcs8', '-topk8', + '-in', srctop_file('test', 'certs', 'pc5-key.pem'), + '-saltlen', '8', + '-out', 'pbkdf2_64bit_saltlen.pem', + '-passout', 'pass:password']))), + "Convert a private key to PKCS5 v2.0 format using pbkdf2 with a salt length of 8 bytes"); + +# We expect the output to be of the form "0:d=0 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:3C1147976A2B61CA" +# i.e. 2 byte header + 8 byte salt. +ok(run(app(([ 'openssl', 'asn1parse', + '-in', 'pbkdf2_64bit_saltlen.pem', + '-offset', '34', '-length', '10']))), + "Check the size of the PBKDF2 PARAM 'salt length' is 8"); + + SKIP: { skip "SM2, SM3 or SM4 is not supported by this OpenSSL build", 3 if disabled("sm2") || disabled("sm3") || disabled("sm4");