Commit 95f35c26f3 for openssl.org
commit 95f35c26f3482e9f05391232d1645fca73da0be5
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Thu Sep 24 15:59:26 2026 +0200
Dealing with symmetric keys in pkcs12 command-line utility
This is a follow-up to #30937
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Simo Sorce <simo@redhat.com>
MergeDate: Tue Sep 29 18:34:13 2026
(Merged from https://github.com/openssl/openssl/pull/32978)
diff --git a/apps/pkcs12.c b/apps/pkcs12.c
index 2deb87a1f8..f97149f8f7 100644
--- a/apps/pkcs12.c
+++ b/apps/pkcs12.c
@@ -1036,6 +1036,51 @@ int dump_certs_pkeys_bags(BIO *out, const STACK_OF(PKCS12_SAFEBAG) *bags,
return 1;
}
+static int extract_symmetric_key(BIO *out, const PKCS12_SAFEBAG *bag,
+ const char *pass, int passlen, int options, const EVP_CIPHER *enc)
+{
+ PKCS8_PRIV_KEY_INFO *p8;
+ EVP_SKEY *skey = NULL;
+
+ if (options & NOKEYS)
+ return 1;
+
+ p8 = PKCS12_decrypt_secretbag(bag, pass, passlen,
+ app_get0_libctx(), app_get0_propq());
+ if (p8 == NULL)
+ return 0;
+ skey = PKCS8_PRIV_KEY_INFO_get1_skey(p8, app_get0_libctx(),
+ app_get0_propq(), NULL, 0);
+ PKCS8_PRIV_KEY_INFO_free(p8);
+ if (skey == NULL)
+ return 0;
+ if (enc == NULL) {
+ const unsigned char *kdata = NULL;
+ size_t klen = 0;
+
+ EVP_SKEY_get0_raw_key(skey, &kdata, &klen);
+
+ if (kdata != NULL && klen > 0) {
+ BIO_puts(bio_err, "Bag Value: ");
+ BIO_puts(out, "\n Key Data: ");
+ hex_print(out, kdata, (int)klen);
+
+ BIO_printf(bio_err, " Key management: %s\n", EVP_SKEY_get0_skeymgmt_name(skey));
+ BIO_printf(bio_err, " Key Length: %zu bytes\n", klen);
+ } else {
+ BIO_printf(bio_err, "Error obtaining key bytes\n");
+ EVP_SKEY_free(skey);
+ return 0;
+ }
+ } else {
+ BIO_puts(bio_err, "Bag Value: ");
+ print_attribute(out, PKCS12_SAFEBAG_get0_bag_obj(bag));
+ }
+
+ EVP_SKEY_free(skey);
+ return 1;
+}
+
int dump_certs_pkeys_bag(BIO *out, const PKCS12_SAFEBAG *bag,
const char *pass, int passlen, int options,
char *pempass, const EVP_CIPHER *enc)
@@ -1115,8 +1160,12 @@ int dump_certs_pkeys_bag(BIO *out, const PKCS12_SAFEBAG *bag,
print_attribs(out, attrs, "Bag Attributes");
BIO_puts(bio_err, "Bag Type: ");
i2a_ASN1_OBJECT(bio_err, PKCS12_SAFEBAG_get0_bag_type(bag));
- BIO_puts(bio_err, "\nBag Value: ");
- print_attribute(out, PKCS12_SAFEBAG_get0_bag_obj(bag));
+ BIO_puts(bio_err, "\n");
+
+ if (PKCS12_SAFEBAG_get_bag_nid(bag) == NID_pkcs8ShroudedKeyBag)
+ return extract_symmetric_key(out, bag, pass, passlen, options, enc);
+ else
+ print_attribute(out, PKCS12_SAFEBAG_get0_bag_obj(bag));
return 1;
case NID_safeContentsBag:
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index a9015c69ee..16652b1fb1 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -57,7 +57,7 @@ $ENV{OPENSSL_WIN32_UTF8}=1;
my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0);
my $no_err = disabled('err') || disabled('autoerrinit');
-plan tests => 77 + ($no_fips ? 0 : 5);
+plan tests => 117 + ($no_fips ? 0 : 5);
# Test different PKCS#12 formats
ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
@@ -630,6 +630,116 @@ unless ($no_fips) {
}
}
+# Test Java PKCS#12 files with symmetric keys
+{
+ my $java_p12 = srctop_file("test", "recipes", "80-test_pkcs12_data", "java-skey.p12");
+ my $java_err = "java-skey.err";
+ my @err_lines;
+ my $fh;
+ my $key_pattern = "41 41 41 41 41 41";
+
+ # Test 1: Info display shows bag attributes
+ my @java_pkcs12info = run(app(["openssl", "pkcs12", "-info", "-in", $java_p12,
+ "-passin", "pass:password"]), capture => 1);
+
+ ok(grep(/friendlyName:\s+my-explicit-key/, @java_pkcs12info) == 1,
+ "test Java PKCS#12 friendly name in output");
+
+ ok(grep(/localKeyID:/, @java_pkcs12info) == 1,
+ "test Java PKCS#12 localKeyID in output");
+
+ # Test 2: Default extraction (without -noenc) should NOT output key data
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-in", $java_p12, "-nocerts",
+ "-passin", "pass:password"], stderr => $java_err), capture => 1);
+ ok(scalar @java_pkcs12info > 0,
+ "test Java PKCS#12 extract without -noenc");
+
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok(grep(/Bag Attributes/, @java_pkcs12info) == 1,
+ "test extracted output contains bag attributes");
+ ok(grep(/Key management/, @err_lines) == 0 && grep(/Key Length:/, @err_lines) == 0,
+ "test extracted output does not contain algorithm and key length without -noenc");
+ ok(grep(/$key_pattern/, @java_pkcs12info, @err_lines) == 0,
+ "test extracted output does NOT contain hex key bytes");
+
+ # Test 3: Extract with -noenc should output key data in hex
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-in", $java_p12, "-nocerts",
+ "-passin", "pass:password", "-noenc"], stderr => $java_err), capture => 1);
+ ok(scalar @java_pkcs12info > 0,
+ "test Java PKCS#12 extract with -noenc");
+
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok(grep(/Bag Attributes/, @java_pkcs12info) == 1,
+ "test extracted key contains bag attributes");
+ ok(grep(/Key management/, @err_lines) == 1
+ && grep(/Key Length: 32 bytes/, @err_lines) == 1 && grep(/Key Data:/, @java_pkcs12info) == 1,
+ "test extracted key contains algorithm, key length and key data labels");
+ ok(grep(/$key_pattern/, @java_pkcs12info) == 1,
+ "test extracted key contains hex data");
+
+ # Test 4: -info -noout does not write to the output BIO
+ ok(run(app(["openssl", "pkcs12", "-info", "-noout", "-in", $java_p12,
+ "-passin", "pass:password"])),
+ "test Java PKCS#12 -info -noout succeeds");
+
+ # Test 5: -noout should not output key material
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-noout", "-in", $java_p12,
+ "-passin", "pass:password"], stderr => $java_err),
+ capture => 1, statusvar => \my $noout_exit);
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($noout_exit, "test Java PKCS#12 -noout succeeds");
+ ok(grep(/$key_pattern/, @java_pkcs12info, @err_lines) == 0,
+ "test Java PKCS#12 -noout does not output key hex data");
+ ok(grep(/Key Data:/, @java_pkcs12info) == 0,
+ "test Java PKCS#12 -noout does not output Key Data label");
+
+ # Test 6: -noout -noenc should still suppress key material
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-noout", "-noenc", "-in", $java_p12,
+ "-passin", "pass:password"], stderr => $java_err),
+ capture => 1, statusvar => \my $noout_noenc_exit);
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($noout_noenc_exit, "test Java PKCS#12 -noout -noenc succeeds");
+ ok(grep(/$key_pattern/, @java_pkcs12info, @err_lines) == 0,
+ "test Java PKCS#12 -noout -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @java_pkcs12info) == 0,
+ "test Java PKCS#12 -noout -noenc does not output Key Data label");
+
+ # Test 7: -nokeys -noenc should suppress key material
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-nokeys", "-noenc", "-in", $java_p12,
+ "-passin", "pass:password"], stderr => $java_err),
+ capture => 1, statusvar => \my $nokeys_noenc_exit);
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($nokeys_noenc_exit, "test Java PKCS#12 -nokeys -noenc succeeds");
+ ok(grep(/$key_pattern/, @java_pkcs12info, @err_lines) == 0,
+ "test Java PKCS#12 -nokeys -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @java_pkcs12info) == 0,
+ "test Java PKCS#12 -nokeys -noenc does not output Key Data label");
+
+ # Test 8: -info -noout -noenc should suppress key material
+ @java_pkcs12info = run(app(["openssl", "pkcs12", "-info", "-noout", "-noenc", "-in", $java_p12,
+ "-passin", "pass:password"], stderr => $java_err),
+ capture => 1, statusvar => \my $info_noout_noenc_exit);
+ open($fh, '<', $java_err) or die "Can't open file $java_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($info_noout_noenc_exit, "test Java PKCS#12 -info -noout -noenc succeeds");
+ ok(grep(/$key_pattern/, @java_pkcs12info, @err_lines) == 0,
+ "test Java PKCS#12 -info -noout -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @java_pkcs12info) == 0,
+ "test Java PKCS#12 -info -noout -noenc does not output Key Data label");
+
+ unlink $java_err;
+}
# Test PKCS12_parse_ex() with Java symmetric key file
ok(run(test(["pkcs12_api_test",
@@ -650,6 +760,102 @@ ok(run(test(["pkcs12_api_test",
like($output_text, qr/Symmetric key/, "OSSL_STORE output shows symmetric key");
}
+# Test PKCS#12 files with multiple symmetric keys
+{
+ my $multi_p12 = srctop_file("test", "recipes", "80-test_pkcs12_data", "multi-skey.p12");
+ my $mskey_err = "mskey_stderr.txt";
+ my @err_lines;
+ my $fh;
+ my $key1 = "01 01 01 01 01 01 01 01";
+ my $key2 = "02 02 02 02 02 02 02 02";
+
+ # Test 1: Info display shows both keys with their friendly names
+ my @multi_info = run(app(["openssl", "pkcs12", "-info", "-in", $multi_p12,
+ "-passin", "pass:password"]), capture => 1);
+
+ ok(grep(/friendlyName:\s+key1/, @multi_info) == 1,
+ "test multi-skey PKCS#12 shows key1 friendly name");
+
+ ok(grep(/friendlyName:\s+key2/, @multi_info) == 1,
+ "test multi-skey PKCS#12 shows key2 friendly name");
+
+ # Test 2: Default extraction shows both keys with metadata
+ @multi_info = run(app(["openssl", "pkcs12", "-in", $multi_p12, "-nocerts",
+ "-passin", "pass:password"],
+ stderr => $mskey_err), capture => 1);
+
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok(grep(/Key Length:/, @multi_info, @err_lines) == 0,
+ "test multi-skey output does not show key lengths without -noenc");
+ ok(grep(/$key1/, @multi_info, @err_lines) == 0 && grep(/$key2/, @multi_info, @err_lines) == 0,
+ "test multi-skey output without -noenc doesn't contain raw key hex data");
+
+ # Test 3: Extract with -noenc should output both keys in hex
+ @multi_info = run(app(["openssl", "pkcs12", "-in", $multi_p12, "-nocerts",
+ "-passin", "pass:password", "-noenc"], stderr => $mskey_err), capture => 1),
+
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok(grep(/$key1/, @multi_info, @err_lines) == 1 && grep(/$key2/, @multi_info, @err_lines) == 1,
+ "test multi-skey output with -noenc contains raw key hex data");
+
+ # Test 4: -noout should not output any key material
+ @multi_info = run(app(["openssl", "pkcs12", "-noout", "-in", $multi_p12,
+ "-passin", "pass:password"], stderr => $mskey_err),
+ capture => 1, statusvar => \my $m_noout_exit);
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($m_noout_exit, "test multi-skey -noout succeeds");
+ ok(grep(/$key1/, @multi_info, @err_lines) == 0 && grep(/$key2/, @multi_info, @err_lines) == 0,
+ "test multi-skey -noout does not output key hex data");
+ ok(grep(/Key Data:/, @multi_info) == 0,
+ "test multi-skey -noout does not output Key Data label");
+
+ # Test 5: -noout -noenc should still suppress key material
+ @multi_info = run(app(["openssl", "pkcs12", "-noout", "-noenc", "-in", $multi_p12,
+ "-passin", "pass:password"], stderr => $mskey_err),
+ capture => 1, statusvar => \my $m_noout_noenc_exit);
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($m_noout_noenc_exit, "test multi-skey -noout -noenc succeeds");
+ ok(grep(/$key1/, @multi_info, @err_lines) == 0 && grep(/$key2/, @multi_info, @err_lines) == 0,
+ "test multi-skey -noout -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @multi_info) == 0,
+ "test multi-skey -noout -noenc does not output Key Data label");
+
+ # Test 6: -nokeys -noenc should suppress key material
+ @multi_info = run(app(["openssl", "pkcs12", "-nokeys", "-noenc", "-in", $multi_p12,
+ "-passin", "pass:password"], stderr => $mskey_err),
+ capture => 1, statusvar => \my $m_nokeys_noenc_exit);
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($m_nokeys_noenc_exit, "test multi-skey -nokeys -noenc succeeds");
+ ok(grep(/$key1/, @multi_info, @err_lines) == 0 && grep(/$key2/, @multi_info, @err_lines) == 0,
+ "test multi-skey -nokeys -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @multi_info) == 0,
+ "test multi-skey -nokeys -noenc does not output Key Data label");
+
+ # Test 7: -info -noout -noenc should suppress key material
+ @multi_info = run(app(["openssl", "pkcs12", "-info", "-noout", "-noenc", "-in", $multi_p12,
+ "-passin", "pass:password"], stderr => $mskey_err),
+ capture => 1, statusvar => \my $m_info_noout_noenc_exit);
+ open $fh, '<', $mskey_err or die "Cannot open $mskey_err: $!";
+ @err_lines = <$fh>;
+ close $fh;
+ ok($m_info_noout_noenc_exit, "test multi-skey -info -noout -noenc succeeds");
+ ok(grep(/$key1/, @multi_info, @err_lines) == 0 && grep(/$key2/, @multi_info, @err_lines) == 0,
+ "test multi-skey -info -noout -noenc does not output key hex data");
+ ok(grep(/Key Data:/, @multi_info) == 0,
+ "test multi-skey -info -noout -noenc does not output Key Data label");
+
+ unlink $mskey_err;
+}
# Test PKCS12_parse_ex() with multiple symmetric keys
ok(run(test(["pkcs12_api_test",