Commit 1aa26e8d71 for strongswan.org

commit 1aa26e8d71220788b8869db1d3b002c7d9bdd55d
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Feb 23 18:59:43 2026 +0100

    openssl: Add generic loader for public keys to avoid pkcs1 dependency

    This can load public keys of unknown type via KEY_ANY without requiring
    the pkcs1 plugin.

diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
index 7c21902a72..2d57d8429f 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
+++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
@@ -42,10 +42,11 @@ typedef struct private_openssl_ec_public_key_t private_openssl_ec_public_key_t;
  * Private data structure with signing context.
  */
 struct private_openssl_ec_public_key_t {
+
 	/**
-	 * Public interface for this signer.
+	 * Public interface
 	 */
-	openssl_ec_public_key_t public;
+	public_key_t public;

 	/**
 	 * EC key object
@@ -282,7 +283,7 @@ METHOD(public_key_t, get_ref, public_key_t*,
 	private_openssl_ec_public_key_t *this)
 {
 	ref_get(&this->ref);
-	return &this->public.key;
+	return &this->public;
 }

 METHOD(public_key_t, destroy, void,
@@ -319,13 +320,44 @@ bool openssl_check_explicit_params(const EVP_PKEY *key)
 	return explicit == 1;
 }

+/*
+ * Described in header
+ */
+public_key_t *openssl_ec_public_key_create(EVP_PKEY *key)
+{
+	private_openssl_ec_public_key_t *this;
+
+	if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC ||
+		openssl_check_explicit_params(key))
+	{
+		EVP_PKEY_free(key);
+		return NULL;
+	}
+
+	INIT(this,
+		.public = {
+			.get_type = _get_type,
+			.verify = _verify,
+			.encrypt = _encrypt,
+			.get_keysize = _get_keysize,
+			.equals = public_key_equals,
+			.get_fingerprint = _get_fingerprint,
+			.has_fingerprint = public_key_has_fingerprint,
+			.get_encoding = _get_encoding,
+			.get_ref = _get_ref,
+			.destroy = _destroy,
+		},
+		.ref = 1,
+		.key = key,
+	);
+	return &this->public;
+}
+
 /**
  * See header.
  */
-openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
-													va_list args)
+public_key_t *openssl_ec_public_key_load(key_type_t type, va_list args)
 {
-	private_openssl_ec_public_key_t *this;
 	chunk_t blob = chunk_empty;
 	EVP_PKEY *key;

@@ -344,32 +376,7 @@ openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
 		break;
 	}
 	key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
-	if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC ||
-		openssl_check_explicit_params(key))
-	{
-		EVP_PKEY_free(key);
-		return NULL;
-	}
-
-	INIT(this,
-		.public = {
-			.key = {
-				.get_type = _get_type,
-				.verify = _verify,
-				.encrypt = _encrypt,
-				.get_keysize = _get_keysize,
-				.equals = public_key_equals,
-				.get_fingerprint = _get_fingerprint,
-				.has_fingerprint = public_key_has_fingerprint,
-				.get_encoding = _get_encoding,
-				.get_ref = _get_ref,
-				.destroy = _destroy,
-			},
-		},
-		.ref = 1,
-		.key = key,
-	);
-	return &this->public;
+	return openssl_ec_public_key_create(key);
 }

 #endif /* OPENSSL_NO_ECDSA */
diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
index 7b380487de..19a3092a81 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
+++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
@@ -15,6 +15,8 @@
  */

 /**
+ * public_key_t implementation of ECDSA using OpenSSL
+ *
  * @defgroup openssl_ec_public_key openssl_ec_public_key
  * @{ @ingroup openssl_p
  */
@@ -22,22 +24,10 @@
 #ifndef OPENSSL_EC_PUBLIC_KEY_H_
 #define OPENSSL_EC_PUBLIC_KEY_H_

-typedef struct openssl_ec_public_key_t openssl_ec_public_key_t;
+#include <openssl/evp.h>

-#include <credentials/builder.h>
 #include <credentials/keys/public_key.h>

-/**
- * public_key_t implementation of ECDSA using OpenSSL.
- */
-struct openssl_ec_public_key_t {
-
-	/**
-	 * Implements the public_key_t interface
-	 */
-	public_key_t key;
-};
-
 /**
  * Load a ECDSA public key using OpenSSL.
  *
@@ -47,7 +37,14 @@ struct openssl_ec_public_key_t {
  * @param args		builder_part_t argument list
  * @return 			loaded key, NULL on failure
  */
-openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
-													va_list args);
+public_key_t *openssl_ec_public_key_load(key_type_t type, va_list args);
+
+/**
+ * Wrap an EVP_PKEY object of type ECDSA.
+ *
+ * @param key		EVP_PKEY object (adopted)
+ * @return 			generated key, NULL on failure
+ */
+public_key_t *openssl_ec_public_key_create(EVP_PKEY *key);

 #endif /** OPENSSL_EC_PUBLIC_KEY_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
index b6974ca1a4..3a29900218 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
+++ b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
@@ -67,6 +67,7 @@ int asn1_unwrap(chunk_t*, chunk_t*);

 /* from ed public key */
 int openssl_ed_key_type(key_type_t type);
+key_type_t openssl_ed_evp_pkey_key_type(EVP_PKEY *key);
 int openssl_ed_keysize(key_type_t type);

 METHOD(private_key_t, sign, bool,
@@ -257,17 +258,11 @@ private_key_t *openssl_ed_private_key_create(EVP_PKEY *key, bool engine)
 	private_private_key_t *this;
 	key_type_t type;

-	switch (EVP_PKEY_base_id(key))
+	type = openssl_ed_evp_pkey_key_type(key);
+	if (type == KEY_ANY)
 	{
-		case EVP_PKEY_ED25519:
-			type = KEY_ED25519;
-			break;
-		case EVP_PKEY_ED448:
-			type = KEY_ED448;
-			break;
-		default:
-			EVP_PKEY_free(key);
-			return NULL;
+		EVP_PKEY_free(key);
+		return NULL;
 	}

 	this = create_internal(type, key);
diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
index 0467c3d79a..6eb4ce98dc 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
+++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
@@ -69,6 +69,22 @@ int openssl_ed_key_type(key_type_t type)
 	}
 }

+/**
+ * Map an EVP_PKEY type to a key type
+ */
+key_type_t openssl_ed_evp_pkey_key_type(EVP_PKEY *key)
+{
+	switch (EVP_PKEY_base_id(key))
+	{
+		case EVP_PKEY_ED25519:
+			return KEY_ED25519;
+		case EVP_PKEY_ED448:
+			return KEY_ED448;
+		default:
+			return KEY_ANY;
+	}
+}
+
 /**
  * Map a key type to a key size
  */
@@ -203,6 +219,28 @@ static private_public_key_t *create_empty(key_type_t type)
 	return this;
 }

+/*
+ * Described in header
+ */
+public_key_t *openssl_ed_public_key_create(EVP_PKEY *key)
+{
+	private_public_key_t *this;
+	key_type_t type;
+
+	if (key)
+	{
+		type = openssl_ed_evp_pkey_key_type(key);
+		if (type != KEY_ANY)
+		{
+			this = create_empty(type);
+			this->key = key;
+			return &this->public;
+		}
+		EVP_PKEY_free(key);
+	}
+	return NULL;
+}
+
 /*
  * Described in header
  */
diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
index 29155d1686..0e3b34b55e 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
+++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
@@ -36,4 +36,12 @@
  */
 public_key_t *openssl_ed_public_key_load(key_type_t type, va_list args);

+/**
+ * Wrap an EVP_PKEY object of type EdDSA.
+ *
+ * @param key		EVP_PKEY object (adopted)
+ * @return 			generated key, NULL on failure
+ */
+public_key_t *openssl_ed_public_key_create(EVP_PKEY *key);
+
 #endif /** OPENSSL_ED_PUBLIC_KEY_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
index 6f736aa0dc..2678357aa7 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
+++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
@@ -234,7 +234,7 @@ METHOD(public_key_t, destroy, void,
 /**
  * Generic private constructor
  */
-static private_public_key_t *create_empty(key_type_t type)
+static public_key_t *create_internal(key_type_t type, EVP_PKEY *key)
 {
 	private_public_key_t *this;

@@ -252,10 +252,27 @@ static private_public_key_t *create_empty(key_type_t type)
 			.destroy = _destroy,
 		},
 		.type = type,
+		.key = key,
 		.ref = 1,
 	);

-	return this;
+	return &this->public;
+}
+
+/*
+ * Described in header
+ */
+public_key_t *openssl_ml_dsa_public_key_create(EVP_PKEY *key)
+{
+	key_type_t type;
+
+	type = openssl_ml_dsa_evp_pkey_key_type(key);
+	if (type == KEY_ANY)
+	{
+		EVP_PKEY_free(key);
+		return NULL;
+	}
+	return create_internal(type, key);
 }

 /*
@@ -263,7 +280,6 @@ static private_public_key_t *create_empty(key_type_t type)
  */
 public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args)
 {
-	private_public_key_t *this;
 	chunk_t pkcs1 = chunk_empty, blob = chunk_empty;
 	EVP_PKEY *key = NULL;

@@ -289,25 +305,25 @@ public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args)
 	{
 		key = EVP_PKEY_new_raw_public_key(openssl_ml_dsa_key_type(type), NULL,
 										  blob.ptr, blob.len);
+		if (key)
+		{
+			return create_internal(type, key);
+		}
 	}
 	else if (pkcs1.len)
 	{
 		key = d2i_PUBKEY(NULL, (const u_char**)&pkcs1.ptr, pkcs1.len);
-		type = openssl_ml_dsa_evp_pkey_key_type(key);
-		if (key && type == KEY_ANY)
+		if (key)
 		{
+			if (type == openssl_ml_dsa_evp_pkey_key_type(key))
+			{
+				return create_internal(type, key);
+			}
 			EVP_PKEY_free(key);
 			return NULL;
 		}
 	}
-	if (!key)
-	{
-		return NULL;
-	}
-	this = create_empty(type);
-	this->key = key;
-
-	return &this->public;
+	return NULL;
 }

 #endif /* OPENSSL_VERSION_NUMBER */
diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
index 82185a19a8..e6e1a9304c 100644
--- a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
+++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
@@ -36,4 +36,12 @@
  */
 public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args);

+/**
+ * Wrap an EVP_PKEY object of type ML-DSA-44/65/87.
+ *
+ * @param key		EVP_PKEY object (adopted)
+ * @return 			generated key, NULL on failure
+ */
+public_key_t *openssl_ml_dsa_public_key_create(EVP_PKEY *key);
+
 #endif /** OPENSSL_ML_DSA_PUBLIC_KEY_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c
index b5ac88d0bb..df167bad14 100644
--- a/src/libstrongswan/plugins/openssl/openssl_plugin.c
+++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c
@@ -24,6 +24,7 @@

 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/x509.h>
 #include <openssl/conf.h>
 #include <openssl/rand.h>
 #include <openssl/crypto.h>
@@ -269,7 +270,7 @@ static bool seed_rng()
 #endif /* OPENSSL_VERSION_NUMBER */

 /**
- * Generic key loader
+ * Generic private key loader
  */
 static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
 {
@@ -298,6 +299,36 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
 	return NULL;
 }

+/**
+ * Generic public key loader
+ */
+static public_key_t *openssl_public_key_load(key_type_t type, va_list args)
+{
+	chunk_t blob = chunk_empty;
+	EVP_PKEY *key;
+
+	while (TRUE)
+	{
+		switch (va_arg(args, builder_part_t))
+		{
+			case BUILD_BLOB_ASN1_DER:
+				blob = va_arg(args, chunk_t);
+				continue;
+			case BUILD_END:
+				break;
+			default:
+				return NULL;
+		}
+		break;
+	}
+	if (blob.ptr)
+	{
+		key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
+		return openssl_wrap_public_key(key);
+	}
+	return NULL;
+}
+
 METHOD(plugin_t, get_name, char*,
 	private_openssl_plugin_t *this)
 {
@@ -547,8 +578,6 @@ METHOD(plugin_t, get_features, int,
 			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_RSA),
 		PLUGIN_REGISTER(PUBKEY, openssl_rsa_public_key_load, TRUE),
 			PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
-		PLUGIN_REGISTER(PUBKEY, openssl_rsa_public_key_load, TRUE),
-			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
 		/* signature/encryption schemes */
 		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_NULL),
 		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL),
