Commit 89c6d4d405 for openssl.org
commit 89c6d4d40562f5a293a6b50113dfafed32b0a816
Author: Dmitry Belyavskiy <beldmit@gmail.com>
Date: Tue Apr 21 19:10:34 2026 +0200
Add support for Java keytool PKCS#12 files with symmetric secret keys
Introduce PKCS12_PARSE_CTX opaque context for PKCS12_parse_ex(), replacing
the growing parameter list pattern. Add PKCS12_decrypt_secretbag() for
decrypting secret bags and PKCS12_SAFEBAG_get1_skey() for converting
decrypted PKCS8 data to EVP_SKEY, following the existing private key
pattern (PKCS12_decrypt_skey_ex + EVP_PKCS82PKEY_ex).
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: Thu Sep 24 07:34:22 2026
(Merged from https://github.com/openssl/openssl/pull/30937)
diff --git a/.gitignore b/.gitignore
index 82abffd4ba..c3d9526f2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,7 @@
/include/openssl/ct.h
/include/openssl/err.h
/include/openssl/ess.h
+/include/openssl/evp.h
/include/openssl/fipskey.h
/include/openssl/lhash.h
/include/openssl/ocsp.h
diff --git a/build.info b/build.info
index 80aeff4f94..7d129700e5 100644
--- a/build.info
+++ b/build.info
@@ -60,6 +60,7 @@ DEPEND[]=include/openssl/asn1.h \
include/openssl/ct.h \
include/openssl/err.h \
include/openssl/ess.h \
+ include/openssl/evp.h \
include/openssl/fipskey.h \
include/openssl/lhash.h \
{- $err_headers -} \
@@ -200,6 +201,7 @@ GENERATE[include/openssl/crypto.h]=include/openssl/crypto.h.in
GENERATE[include/openssl/ct.h]=include/openssl/ct.h.in
GENERATE[include/openssl/err.h]=include/openssl/err.h.in
GENERATE[include/openssl/ess.h]=include/openssl/ess.h.in
+GENERATE[include/openssl/evp.h]=include/openssl/evp.h.in
GENERATE[include/openssl/fipskey.h]=include/openssl/fipskey.h.in
GENERATE[include/openssl/lhash.h]=include/openssl/lhash.h.in
GENERATE[include/openssl/ocsp.h]=include/openssl/ocsp.h.in
diff --git a/crypto/pkcs12/p12_add.c b/crypto/pkcs12/p12_add.c
index 1217317783..1d330e4701 100644
--- a/crypto/pkcs12/p12_add.c
+++ b/crypto/pkcs12/p12_add.c
@@ -149,8 +149,9 @@ PKCS7 *PKCS12_pack_p7encdata(int pbe_nid, const char *pass, int passlen,
iter, bags, NULL, NULL);
}
-STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7encdata(PKCS7 *p7, const char *pass,
- int passlen)
+STACK_OF(PKCS12_SAFEBAG) *ossl_pkcs12_unpack_p7encdata_ex(PKCS7 *p7,
+ const char *pass, int passlen,
+ OSSL_LIB_CTX *libctx, const char *propq)
{
if (!PKCS7_type_is_encrypted(p7))
return NULL;
@@ -164,6 +165,13 @@ STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7encdata(PKCS7 *p7, const char *pass,
ASN1_ITEM_rptr(PKCS12_SAFEBAGS),
pass, passlen,
p7->d.encrypted->enc_data->enc_data, 1,
+ libctx, propq);
+}
+
+STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7encdata(PKCS7 *p7, const char *pass,
+ int passlen)
+{
+ return ossl_pkcs12_unpack_p7encdata_ex(p7, pass, passlen,
p7->ctx.libctx, p7->ctx.propq);
}
@@ -180,6 +188,35 @@ PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_skey(const PKCS12_SAFEBAG *bag,
return PKCS12_decrypt_skey_ex(bag, pass, passlen, NULL, NULL);
}
+PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_secretbag(const PKCS12_SAFEBAG *bag,
+ const char *pass, int passlen,
+ OSSL_LIB_CTX *ctx, const char *propq)
+{
+ const ASN1_TYPE *bag_obj;
+ const unsigned char *p;
+ X509_SIG *p8enc = NULL;
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+
+ if (PKCS12_SAFEBAG_get_nid(bag) != NID_secretBag)
+ return NULL;
+ if (PKCS12_SAFEBAG_get_bag_nid(bag) != NID_pkcs8ShroudedKeyBag)
+ return NULL;
+
+ bag_obj = PKCS12_SAFEBAG_get0_bag_obj(bag);
+ if (bag_obj == NULL || bag_obj->type != V_ASN1_OCTET_STRING)
+ return NULL;
+
+ p = ASN1_STRING_get0_data(bag_obj->value.octet_string);
+ p8enc = d2i_X509_SIG(NULL, &p,
+ (unsigned int)ASN1_STRING_get_length(bag_obj->value.octet_string));
+ if (p8enc == NULL)
+ return NULL;
+
+ p8inf = PKCS8_decrypt_ex(p8enc, pass, passlen, ctx, propq);
+ X509_SIG_free(p8enc);
+ return p8inf;
+}
+
int PKCS12_pack_authsafes(PKCS12 *p12, STACK_OF(PKCS7) *safes)
{
if (ASN1_item_pack(safes, ASN1_ITEM_rptr(PKCS12_AUTHSAFES),
@@ -188,10 +225,10 @@ int PKCS12_pack_authsafes(PKCS12 *p12, STACK_OF(PKCS7) *safes)
return 0;
}
-STACK_OF(PKCS7) *PKCS12_unpack_authsafes(const PKCS12 *p12)
+STACK_OF(PKCS7) *ossl_pkcs12_unpack_authsafes_ex(const PKCS12 *p12,
+ OSSL_LIB_CTX *libctx, const char *propq)
{
STACK_OF(PKCS7) *p7s;
- PKCS7_CTX *p7ctx;
PKCS7 *p7;
int i;
@@ -205,16 +242,15 @@ STACK_OF(PKCS7) *PKCS12_unpack_authsafes(const PKCS12 *p12)
return NULL;
}
- p7ctx = &p12->authsafes->ctx;
p7s = ASN1_item_unpack_ex(p12->authsafes->d.data,
- ASN1_ITEM_rptr(PKCS12_AUTHSAFES),
- ossl_pkcs7_ctx_get0_libctx(p7ctx),
- ossl_pkcs7_ctx_get0_propq(p7ctx));
+ ASN1_ITEM_rptr(PKCS12_AUTHSAFES), libctx, propq);
if (p7s != NULL) {
for (i = 0; i < sk_PKCS7_num(p7s); i++) {
p7 = sk_PKCS7_value(p7s, i);
- if (!ossl_pkcs7_ctx_propagate(p12->authsafes, p7))
+ ossl_pkcs7_set0_libctx(p7, libctx);
+ if (!ossl_pkcs7_set1_propq(p7, propq))
goto err;
+ ossl_pkcs7_resolve_libctx(p7);
}
}
return p7s;
@@ -222,3 +258,11 @@ err:
sk_PKCS7_pop_free(p7s, PKCS7_free);
return NULL;
}
+
+STACK_OF(PKCS7) *PKCS12_unpack_authsafes(const PKCS12 *p12)
+{
+ PKCS7_CTX *p7ctx;
+ p7ctx = &p12->authsafes->ctx;
+
+ return ossl_pkcs12_unpack_authsafes_ex(p12, ossl_pkcs7_ctx_get0_libctx(p7ctx), ossl_pkcs7_ctx_get0_propq(p7ctx));
+}
diff --git a/crypto/pkcs12/p12_crt.c b/crypto/pkcs12/p12_crt.c
index 948d46b851..c0bcc155af 100644
--- a/crypto/pkcs12/p12_crt.c
+++ b/crypto/pkcs12/p12_crt.c
@@ -126,7 +126,7 @@ PKCS12 *PKCS12_create_ex2(const char *pass, const char *name, EVP_PKEY *pkey,
if (!copy_bag_attr(bag, pkey, NID_LocalKeySet))
goto err;
- if (name && !PKCS12_add_friendlyname(bag, name, -1))
+ if (name && !PKCS12_add_friendlyname(bag, name, namelen))
goto err;
if (keyidlen && !PKCS12_add_localkeyid(bag, keyid, keyidlen))
goto err;
diff --git a/crypto/pkcs12/p12_kiss.c b/crypto/pkcs12/p12_kiss.c
index 20fd100cf3..55f2ca2b60 100644
--- a/crypto/pkcs12/p12_kiss.c
+++ b/crypto/pkcs12/p12_kiss.c
@@ -10,49 +10,92 @@
#include <stdio.h>
#include "internal/cryptlib.h"
#include <openssl/pkcs12.h>
+#include "crypto/pkcs7/pk7_local.h"
+#include "p12_local.h"
#include "crypto/x509.h" /* for ossl_x509_add_cert_new() */
/* Simplified PKCS#12 routines */
static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
- EVP_PKEY **pkey, STACK_OF(X509) *ocerts);
+ PKCS12_PARSE_CTX *ctx,
+ OSSL_LIB_CTX *libctx, const char *propq);
static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
- int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts,
+ int passlen, PKCS12_PARSE_CTX *ctx,
OSSL_LIB_CTX *libctx, const char *propq);
static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
- EVP_PKEY **pkey, STACK_OF(X509) *ocerts,
+ PKCS12_PARSE_CTX *ctx,
OSSL_LIB_CTX *libctx, const char *propq);
+PKCS12_PARSE_CTX *PKCS12_PARSE_CTX_new(void)
+{
+ return OPENSSL_zalloc(sizeof(PKCS12_PARSE_CTX));
+}
+
+void PKCS12_PARSE_CTX_free(PKCS12_PARSE_CTX *ctx)
+{
+ OPENSSL_free(ctx);
+}
+
+void PKCS12_PARSE_CTX_set_pkey(PKCS12_PARSE_CTX *ctx, EVP_PKEY **pkey)
+{
+ ctx->pkey = pkey;
+}
+
+void PKCS12_PARSE_CTX_set_cert(PKCS12_PARSE_CTX *ctx, X509 **cert)
+{
+ ctx->cert = cert;
+}
+
+void PKCS12_PARSE_CTX_set_ca(PKCS12_PARSE_CTX *ctx, STACK_OF(X509) **ca)
+{
+ ctx->ca = ca;
+}
+
+void PKCS12_PARSE_CTX_set_skeys(PKCS12_PARSE_CTX *ctx, STACK_OF(EVP_SKEY) **skeys)
+{
+ ctx->skeys = skeys;
+}
+
/*
- * Parse and decrypt a PKCS#12 structure returning user key, user cert and
- * other (CA) certs. Note either ca should be NULL, *ca should be NULL, or it
- * should point to a valid STACK structure. pkey and/or cert may be NULL;
- * if non-NULL the variables they point to can be passed uninitialised.
+ * Parse and decrypt a PKCS#12 structure returning user key, user cert,
+ * other (CA) certs, and/or symmetric secret keys according to ctx settings.
*/
-int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
- STACK_OF(X509) **ca)
+int PKCS12_parse_ex(PKCS12 *p12, const char *pass,
+ PKCS12_PARSE_CTX *ctx,
+ OSSL_LIB_CTX *libctx, const char *propq)
{
- STACK_OF(X509) *ocerts = NULL;
X509 *x = NULL;
- STACK_OF(X509) *initial_ca = ca != NULL ? *ca : NULL;
- int initial_ca_count = initial_ca == NULL ? 0 : sk_X509_num(initial_ca);
+ STACK_OF(X509) *initial_ca = NULL;
+ STACK_OF(EVP_SKEY) *initial_skeys = NULL;
+ int initial_ca_count = 0;
+ int initial_skeys_count = 0;
int ca_added_count = 0;
-
- if (pkey != NULL)
- *pkey = NULL;
- if (cert != NULL)
- *cert = NULL;
-
- /* Check for NULL PKCS12 structure */
+ int skeys_added_count = 0;
if (p12 == NULL) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER);
return 0;
}
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ initial_ca = ctx->ca != NULL ? *ctx->ca : NULL;
+ initial_ca_count = initial_ca == NULL ? 0 : sk_X509_num(initial_ca);
+ initial_skeys = ctx->skeys != NULL ? *ctx->skeys : NULL;
+ initial_skeys_count = initial_skeys == NULL ? 0 : sk_EVP_SKEY_num(initial_skeys);
+
+ if (ctx->pkey != NULL)
+ *ctx->pkey = NULL;
+ if (ctx->cert != NULL)
+ *ctx->cert = NULL;
+ ctx->ocerts = NULL;
+
/* Check the mac */
if (PKCS12_mac_present(p12)) {
/*
@@ -62,15 +105,15 @@ int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
* password are two different things...
*/
if (pass == NULL || *pass == '\0') {
- if (PKCS12_verify_mac(p12, NULL, 0))
+ if (ossl_pkcs12_verify_mac(p12, NULL, 0, libctx, propq))
pass = NULL;
- else if (PKCS12_verify_mac(p12, "", 0))
+ else if (ossl_pkcs12_verify_mac(p12, "", 0, libctx, propq))
pass = "";
else {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE);
goto err;
}
- } else if (!PKCS12_verify_mac(p12, pass, -1)) {
+ } else if (!ossl_pkcs12_verify_mac(p12, pass, -1, libctx, propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_VERIFY_FAILURE);
goto err;
}
@@ -79,13 +122,13 @@ int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
}
/* If needed, allocate stack for other certificates */
- if ((cert != NULL || ca != NULL)
- && (ocerts = sk_X509_new_null()) == NULL) {
+ if ((ctx->cert != NULL || ctx->ca != NULL)
+ && (ctx->ocerts = sk_X509_new_null()) == NULL) {
ERR_raise(ERR_LIB_PKCS12, ERR_R_CRYPTO_LIB);
goto err;
}
- if (!parse_pk12(p12, pass, -1, pkey, ocerts)) {
+ if (!parse_pk12(p12, pass, -1, ctx, libctx, propq)) {
int err = ERR_peek_last_error();
if (ERR_GET_LIB(err) != ERR_LIB_EVP
@@ -95,71 +138,112 @@ int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
}
/* Split the certs in ocerts over *cert and *ca as far as requested */
- while ((x = sk_X509_shift(ocerts)) != NULL) {
- if (pkey != NULL && *pkey != NULL
- && cert != NULL && *cert == NULL) {
+ while ((x = sk_X509_shift(ctx->ocerts)) != NULL) {
+ if (ctx->pkey != NULL && *ctx->pkey != NULL
+ && ctx->cert != NULL && *ctx->cert == NULL) {
int match;
ERR_set_mark();
- match = X509_check_private_key(x, *pkey);
+ match = X509_check_private_key(x, *ctx->pkey);
ERR_pop_to_mark();
if (match) {
- *cert = x;
+ *ctx->cert = x;
continue;
}
}
- if (ca != NULL) {
- if (!ossl_x509_add_cert_new(ca, x, X509_ADD_FLAG_DEFAULT))
+ if (ctx->ca != NULL) {
+ if (!ossl_x509_add_cert_new(ctx->ca, x, X509_ADD_FLAG_DEFAULT))
goto err;
continue;
}
X509_free(x);
}
- sk_X509_free(ocerts);
+ sk_X509_free(ctx->ocerts);
+ ctx->ocerts = NULL;
return 1;
err:
-
- if (pkey != NULL) {
- EVP_PKEY_free(*pkey);
- *pkey = NULL;
+ if (ctx->pkey != NULL) {
+ EVP_PKEY_free(*ctx->pkey);
+ *ctx->pkey = NULL;
}
- if (cert != NULL) {
- X509_free(*cert);
- *cert = NULL;
+ if (ctx->cert != NULL) {
+ X509_free(*ctx->cert);
+ *ctx->cert = NULL;
+ }
+ skeys_added_count = ctx->skeys == NULL || *ctx->skeys == NULL ? 0 : sk_EVP_SKEY_num(*ctx->skeys) - initial_skeys_count;
+ while (skeys_added_count > 0) {
+ EVP_SKEY *t_skey = sk_EVP_SKEY_pop(*ctx->skeys);
+ EVP_SKEY_free(t_skey);
+ skeys_added_count--;
+ }
+
+ if (initial_skeys == NULL && ctx->skeys != NULL) {
+ sk_EVP_SKEY_free(*ctx->skeys);
+ *ctx->skeys = NULL;
}
- ca_added_count = ca == NULL || *ca == NULL ? 0 : sk_X509_num(*ca) - initial_ca_count;
+ ca_added_count = ctx->ca == NULL || *ctx->ca == NULL ? 0 : sk_X509_num(*ctx->ca) - initial_ca_count;
while (ca_added_count > 0) {
- X509 *t_cert = sk_X509_pop(*ca);
+ X509 *t_cert = sk_X509_pop(*ctx->ca);
X509_free(t_cert);
ca_added_count--;
}
- if (initial_ca == NULL && ca != NULL) {
- sk_X509_free(*ca);
- *ca = NULL;
+ if (initial_ca == NULL && ctx->ca != NULL) {
+ sk_X509_free(*ctx->ca);
+ *ctx->ca = NULL;
}
X509_free(x);
- OSSL_STACK_OF_X509_free(ocerts);
+ OSSL_STACK_OF_X509_free(ctx->ocerts);
+ ctx->ocerts = NULL;
return 0;
}
+int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
+ STACK_OF(X509) **ca)
+{
+ PKCS12_PARSE_CTX *ctx;
+ int ret;
+
+ if (pkey != NULL)
+ *pkey = NULL;
+ if (cert != NULL)
+ *cert = NULL;
+
+ if (p12 == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_INVALID_NULL_PKCS12_POINTER);
+ return 0;
+ }
+
+ ctx = PKCS12_PARSE_CTX_new();
+ if (ctx == NULL)
+ return 0;
+
+ PKCS12_PARSE_CTX_set_pkey(ctx, pkey);
+ PKCS12_PARSE_CTX_set_cert(ctx, cert);
+ PKCS12_PARSE_CTX_set_ca(ctx, ca);
+
+ ret = PKCS12_parse_ex(p12, pass, ctx, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq);
+ PKCS12_PARSE_CTX_free(ctx);
+ return ret;
+}
+
/* Parse the outer PKCS#12 structure */
-/* pkey and/or ocerts may be NULL */
static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
- EVP_PKEY **pkey, STACK_OF(X509) *ocerts)
+ PKCS12_PARSE_CTX *ctx,
+ OSSL_LIB_CTX *libctx, const char *propq)
{
STACK_OF(PKCS7) *asafes;
STACK_OF(PKCS12_SAFEBAG) *bags;
int i, bagnid;
PKCS7 *p7;
- if ((asafes = PKCS12_unpack_authsafes(p12)) == NULL)
+ if ((asafes = ossl_pkcs12_unpack_authsafes_ex(p12, libctx, propq)) == NULL)
return 0;
for (i = 0; i < sk_PKCS7_num(asafes); i++) {
p7 = sk_PKCS7_value(asafes, i);
@@ -167,15 +251,15 @@ static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
if (bagnid == NID_pkcs7_data) {
bags = PKCS12_unpack_p7data(p7);
} else if (bagnid == NID_pkcs7_encrypted) {
- bags = PKCS12_unpack_p7encdata(p7, pass, passlen);
+ bags = ossl_pkcs12_unpack_p7encdata_ex(p7, pass, passlen,
+ libctx, propq);
} else
continue;
if (!bags) {
sk_PKCS7_pop_free(asafes, PKCS7_free);
return 0;
}
- if (!parse_bags(bags, pass, passlen, pkey, ocerts,
- p7->ctx.libctx, p7->ctx.propq)) {
+ if (!parse_bags(bags, pass, passlen, ctx, libctx, propq)) {
sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
sk_PKCS7_pop_free(asafes, PKCS7_free);
return 0;
@@ -186,24 +270,22 @@ static int parse_pk12(PKCS12 *p12, const char *pass, int passlen,
return 1;
}
-/* pkey and/or ocerts may be NULL */
static int parse_bags(const STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
- int passlen, EVP_PKEY **pkey, STACK_OF(X509) *ocerts,
+ int passlen, PKCS12_PARSE_CTX *ctx,
OSSL_LIB_CTX *libctx, const char *propq)
{
int i;
for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
if (!parse_bag(sk_PKCS12_SAFEBAG_value(bags, i),
- pass, passlen, pkey, ocerts,
+ pass, passlen, ctx,
libctx, propq))
return 0;
}
return 1;
}
-/* pkey and/or ocerts may be NULL */
static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
- EVP_PKEY **pkey, STACK_OF(X509) *ocerts,
+ PKCS12_PARSE_CTX *ctx,
OSSL_LIB_CTX *libctx, const char *propq)
{
PKCS8_PRIV_KEY_INFO *p8;
@@ -226,29 +308,29 @@ static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
switch (PKCS12_SAFEBAG_get_nid(bag)) {
case NID_keyBag:
- if (pkey == NULL || *pkey != NULL)
+ if (ctx->pkey == NULL || *ctx->pkey != NULL)
return 1;
- *pkey = EVP_PKCS82PKEY_ex(PKCS12_SAFEBAG_get0_p8inf(bag),
+ *ctx->pkey = EVP_PKCS82PKEY_ex(PKCS12_SAFEBAG_get0_p8inf(bag),
libctx, propq);
- if (*pkey == NULL)
+ if (*ctx->pkey == NULL)
return 0;
break;
case NID_pkcs8ShroudedKeyBag:
- if (pkey == NULL || *pkey != NULL)
+ if (ctx->pkey == NULL || *ctx->pkey != NULL)
return 1;
if ((p8 = PKCS12_decrypt_skey_ex(bag, pass, passlen,
libctx, propq))
== NULL)
return 0;
- *pkey = EVP_PKCS82PKEY_ex(p8, libctx, propq);
+ *ctx->pkey = EVP_PKCS82PKEY_ex(p8, libctx, propq);
PKCS8_PRIV_KEY_INFO_free(p8);
- if (!(*pkey))
+ if (!(*ctx->pkey))
return 0;
break;
case NID_certBag:
- if (ocerts == NULL
+ if (ctx->ocerts == NULL
|| PKCS12_SAFEBAG_get_bag_nid(bag) != NID_x509Certificate)
return 1;
if ((x509 = PKCS12_SAFEBAG_get1_cert_ex(bag, libctx, propq)) == NULL)
@@ -272,16 +354,45 @@ static int parse_bag(PKCS12_SAFEBAG *bag, const char *pass, int passlen,
}
}
- if (!sk_X509_push(ocerts, x509)) {
+ if (!sk_X509_push(ctx->ocerts, x509)) {
X509_free(x509);
return 0;
}
break;
+ case NID_secretBag:
+ if (ctx->skeys == NULL)
+ return 1;
+ if (PKCS12_SAFEBAG_get_bag_nid(bag) != NID_pkcs8ShroudedKeyBag)
+ return 1;
+ {
+ EVP_SKEY *skey;
+ PKCS8_PRIV_KEY_INFO *p8sb = PKCS12_decrypt_secretbag(bag, pass,
+ passlen, libctx, propq);
+ if (p8sb == NULL)
+ return 0;
+ skey = PKCS8_PRIV_KEY_INFO_get1_skey(p8sb, libctx, propq);
+ PKCS8_PRIV_KEY_INFO_free(p8sb);
+ if (skey == NULL) {
+ ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
+ return 0;
+ }
+ if (*ctx->skeys == NULL
+ && (*ctx->skeys = sk_EVP_SKEY_new_null()) == NULL) {
+ EVP_SKEY_free(skey);
+ return 0;
+ }
+ if (!sk_EVP_SKEY_push(*ctx->skeys, skey)) {
+ EVP_SKEY_free(skey);
+ return 0;
+ }
+ }
+ return 1;
+
case NID_safeContentsBag:
- return parse_bags(PKCS12_SAFEBAG_get0_safes(bag), pass, passlen, pkey,
- ocerts, libctx, propq);
+ return parse_bags(PKCS12_SAFEBAG_get0_safes(bag), pass, passlen, ctx,
+ libctx, propq);
default:
return 1;
diff --git a/crypto/pkcs12/p12_local.h b/crypto/pkcs12/p12_local.h
index 4025fbab16..147454bc7a 100644
--- a/crypto/pkcs12/p12_local.h
+++ b/crypto/pkcs12/p12_local.h
@@ -49,6 +49,22 @@ struct pkcs12_bag_st {
} value;
};
+struct pkcs12_parse_ctx_st {
+ EVP_PKEY **pkey;
+ X509 **cert;
+ STACK_OF(X509) **ca;
+ STACK_OF(EVP_SKEY) **skeys;
+ /* internal: temporary cert collection used during parsing */
+ STACK_OF(X509) *ocerts;
+};
+
const PKCS7_CTX *ossl_pkcs12_get0_pkcs7ctx(const PKCS12 *p12);
+int ossl_pkcs12_verify_mac(PKCS12 *p12, const char *pass, int passlen,
+ OSSL_LIB_CTX *libctx, const char *propq);
+STACK_OF(PKCS7) *ossl_pkcs12_unpack_authsafes_ex(const PKCS12 *p12,
+ OSSL_LIB_CTX *libctx, const char *propq);
+STACK_OF(PKCS12_SAFEBAG) *ossl_pkcs12_unpack_p7encdata_ex(PKCS7 *p7,
+ const char *pass, int passlen,
+ OSSL_LIB_CTX *libctx, const char *propq);
#endif /* !defined(OSSL_LIBCRYPTO_PKCS12_P12_LOCAL_H) */
diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c
index 6720349d8d..f434b15f63 100644
--- a/crypto/pkcs12/p12_mutl.c
+++ b/crypto/pkcs12/p12_mutl.c
@@ -182,7 +182,8 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *out,
const EVP_MD *md_type,
OSSL_LIB_CTX *libctx,
- const char *propq))
+ const char *propq),
+ OSSL_LIB_CTX *libctx, const char *propq)
{
int ret = 0;
EVP_MD *md;
@@ -193,8 +194,6 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
int md_nid = NID_undef;
const X509_ALGOR *macalg;
const ASN1_OBJECT *macoid;
- OSSL_LIB_CTX *libctx;
- const char *propq;
size_t md_sz, outlen;
if (!PKCS7_type_is_data(p12->authsafes)) {
@@ -207,8 +206,6 @@ static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
return 0;
}
- libctx = p12->authsafes->ctx.libctx;
- propq = p12->authsafes->ctx.propq;
salt = p12->mac->salt->data;
saltlen = p12->mac->salt->length;
if (p12->mac->iter == NULL)
@@ -305,11 +302,11 @@ err:
int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *mac, unsigned int *maclen)
{
- return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NID_undef, NID_undef, NULL);
+ return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NID_undef, NID_undef, NULL, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq);
}
-/* Verify the mac */
-int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
+int ossl_pkcs12_verify_mac(PKCS12 *p12, const char *pass, int passlen,
+ OSSL_LIB_CTX *libctx, const char *propq)
{
unsigned char mac[EVP_MAX_MD_SIZE];
unsigned int maclen;
@@ -337,14 +334,14 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
X509_ALGOR_get0(&hmac_oid, NULL, NULL, param->messageAuthScheme);
md_nid = ossl_hmac2mdnid(OBJ_obj2nid(hmac_oid));
- if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, md_nid, NID_undef, NULL)) {
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, md_nid, NID_undef, NULL, libctx, propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
PBMAC1PARAM_free(param);
return 0;
}
PBMAC1PARAM_free(param);
} else {
- if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) {
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL, libctx, propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
return 0;
}
@@ -357,6 +354,12 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
return 1;
}
+/* Verify the mac */
+int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen)
+{
+ return ossl_pkcs12_verify_mac(p12, pass, passlen, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq);
+}
+
/* Set a mac */
int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
unsigned char *salt, int saltlen, int iter,
@@ -378,7 +381,7 @@ int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen,
/*
* Note that output mac is forced to UTF-8...
*/
- if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL)) {
+ if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, NID_undef, NID_undef, NULL, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
return 0;
}
@@ -542,7 +545,7 @@ int PKCS12_set_pbmac1_pbkdf2(PKCS12 *p12, const char *pass, int passlen,
*/
if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen,
EVP_MD_get_type(md_type), prf_md_nid,
- pkcs12_pbmac1_pbkdf2_key_gen)) {
+ pkcs12_pbmac1_pbkdf2_key_gen, p12->authsafes->ctx.libctx, p12->authsafes->ctx.propq)) {
ERR_raise(ERR_LIB_PKCS12, PKCS12_R_MAC_GENERATION_ERROR);
goto err;
}
diff --git a/crypto/pkcs12/p12_npas.c b/crypto/pkcs12/p12_npas.c
index 44763e8668..6b25bf95bd 100644
--- a/crypto/pkcs12/p12_npas.c
+++ b/crypto/pkcs12/p12_npas.c
@@ -99,7 +99,9 @@ static int newpass_p12(PKCS12 *p12, const char *oldpass, const char *newpass)
if (bagnid == NID_pkcs7_data)
p7new = PKCS12_pack_p7data(bags);
else
- p7new = PKCS12_pack_p7encdata_ex(pbe_nid, newpass, -1, NULL,
+ p7new = PKCS12_pack_p7encdata_ex(
+ cipherid != NID_undef ? cipherid : pbe_nid,
+ newpass, -1, NULL,
pbe_saltlen, pbe_iter, bags,
p7->ctx.libctx, p7->ctx.propq);
if (p7new == NULL || !sk_PKCS7_push(newsafes, p7new)) {
diff --git a/crypto/pkcs12/p12_sbag.c b/crypto/pkcs12/p12_sbag.c
index 2bb7c6fa8b..40f53a71fa 100644
--- a/crypto/pkcs12/p12_sbag.c
+++ b/crypto/pkcs12/p12_sbag.c
@@ -10,6 +10,8 @@
#include <stdio.h>
#include "internal/cryptlib.h"
#include <openssl/pkcs12.h>
+#include <openssl/core_names.h>
+#include <openssl/objects.h>
#include "p12_local.h"
#include "crypto/x509.h"
@@ -147,6 +149,63 @@ X509_CRL *PKCS12_SAFEBAG_get1_crl_ex(const PKCS12_SAFEBAG *bag,
return ret;
}
+EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
+ OSSL_LIB_CTX *libctx, const char *propq)
+{
+ const ASN1_OBJECT *algoid = NULL;
+ const unsigned char *raw_key = NULL;
+ int raw_key_len = 0;
+ const char *skey_type = OSSL_SKEY_TYPE_GENERIC;
+
+ if (p8inf == NULL)
+ return NULL;
+
+ if (!PKCS8_pkey_get0(&algoid, &raw_key, &raw_key_len, NULL, p8inf)
+ || raw_key == NULL || raw_key_len <= 0)
+ return NULL;
+
+ if (algoid != NULL) {
+ int nid = OBJ_obj2nid(algoid);
+
+ switch (nid) {
+ case NID_id_aes:
+ if (raw_key_len != 16 && raw_key_len != 24 && raw_key_len != 32)
+ return NULL;
+ skey_type = OSSL_SKEY_TYPE_AES;
+ break;
+ case NID_aes_128_cbc:
+ case NID_aes_128_ccm:
+ case NID_aes_128_ecb:
+ case NID_aes_128_gcm:
+ if (raw_key_len != 16)
+ return NULL;
+ skey_type = OSSL_SKEY_TYPE_AES;
+ break;
+ case NID_aes_192_cbc:
+ case NID_aes_192_ccm:
+ case NID_aes_192_ecb:
+ case NID_aes_192_gcm:
+ if (raw_key_len != 24)
+ return NULL;
+ skey_type = OSSL_SKEY_TYPE_AES;
+ break;
+ case NID_aes_256_cbc:
+ case NID_aes_256_ccm:
+ case NID_aes_256_ecb:
+ case NID_aes_256_gcm:
+ if (raw_key_len != 32)
+ return NULL;
+ skey_type = OSSL_SKEY_TYPE_AES;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return EVP_SKEY_import_raw_key(libctx, skey_type,
+ (unsigned char *)raw_key, raw_key_len, propq);
+}
+
PKCS12_SAFEBAG *PKCS12_SAFEBAG_create_cert(X509 *x509)
{
return PKCS12_item_pack_safebag(x509, ASN1_ITEM_rptr(X509),
diff --git a/fuzz/pkcs12.c b/fuzz/pkcs12.c
index 74e2b6dd8a..b8c8927253 100644
--- a/fuzz/pkcs12.c
+++ b/fuzz/pkcs12.c
@@ -27,14 +27,31 @@ int FuzzerInitialize(int *argc, char ***argv)
return 1;
}
-int FuzzerTestOneInput(const uint8_t *buf, size_t len)
+static void fuzz_pkcs12_parse(PKCS12 *p12, const char *pass)
{
- PKCS12 *p12;
- BIO *in;
+ PKCS12_PARSE_CTX *ctx;
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
STACK_OF(X509) *ca = NULL;
+ ctx = PKCS12_PARSE_CTX_new();
+ if (ctx == NULL)
+ return;
+ PKCS12_PARSE_CTX_set_pkey(ctx, &pkey);
+ PKCS12_PARSE_CTX_set_cert(ctx, &cert);
+ PKCS12_PARSE_CTX_set_ca(ctx, &ca);
+ PKCS12_parse_ex(p12, pass, ctx, NULL, NULL);
+ PKCS12_PARSE_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ OSSL_STACK_OF_X509_free(ca);
+}
+
+int FuzzerTestOneInput(const uint8_t *buf, size_t len)
+{
+ PKCS12 *p12;
+ BIO *in;
+
if (len == 0 || len > INT_MAX)
return 0;
@@ -53,18 +70,8 @@ int FuzzerTestOneInput(const uint8_t *buf, size_t len)
PKCS12_verify_mac(p12, NULL, 0);
PKCS12_verify_mac(p12, "", 0);
- PKCS12_parse(p12, NULL, &pkey, &cert, &ca);
- EVP_PKEY_free(pkey);
- X509_free(cert);
- OSSL_STACK_OF_X509_free(ca);
-
- pkey = NULL;
- cert = NULL;
- ca = NULL;
- PKCS12_parse(p12, "", &pkey, &cert, &ca);
- EVP_PKEY_free(pkey);
- X509_free(cert);
- OSSL_STACK_OF_X509_free(ca);
+ fuzz_pkcs12_parse(p12, NULL);
+ fuzz_pkcs12_parse(p12, "");
PKCS12_free(p12);
}
diff --git a/include/openssl/evp.h b/include/openssl/evp.h.in
similarity index 99%
rename from include/openssl/evp.h
rename to include/openssl/evp.h.in
index 55503411eb..7bad41ba79 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h.in
@@ -7,6 +7,12 @@
* https://www.openssl.org/source/license.html
*/
+/* clang-format off */
+{-
+use OpenSSL::stackhash qw(generate_stack_macros);
+-}
+/* clang-format on */
+
#ifndef OPENSSL_EVP_H
#define OPENSSL_EVP_H
#pragma once
@@ -118,6 +124,12 @@
extern "C" {
#endif
+/* clang-format off */
+{-
+ generate_stack_macros("EVP_SKEY");
+-}
+/* clang-format on */
+
int EVP_set_default_properties(OSSL_LIB_CTX *libctx, const char *propq);
char *EVP_get1_default_properties(OSSL_LIB_CTX *libctx);
int EVP_default_properties_is_fips_enabled(OSSL_LIB_CTX *libctx);
diff --git a/include/openssl/pkcs12.h.in b/include/openssl/pkcs12.h.in
index 6555322be3..e966228ea9 100644
--- a/include/openssl/pkcs12.h.in
+++ b/include/openssl/pkcs12.h.in
@@ -27,6 +27,7 @@ use OpenSSL::stackhash qw(generate_stack_macros);
#include <openssl/bio.h>
#include <openssl/core.h>
#include <openssl/x509.h>
+#include <openssl/evp.h>
#include <openssl/pkcs12err.h>
#ifndef OPENSSL_NO_STDIO
#include <stdio.h>
@@ -125,6 +126,8 @@ X509 *PKCS12_SAFEBAG_get1_cert_ex(const PKCS12_SAFEBAG *bag, OSSL_LIB_CTX *libct
X509 *PKCS12_SAFEBAG_get1_cert(const PKCS12_SAFEBAG *bag);
X509_CRL *PKCS12_SAFEBAG_get1_crl_ex(const PKCS12_SAFEBAG *bag, OSSL_LIB_CTX *libctx, const char *propq);
X509_CRL *PKCS12_SAFEBAG_get1_crl(const PKCS12_SAFEBAG *bag);
+EVP_SKEY *PKCS8_PRIV_KEY_INFO_get1_skey(const PKCS8_PRIV_KEY_INFO *p8inf,
+ OSSL_LIB_CTX *libctx, const char *propq);
const STACK_OF(PKCS12_SAFEBAG) *
PKCS12_SAFEBAG_get0_safes(const PKCS12_SAFEBAG *bag);
const PKCS8_PRIV_KEY_INFO *PKCS12_SAFEBAG_get0_p8inf(const PKCS12_SAFEBAG *bag);
@@ -163,6 +166,9 @@ PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_skey_ex(const PKCS12_SAFEBAG *bag,
const char *pass, int passlen,
OSSL_LIB_CTX *ctx,
const char *propq);
+PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_secretbag(const PKCS12_SAFEBAG *bag,
+ const char *pass, int passlen,
+ OSSL_LIB_CTX *ctx, const char *propq);
X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher,
const char *pass, int passlen, unsigned char *salt,
int saltlen, int iter, PKCS8_PRIV_KEY_INFO *p8);
@@ -300,6 +306,19 @@ DECLARE_ASN1_ITEM(PKCS12_SAFEBAGS)
DECLARE_ASN1_ITEM(PKCS12_AUTHSAFES)
void PKCS12_PBE_add(void);
+
+typedef struct pkcs12_parse_ctx_st PKCS12_PARSE_CTX;
+PKCS12_PARSE_CTX *PKCS12_PARSE_CTX_new(void);
+void PKCS12_PARSE_CTX_free(PKCS12_PARSE_CTX *ctx);
+void PKCS12_PARSE_CTX_set_pkey(PKCS12_PARSE_CTX *ctx, EVP_PKEY **pkey);
+void PKCS12_PARSE_CTX_set_cert(PKCS12_PARSE_CTX *ctx, X509 **cert);
+void PKCS12_PARSE_CTX_set_ca(PKCS12_PARSE_CTX *ctx, STACK_OF(X509) **ca);
+void PKCS12_PARSE_CTX_set_skeys(PKCS12_PARSE_CTX *ctx, STACK_OF(EVP_SKEY) **skeys);
+
+int PKCS12_parse_ex(PKCS12 *p12, const char *pass,
+ PKCS12_PARSE_CTX *ctx,
+ OSSL_LIB_CTX *libctx, const char *propq);
+
int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert,
STACK_OF(X509) **ca);
typedef int PKCS12_create_cb(PKCS12_SAFEBAG *bag, void *cbarg);
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index e1457639c4..862a81abbc 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -520,7 +520,7 @@ unless ($no_fips) {
stderr => "outerr.txt"),
capture => 1);
open DATA, "outerr.txt";
- my @match = grep /:PKCS12_parse:parse error:/, <DATA>;
+ my @match = grep /:PKCS12_parse_ex:parse error:/, <DATA>;
close DATA;
ok(scalar @match > 0, "Test against CVE-2026-22795 , missing ASN1_TYPE validation in cert, sha256mac");
}
@@ -544,7 +544,7 @@ unless ($no_fips) {
stderr => "outerr.txt"),
capture => 1);
open DATA, "outerr.txt";
- my @match = grep /:PKCS12_parse:parse error:/, <DATA>;
+ my @match = grep /:PKCS12_parse_ex:parse error:/, <DATA>;
close DATA;
ok(scalar @match > 0, "Test against CVE-2026-22795 , missing ASN1_TYPE validation in cert, pbmac1");
}
@@ -569,7 +569,7 @@ unless ($no_fips) {
stderr => "outerr.txt"),
capture => 1);
open DATA, "outerr.txt";
- my @match = grep /:PKCS12_parse:parse error:/, <DATA>;
+ my @match = grep /:PKCS12_parse_ex:parse error:/, <DATA>;
close DATA;
ok(scalar @match > 0, "Test against CVE-2026-22795 , missing ASN1_TYPE validation in keys, sha256mac");
}
@@ -594,7 +594,7 @@ unless ($no_fips) {
stderr => "outerr.txt"),
capture => 1);
open DATA, "outerr.txt";
- my @match = grep /:PKCS12_parse:parse error:/, <DATA>;
+ my @match = grep /:PKCS12_parse_ex:parse error:/, <DATA>;
close DATA;
ok(scalar @match > 0, "Test against CVE-2026-22795 , missing ASN1_TYPE validation in keys, pbmac1");
}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index fe2d6afb4f..eebc52128d 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5738,3 +5738,12 @@ i2d_IPAddrBlocks ? 4_1_0 EXIST::FUNCTION:RFC3779
IPAddrBlocks_free ? 4_1_0 EXIST::FUNCTION:RFC3779
IPAddrBlocks_new ? 4_1_0 EXIST::FUNCTION:RFC3779
IPAddrBlocks_it ? 4_1_0 EXIST::FUNCTION:RFC3779
+PKCS8_PRIV_KEY_INFO_get1_skey ? 4_2_0 EXIST::FUNCTION:
+PKCS12_decrypt_secretbag ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_new ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_free ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_set_pkey ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_set_cert ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_set_ca ? 4_2_0 EXIST::FUNCTION:
+PKCS12_PARSE_CTX_set_skeys ? 4_2_0 EXIST::FUNCTION:
+PKCS12_parse_ex ? 4_2_0 EXIST::FUNCTION: