Commit c8af5fc081 for openssl.org
commit c8af5fc081e8914898c572ff1878c333ee7d4c8a
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Thu Sep 10 17:43:01 2026 +0200
Add symmetric key support to OSSL_STORE PKCS#12 loader
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Simo Sorce <simo@redhat.com>
MergeDate: Thu Sep 24 07:34:25 2026
(Merged from https://github.com/openssl/openssl/pull/30937)
diff --git a/crypto/store/store_result.c b/crypto/store/store_result.c
index 576e3d307e..0a0198ee32 100644
--- a/crypto/store/store_result.c
+++ b/crypto/store/store_result.c
@@ -566,9 +566,11 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
/* There is no specific object type for PKCS12 */
if (data->object_type == OSSL_OBJECT_UNKNOWN) {
/* Initial parsing */
- PKCS12 *p12;
+ PKCS12 *p12 = PKCS12_init_ex(NID_pkcs7_data, libctx, propq);
+ if (p12 == NULL)
+ return 0;
- p12 = d2i_PKCS12(NULL, (const unsigned char **)&data->octet_data,
+ p12 = d2i_PKCS12(&p12, (const unsigned char **)&data->octet_data,
(long)data->octet_data_size);
if (p12 != NULL) {
@@ -578,6 +580,8 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
STACK_OF(X509) *chain = NULL;
+ STACK_OF(EVP_SKEY) *skeys = NULL;
+ PKCS12_PARSE_CTX *pctx = NULL;
data->object_type = OSSL_OBJECT_PKCS12;
@@ -607,7 +611,7 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
pass = tpass;
/*
* ossl_pw_get_passphrase() does not NUL terminate but
- * we must do it for PKCS12_parse()
+ * we must do it for PKCS12_parse_ex()
*/
pass[tpass_len] = '\0';
if (!PKCS12_verify_mac(p12, pass, (int)tpass_len)) {
@@ -618,11 +622,19 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
}
}
- if (PKCS12_parse(p12, pass, &pkey, &cert, &chain)) {
+ if ((pctx = PKCS12_PARSE_CTX_new()) == NULL)
+ goto p12_end;
+ PKCS12_PARSE_CTX_set_pkey(pctx, &pkey);
+ PKCS12_PARSE_CTX_set_cert(pctx, &cert);
+ PKCS12_PARSE_CTX_set_ca(pctx, &chain);
+ PKCS12_PARSE_CTX_set_skeys(pctx, &skeys);
+
+ if (PKCS12_parse_ex(p12, pass, pctx, libctx, propq)) {
STACK_OF(OSSL_STORE_INFO) *infos = NULL;
OSSL_STORE_INFO *osi_pkey = NULL;
OSSL_STORE_INFO *osi_cert = NULL;
OSSL_STORE_INFO *osi_ca = NULL;
+ OSSL_STORE_INFO *osi_skey = NULL;
ok = 1; /* Parsing went through correctly! */
@@ -645,6 +657,16 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
else
ok = 0;
}
+ while (ok && sk_EVP_SKEY_num(skeys) > 0) {
+ EVP_SKEY *sk = sk_EVP_SKEY_value(skeys, 0);
+
+ if ((osi_skey = OSSL_STORE_INFO_new_SKEY(sk)) != NULL
+ && sk_EVP_SKEY_shift(skeys) != NULL
+ && sk_OSSL_STORE_INFO_push(infos, osi_skey) != 0)
+ osi_skey = NULL;
+ else
+ ok = 0;
+ }
while (ok && sk_X509_num(chain) > 0) {
X509 *ca = sk_X509_value(chain, 0);
@@ -659,15 +681,18 @@ static int try_pkcs12(struct extracted_param_data_st *data, OSSL_STORE_INFO **v,
EVP_PKEY_free(pkey);
X509_free(cert);
OSSL_STACK_OF_X509_free(chain);
+ sk_EVP_SKEY_pop_free(skeys, EVP_SKEY_free);
OSSL_STORE_INFO_free(osi_pkey);
OSSL_STORE_INFO_free(osi_cert);
OSSL_STORE_INFO_free(osi_ca);
+ OSSL_STORE_INFO_free(osi_skey);
if (!ok) {
sk_OSSL_STORE_INFO_pop_free(infos, OSSL_STORE_INFO_free);
infos = NULL;
}
ctx->cached_info = infos;
}
+ PKCS12_PARSE_CTX_free(pctx);
p12_end:
OPENSSL_cleanse(tpass, sizeof(tpass));
PKCS12_free(p12);
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index 55afbd259c..a9015c69ee 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 => 70 + ($no_fips ? 0 : 5);
+plan tests => 77 + ($no_fips ? 0 : 5);
# Test different PKCS#12 formats
ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
@@ -85,6 +85,7 @@ my $outfile4 = "out4.p12";
my $outfile5 = "out5.p12";
my $outfile6 = "out6.p12";
my $outfile7 = "out7.p12";
+my $outfile8 = "out8.p12";
# Test the -chain option with -untrusted
ok(run(app(["openssl", "pkcs12", "-export", "-chain",
@@ -177,6 +178,15 @@ ok(grep(/Trusted key usage (Oracle)/, @pkcs12info) == 0,
ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_outerr6_empty");
}
+# Export key + cert + distinct CA cert for combination tests
+ok(run(app(["openssl", "pkcs12", "-export",
+ "-inkey", srctop_file(@path, "ee-key.pem"),
+ "-in", srctop_file(@path, "ee-cert.pem"),
+ "-certfile", srctop_file(@path, "ca-cert.pem"),
+ "-passout", "pass:",
+ "-nomac", "-out", $outfile8])),
+ "test_export_pkcs12_key_cert_ca");
+
# Test dumping a PKCS#12 file whose private key is stored in an unencrypted
# keyBag (created with -keypbe NONE) rather than a shrouded keyBag.
{
@@ -467,6 +477,13 @@ ok(run(test(["pkcs12_api_test",
"mismatched_key_pass.p12"),
"-mismatched-key-pass",
])), "Test PKCS12_parse with mismatched key password");
+ok(run(test(["pkcs12_api_test",
+ "-in", $outfile8,
+ "-pass", "",
+ "-has-ca", 1,
+ "-has-key", 1,
+ "-has-cert", 1,
+ ])), "Test pkcs12_parse() key+cert+ca combinations");
# Test against CVE-2025-69421, octet parameter is expected, but
# NULL is being received and dereferenced
@@ -621,4 +638,37 @@ ok(run(test(["pkcs12_api_test",
"-num-skeys", "1",
])), "Test PKCS12_parse_ex() with symmetric key");
+# Test OSSL_STORE with Java symmetric key file
+{
+ my @output = run(app(["openssl", "storeutl",
+ "-passin", "pass:password",
+ srctop_file("test", "recipes", "80-test_pkcs12_data", "java-skey.p12")]),
+ capture => 1);
+ ok(@output > 0, "Test OSSL_STORE loads symmetric key from PKCS#12");
+
+ my $output_text = join("", @output);
+ like($output_text, qr/Symmetric key/, "OSSL_STORE output shows symmetric key");
+}
+
+
+# Test PKCS12_parse_ex() with multiple symmetric keys
+ok(run(test(["pkcs12_api_test",
+ "-in", srctop_file("test", "recipes", "80-test_pkcs12_data", "multi-skey.p12"),
+ "-pass", "password",
+ "-num-skeys", "2",
+ ])), "Test PKCS12_parse_ex() with multiple symmetric keys");
+
+# Test OSSL_STORE with multiple symmetric keys
+{
+ my @output = run(app(["openssl", "storeutl",
+ "-passin", "pass:password",
+ srctop_file("test", "recipes", "80-test_pkcs12_data", "multi-skey.p12")]),
+ capture => 1);
+ ok(@output > 0, "Test OSSL_STORE loads multiple symmetric keys from PKCS#12");
+
+ my $output_text = join("", @output);
+ my @skey_matches = ($output_text =~ /Symmetric key/g);
+ ok(scalar @skey_matches == 2, "OSSL_STORE output shows two symmetric keys");
+}
+
SetConsoleOutputCP($savedcp) if (defined($savedcp));