@@ -678,7 +707,6 @@ METHOD(plugin_t, get_features, int,
 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
-			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
 		PLUGIN_REGISTER(PRIVKEY, openssl_ml_dsa_private_key_load, TRUE),
 			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
 			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
@@ -696,6 +724,8 @@ METHOD(plugin_t, get_features, int,
 #endif /* OPENSSL_NO_ML_DSA */

 		/* generic key loader */
+		PLUGIN_REGISTER(PUBKEY, openssl_public_key_load, TRUE),
+			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
 		PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE),
 			PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
 		PLUGIN_REGISTER(PRIVKEY, openssl_private_key_connect, FALSE),
diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
index 1607d75790..4bbd552c04 100644
--- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
+++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
@@ -47,10 +47,11 @@ typedef struct private_openssl_rsa_public_key_t private_openssl_rsa_public_key_t
  * Private data structure with signing context.
  */
 struct private_openssl_rsa_public_key_t {
+
 	/**
-	 * Public interface for this signer.
+	 * Public interface
 	 */
-	openssl_rsa_public_key_t public;
+	public_key_t public;

 	/**
 	 * RSA key object
@@ -479,7 +480,7 @@ METHOD(public_key_t, get_ref, public_key_t*,
 	private_openssl_rsa_public_key_t *this)
 {
 	ref_get(&this->ref);
-	return &this->public.key;
+	return &this->public;
 }

 METHOD(public_key_t, destroy, void,
@@ -505,18 +506,16 @@ static private_openssl_rsa_public_key_t *create_internal(EVP_PKEY *key)

 	INIT(this,
 		.public = {
-			.key = {
-				.get_type = _get_type,
-				.verify = _verify,
-				.encrypt = _encrypt,
-				.equals = public_key_equals,
-				.get_keysize = _get_keysize,
-				.get_fingerprint = _get_fingerprint,
-				.has_fingerprint = public_key_has_fingerprint,
-				.get_encoding = _get_encoding,
-				.get_ref = _get_ref,
-				.destroy = _destroy,
-			},
+			.get_type = _get_type,
+			.verify = _verify,
+			.encrypt = _encrypt,
+			.equals = public_key_equals,
+			.get_keysize = _get_keysize,
+			.get_fingerprint = _get_fingerprint,
+			.has_fingerprint = public_key_has_fingerprint,
+			.get_encoding = _get_encoding,
+			.get_ref = _get_ref,
+			.destroy = _destroy,
 		},
 		.ref = 1,
 		.key = key,
@@ -525,11 +524,26 @@ static private_openssl_rsa_public_key_t *create_internal(EVP_PKEY *key)
 	return this;
 }

+/*
+ * Described in header
+ */
+public_key_t *openssl_rsa_public_key_create(EVP_PKEY *key)
+{
+	private_openssl_rsa_public_key_t *this;
+
+	if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
+	{
+		EVP_PKEY_free(key);
+		return NULL;
+	}
+	this = create_internal(key);
+	return &this->public;
+}
+
 /**
  * See header.
  */
-openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
-													  va_list args)
+public_key_t *openssl_rsa_public_key_load(key_type_t type, va_list args)
 {
 	private_openssl_rsa_public_key_t *this;
 	EVP_PKEY *key = NULL;
@@ -559,40 +573,23 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,

 	if (blob.ptr)
 	{
-		switch (type)
-		{
-			case KEY_ANY:
-				key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
-				if (key && EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
-				{
-					EVP_PKEY_free(key);
-					key = NULL;
-				}
-				break;
-			case KEY_RSA:
 #if defined(OPENSSL_IS_BORINGSSL) && \
 	(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 10)
-			{
-				RSA *rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
-											blob.len);
-				key = EVP_PKEY_new();
-				if (!key || !EVP_PKEY_assign_RSA(key, rsa))
-				{
-					RSA_free(rsa);
-					EVP_PKEY_free(key);
-					key = NULL;
-				}
-			}
-#else
-				key = d2i_PublicKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
+		RSA *rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
 									blob.len);
-#endif
-				break;
-			default:
-				break;
+		key = EVP_PKEY_new();
+		if (!key || !EVP_PKEY_assign_RSA(key, rsa))
+		{
+			RSA_free(rsa);
+			EVP_PKEY_free(key);
+			key = NULL;
 		}
+#else
+		key = d2i_PublicKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
+							blob.len);
+#endif
 	}
-	else if (n.ptr && e.ptr && type == KEY_RSA)
+	else if (n.ptr && e.ptr)
 	{
 		BIGNUM *bn_n, *bn_e;

diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
index 77a573a638..a3945476b3 100644
--- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
+++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
@@ -15,6 +15,8 @@
  */

 /**
+ * public_key_t implementation of RSA algorithm using OpenSSL.
+ *
  * @defgroup openssl_rsa_public_key openssl_rsa_public_key
  * @{ @ingroup openssl_p
  */
@@ -22,21 +24,10 @@
 #ifndef OPENSSL_RSA_PUBLIC_KEY_H_
 #define OPENSSL_RSA_PUBLIC_KEY_H_

-typedef struct openssl_rsa_public_key_t openssl_rsa_public_key_t;
+#include <openssl/evp.h>

 #include <credentials/keys/public_key.h>

-/**
- * public_key_t implementation of RSA algorithm using OpenSSL.
- */
-struct openssl_rsa_public_key_t {
-
-	/**
-	 * Implements the public_key_t interface
-	 */
-	public_key_t key;
-};
-
 /**
  * Load a RSA public key using OpenSSL.
  *
@@ -46,7 +37,14 @@ struct openssl_rsa_public_key_t {
  * @param args		builder_part_t argument list
  * @return 			loaded key, NULL on failure
  */
-openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
-													  va_list args);
+public_key_t *openssl_rsa_public_key_load(key_type_t type, va_list args);
+
+/**
+ * Wrap an EVP_PKEY object of type RSA.
+ *
+ * @param key		EVP_PKEY object (adopted)
+ * @return 			generated key, NULL on failure
+ */
+public_key_t *openssl_rsa_public_key_create(EVP_PKEY *key);

 #endif /** OPENSSL_RSA_PUBLIC_KEY_H_ @}*/
diff --git a/src/libstrongswan/plugins/openssl/openssl_util.c b/src/libstrongswan/plugins/openssl/openssl_util.c
index 212a598265..5bdfb42822 100644
--- a/src/libstrongswan/plugins/openssl/openssl_util.c
+++ b/src/libstrongswan/plugins/openssl/openssl_util.c
@@ -30,9 +30,13 @@
 #endif

 #include "openssl_ec_private_key.h"
+#include "openssl_ec_public_key.h"
 #include "openssl_ed_private_key.h"
+#include "openssl_ed_public_key.h"
 #include "openssl_rsa_private_key.h"
+#include "openssl_rsa_public_key.h"
 #include "openssl_ml_dsa_private_key.h"
+#include "openssl_ml_dsa_public_key.h"

 /* these were added with 1.1.0 when ASN1_OBJECT was made opaque */
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -203,6 +207,48 @@ private_key_t *openssl_wrap_private_key(EVP_PKEY *key, bool engine)
 	return NULL;
 }

+/*
+ * Described in header
+ */
+public_key_t *openssl_wrap_public_key(EVP_PKEY *key)
+{
+	if (key)
+	{
+		switch (EVP_PKEY_base_id(key))
+		{
+#ifndef OPENSSL_NO_RSA
+			case EVP_PKEY_RSA:
+				return openssl_rsa_public_key_create(key);
+#endif
+#ifndef OPENSSL_NO_ECDSA
+			case EVP_PKEY_EC:
+				return openssl_ec_public_key_create(key);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC)
+			case EVP_PKEY_ED25519:
+#ifndef OPENSSL_IS_AWSLC
+			case EVP_PKEY_ED448:
+#endif
+				return openssl_ed_public_key_create(key);
+#endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC && !OPENSSL_IS_AWSLC */
+			default:
+#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
+				/* OpenSSL does not provide an ID for ML-DSA keys, which is
+				 * apparently a legacy feature */
+				if (EVP_PKEY_is_a(key, "ML-DSA-44") ||
+					EVP_PKEY_is_a(key, "ML-DSA-65") ||
+					EVP_PKEY_is_a(key, "ML-DSA-87"))
+				{
+					return openssl_ml_dsa_public_key_create(key);
+				}
+#endif
+				EVP_PKEY_free(key);
+				break;
+		}
+	}
+	return NULL;
+}
+
 /**
  * Described in header.
  */
diff --git a/src/libstrongswan/plugins/openssl/openssl_util.h b/src/libstrongswan/plugins/openssl/openssl_util.h
index 7c72db2cce..9e1ccb2ecc 100644
--- a/src/libstrongswan/plugins/openssl/openssl_util.h
+++ b/src/libstrongswan/plugins/openssl/openssl_util.h
@@ -66,6 +66,14 @@ bool openssl_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
  */
 private_key_t *openssl_wrap_private_key(EVP_PKEY *key, bool engine);

+/**
+ * Wrap the given OpenSSL public key in a type-specific public key object.
+ *
+ * @param key		key object to wrap
+ * @returns			created object or NULL if key type is not supported
+ */
+public_key_t *openssl_wrap_public_key(EVP_PKEY *key);
+
 /**
  * Concatenates two bignums into a chunk, thereby enforcing the length of
  * a single BIGNUM, if necessary, by prepending it with zeros.