diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs
index b5cf5295e47872..3563e43af40cf4 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EcDsa.ImportExport.cs
@@ -11,34 +11,53 @@ internal static partial class Interop
 {
     internal static partial class Crypto
     {
-        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByKeyParameters", StringMarshalling = StringMarshalling.Utf8)]
-        private static partial int EcKeyCreateByKeyParameters(
-            out SafeEcKeyHandle key,
+        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcParameters", StringMarshalling = StringMarshalling.Utf8)]
+        private static partial int EvpPKeyCreateByEcParameters(
+            out SafeEvpPKeyHandle pkey,
             string oid,
             byte[]? qx, int qxLength,
             byte[]? qy, int qyLength,
-            byte[]? d, int dLength);
+            byte[]? d, int dLength,
+            out int keySize);
 
-        internal static SafeEcKeyHandle EcKeyCreateByKeyParameters(
+        internal static SafeEvpPKeyHandle EvpPKeyCreateByEcParameters(
             string oid,
-            byte[]? qx, int qxLength,
-            byte[]? qy, int qyLength,
-            byte[]? d, int dLength)
+            byte[]? qx,
+            byte[]? qy,
+            byte[]? d,
+            out int keySize)
         {
-            SafeEcKeyHandle key;
-            int rc = EcKeyCreateByKeyParameters(out key, oid, qx, qxLength, qy, qyLength, d, dLength);
-            if (rc == -1)
+            SafeEvpPKeyHandle pkey;
+            int rc = EvpPKeyCreateByEcParameters(
+                out pkey, oid,
+                qx, qx?.Length ?? 0,
+                qy, qy?.Length ?? 0,
+                d, d?.Length ?? 0,
+                out keySize);
+
+            if (rc == 2)
             {
-                key?.Dispose();
+                pkey?.Dispose();
                 Interop.Crypto.ErrClearError();
-
                 throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
             }
-            return key;
+
+            if (rc != 1 || pkey == null || pkey.IsInvalid)
+            {
+                Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
+                pkey?.Dispose();
+                throw ex;
+            }
+
+            // EvpPKeyCreateByEcParameters may have polluted the error queue, but key was good in the end.
+            // Clean up the error queue.
+            Interop.Crypto.ErrClearError();
+
+            return pkey;
         }
 
-        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByExplicitParameters")]
-        internal static partial SafeEcKeyHandle EcKeyCreateByExplicitParameters(
+        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcExplicitParameters")]
+        private static partial int EvpPKeyCreateByEcExplicitParameters(
             ECCurve.ECCurveType curveType,
             byte[]? qx, int qxLength,
             byte[]? qy, int qyLength,
@@ -48,11 +67,18 @@ internal static partial SafeEcKeyHandle EcKeyCreateByExplicitParameters(
             byte[] b, int bLength,
             byte[] gx, int gxLength,
             byte[] gy, int gyLength,
-            byte[] order, int nLength,
+            byte[] order, int orderLength,
             byte[]? cofactor, int cofactorLength,
-            byte[]? seed, int seedLength);
-
-        internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve)
+            byte[]? seed, int seedLength,
+            out SafeEvpPKeyHandle pkey,
+            out int keySize);
+
+        internal static SafeEvpPKeyHandle EvpPKeyCreateByEcExplicitParameters(
+            ECCurve curve,
+            byte[]? qx,
+            byte[]? qy,
+            byte[]? d,
+            out int keySize)
         {
             byte[] p;
             if (curve.IsPrime)
@@ -68,32 +94,65 @@ internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve)
                 throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, curve.CurveType.ToString()));
             }
 
-            SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
+            int rc = EvpPKeyCreateByEcExplicitParameters(
                 curve.CurveType,
-                null, 0,
-                null, 0,
-                null, 0,
+                qx, qx?.Length ?? 0,
+                qy, qy?.Length ?? 0,
+                d, d?.Length ?? 0,
                 p, p.Length,
                 curve.A!, curve.A!.Length,
                 curve.B!, curve.B!.Length,
                 curve.G.X!, curve.G.X!.Length,
                 curve.G.Y!, curve.G.Y!.Length,
                 curve.Order!, curve.Order!.Length,
-                curve.Cofactor, curve.Cofactor!.Length,
-                curve.Seed, curve.Seed == null ? 0 : curve.Seed.Length);
+                curve.Cofactor, curve.Cofactor?.Length ?? 0,
+                curve.Seed, curve.Seed?.Length ?? 0,
+                out SafeEvpPKeyHandle pkey,
+                out keySize);
 
-            if (key == null || key.IsInvalid)
+            if (rc != 1 || pkey == null || pkey.IsInvalid)
             {
-                Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
-                key?.Dispose();
-                throw e;
+                Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
+                pkey?.Dispose();
+                throw ex;
             }
 
-            // EcKeyCreateByExplicitParameters may have polluted the error queue, but key was good in the end.
+            // EvpPKeyCreateByEcExplicitParameters may have polluted the error queue, but key was good in the end.
             // Clean up the error queue.
             Interop.Crypto.ErrClearError();
 
-            return key;
+            return pkey;
+        }
+
+        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcCurveOid", StringMarshalling = StringMarshalling.Utf8)]
+        private static partial int EvpPKeyGenerateByEcCurveOid(
+            out SafeEvpPKeyHandle pkey,
+            string oid,
+            out int keySize);
+
+        internal static SafeEvpPKeyHandle EvpPKeyGenerateByEcCurveOid(string oid, out int keySize)
+        {
+            int rc = EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out keySize);
+
+            if (rc == 2)
+            {
+                pkey?.Dispose();
+                Interop.Crypto.ErrClearError();
+                throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
+            }
+
+            if (rc != 1 || pkey == null || pkey.IsInvalid)
+            {
+                Exception ex = Interop.Crypto.CreateOpenSslCryptographicException();
+                pkey?.Dispose();
+                throw ex;
+            }
+
+            // EvpPKeyGenerateByEcCurveOid may have polluted the error queue, but key was good in the end.
+            // Clean up the error queue.
+            Interop.Crypto.ErrClearError();
+
+            return pkey;
         }
 
         [LibraryImport(Libraries.CryptoNative)]
@@ -104,11 +163,12 @@ internal static bool EvpPKeyHasCurveName(SafeEvpPKeyHandle pkey)
             int rc = CryptoNative_EvpPKeyGetEcGroupNid(pkey, out int nidCurveName);
             if (rc == 1)
             {
-                // Key is invalid or doesn't have a curve
-                return (nidCurveName != Interop.Crypto.NID_undef);
+                return nidCurveName != Interop.Crypto.NID_undef;
             }
 
-            throw Interop.Crypto.CreateOpenSslCryptographicException();
+            // rc == 0 means the group name could not be retrieved
+            // (e.g., explicit curve or OpenSSL < 3.0). Treat as no curve name.
+            return false;
         }
 
         /// <summary>
@@ -122,7 +182,36 @@ internal static bool EvpPKeyHasCurveName(SafeEvpPKeyHandle pkey)
                 return nidCurveName != Interop.Crypto.NID_undef ? CurveNidToOidValue(nidCurveName) : null;
             }
 
-            throw Interop.Crypto.CreateOpenSslCryptographicException();
+            // rc == 0 means the group name could not be retrieved
+            // (e.g., explicit curve or OpenSSL < 3.0).
+            return null;
+        }
+
+        [LibraryImport(Libraries.CryptoNative)]
+        private static partial int CryptoNative_EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey);
+
+        /// <summary>
+        /// Returns <see langword="true"/> if the key has explicit encoding, <see langword="false"/> if named.
+        /// </summary>
+        /// <exception cref="CryptographicException">The encoding could not be determined from the key.</exception>
+        internal static bool EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey)
+        {
+            int result = CryptoNative_EvpPKeyEcHasExplicitEncoding(pkey);
+
+            if (result < 0)
+            {
+                throw CreateOpenSslCryptographicException();
+            }
+
+            return result == 1;
+        }
+
+        [LibraryImport(Libraries.CryptoNative)]
+        private static partial int CryptoNative_EvpPKeyGetEcKeySize(SafeEvpPKeyHandle pkey);
+
+        internal static int EvpPKeyGetEcKeySize(SafeEvpPKeyHandle pkey)
+        {
+            return CryptoNative_EvpPKeyGetEcKeySize(pkey);
         }
 
         [LibraryImport(Libraries.CryptoNative)]
diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
index 1f65d98a5bfa92..9efaddc6e8e4ad 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
@@ -88,20 +88,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa
             Debug.Assert(otherPartyPublicKey != null);
             Debug.Assert(_key is not null); // Callers should validate prior.
 
-            bool thisIsNamed;
-
-            using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key.Value))
-            {
-                if (ecKey == null || ecKey.IsInvalid)
-                {
-                    // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible.
-                    thisIsNamed = Interop.Crypto.EvpPKeyHasCurveName(_key.Value);
-                }
-                else
-                {
-                    thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey);
-                }
-            }
+            bool thisIsNamed = !Interop.Crypto.EvpPKeyEcHasExplicitEncoding(_key.Value);
 
             ECDiffieHellmanOpenSslPublicKey? otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey;
             bool disposeOtherKey = false;
@@ -118,7 +105,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa
                 otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters);
             }
 
-            bool otherIsNamed = otherKey.HasCurveName;
+            bool otherIsNamed = !otherKey.HasExplicitEncoding;
 
             // We need to always duplicate handle in case this operation is done by multiple threads and one of them disposes the handle
             SafeEvpPKeyHandle? ourKey = _key.Value;
@@ -143,10 +130,7 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa
                 }
                 else if (otherIsNamed)
                 {
-                    using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters()))
-                    {
-                        theirKey = tmp.CreateEvpPKeyHandle();
-                    }
+                    theirKey = ECOpenSsl.ImportECKey(otherKey.ExportExplicitParameters(), out _);
                 }
                 else
                 {
@@ -155,11 +139,8 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa
                         // This is generally not expected to fail except:
                         // - when key can't be accessed but is available (i.e. TPM)
                         // - private key is actually missing
-                        using (ECOpenSsl tmp = new ECOpenSsl(ExportExplicitParameters(true)))
-                        {
-                            ourKey = tmp.CreateEvpPKeyHandle();
-                            disposeOurKey = true;
-                        }
+                        ourKey = ECOpenSsl.ImportECKey(ExportExplicitParameters(true), out _);
+                        disposeOurKey = true;
                     }
                     catch (CryptographicException)
                     {
diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs
index 8a225c5b4b3951..cf8f670bbfe3af 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSslPublicKey.cs
@@ -8,7 +8,7 @@ namespace System.Security.Cryptography
 {
     internal sealed class ECDiffieHellmanOpenSslPublicKey : ECDiffieHellmanPublicKey
     {
-        private ECOpenSsl? _key;
+        private SafeEvpPKeyHandle? _key;
 
         internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle)
         {
@@ -17,27 +17,12 @@ internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle)
             if (pkeyHandle.IsInvalid)
                 throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(pkeyHandle));
 
-            // If ecKey is valid it has already been up-ref'd, so we can just use this handle as-is.
-            SafeEcKeyHandle key = Interop.Crypto.EvpPkeyGetEcKey(pkeyHandle);
-
-            if (key == null || key.IsInvalid)
-            {
-                key?.Dispose();
-
-                // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible.
-                // Since you cannot mix EC_KEY and EVP_PKEY params API we need to export and re-import the public key.
-                ECParameters ecParams = ECOpenSsl.ExportParameters(pkeyHandle, includePrivateParameters: false);
-                _key = new ECOpenSsl(ecParams);
-            }
-            else
-            {
-                _key = new ECOpenSsl(key);
-            }
+            _key = pkeyHandle.DuplicateHandle();
         }
 
         internal ECDiffieHellmanOpenSslPublicKey(ECParameters parameters)
         {
-            _key = new ECOpenSsl(parameters);
+            _key = ECOpenSsl.ImportECKey(parameters, out _);
         }
 
 #pragma warning disable 0672 // Member overrides an obsolete member.
@@ -60,14 +45,31 @@ public override ECParameters ExportExplicitParameters() =>
         public override ECParameters ExportParameters() =>
             ECOpenSsl.ExportParameters(GetKey(), includePrivateParameters: false);
 
-        internal bool HasCurveName => Interop.Crypto.EcKeyHasCurveName(GetKey());
+        internal bool HasCurveName => Interop.Crypto.EvpPKeyHasCurveName(GetKey());
+
+        internal bool HasExplicitEncoding
+        {
+            get
+            {
+                ThrowIfDisposed();
+                return Interop.Crypto.EvpPKeyEcHasExplicitEncoding(GetKey());
+            }
+        }
 
         internal int KeySize
         {
             get
             {
                 ThrowIfDisposed();
-                return _key.KeySize;
+
+                int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(_key);
+
+                if (keySize == 0)
+                {
+                    throw new CryptographicException(SR.Cryptography_InvalidHandle);
+                }
+
+                return keySize;
             }
         }
 
@@ -84,8 +86,7 @@ protected override void Dispose(bool disposing)
 
         internal SafeEvpPKeyHandle DuplicateKeyHandle()
         {
-            SafeEcKeyHandle currentKey = GetKey();
-            return Interop.Crypto.CreateEvpPkeyFromEcKey(currentKey);
+            return GetKey().DuplicateHandle();
         }
 
         [MemberNotNull(nameof(_key))]
@@ -94,10 +95,10 @@ private void ThrowIfDisposed()
             ObjectDisposedException.ThrowIf(_key is null, this);
         }
 
-        private SafeEcKeyHandle GetKey()
+        private SafeEvpPKeyHandle GetKey()
         {
             ThrowIfDisposed();
-            return _key.Value;
+            return _key;
         }
     }
 }
diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs
index 625b532a023015..4c887fd999892c 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs
@@ -6,56 +6,16 @@
 
 namespace System.Security.Cryptography
 {
-    internal sealed partial class ECOpenSsl
+    internal static partial class ECOpenSsl
     {
         internal const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1
         internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1
         internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1
 
-        public int ImportParameters(ECParameters parameters)
-        {
-            SafeEcKeyHandle key;
-
-            parameters.Validate();
-
-            if (parameters.Curve.IsPrime)
-            {
-                key = ImportPrimeCurveParameters(parameters);
-            }
-            else if (parameters.Curve.IsCharacteristic2)
-            {
-                key = ImportCharacteristic2CurveParameters(parameters);
-            }
-            else if (parameters.Curve.IsNamed)
-            {
-                key = ImportNamedCurveParameters(parameters);
-            }
-            else
-            {
-                throw new PlatformNotSupportedException(
-                    SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString()));
-            }
-
-            if (key == null || key.IsInvalid)
-            {
-                Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
-                key?.Dispose();
-                throw e;
-            }
-
-            // The Import* methods above may have polluted the error queue even if in the end they succeeded.
-            // Clean up the error queue.
-            Interop.Crypto.ErrClearError();
-
-            FreeKey();
-            _key = new Lazy<SafeEcKeyHandle>(key);
-            return KeySize;
-        }
-
-        public static ECParameters ExportExplicitParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) =>
+        private static ECParameters ExportExplicitParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters) =>
             ExportExplicitCurveParameters(currentKey, includePrivateParameters);
 
-        public static ECParameters ExportParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters)
+        private static ECParameters ExportParameters(SafeEcKeyHandle currentKey, bool includePrivateParameters)
         {
             ECParameters ecparams;
             if (Interop.Crypto.EcKeyHasCurveName(currentKey))
@@ -104,63 +64,6 @@ private static ECParameters ExportExplicitCurveParameters(SafeEcKeyHandle key, b
             return parameters;
         }
 
-        private static SafeEcKeyHandle ImportNamedCurveParameters(ECParameters parameters)
-        {
-            Debug.Assert(parameters.Curve.IsNamed);
-
-            // Use oid Value first if present, otherwise FriendlyName
-            string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ?
-                parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!;
-
-            SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByKeyParameters(
-                oid,
-                parameters.Q.X, parameters.Q.X?.Length ?? 0,
-                parameters.Q.Y, parameters.Q.Y?.Length ?? 0,
-                parameters.D, parameters.D == null ? 0 : parameters.D.Length);
-
-            return key;
-        }
-
-        private static SafeEcKeyHandle ImportPrimeCurveParameters(ECParameters parameters)
-        {
-            Debug.Assert(parameters.Curve.IsPrime);
-            SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
-                parameters.Curve.CurveType,
-                parameters.Q.X, parameters.Q.X?.Length ?? 0,
-                parameters.Q.Y, parameters.Q.Y?.Length ?? 0,
-                parameters.D, parameters.D == null ? 0 : parameters.D.Length,
-                parameters.Curve.Prime!, parameters.Curve.Prime!.Length,
-                parameters.Curve.A!, parameters.Curve.A!.Length,
-                parameters.Curve.B!, parameters.Curve.B!.Length,
-                parameters.Curve.G.X!, parameters.Curve.G.X!.Length,
-                parameters.Curve.G.Y!, parameters.Curve.G.Y!.Length,
-                parameters.Curve.Order!, parameters.Curve.Order!.Length,
-                parameters.Curve.Cofactor, parameters.Curve.Cofactor!.Length,
-                parameters.Curve.Seed, parameters.Curve.Seed == null ? 0 : parameters.Curve.Seed.Length);
-
-            return key;
-        }
-
-        private static SafeEcKeyHandle ImportCharacteristic2CurveParameters(ECParameters parameters)
-        {
-            Debug.Assert(parameters.Curve.IsCharacteristic2);
-            SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
-                parameters.Curve.CurveType,
-                parameters.Q.X, parameters.Q.X?.Length ?? 0,
-                parameters.Q.Y, parameters.Q.Y?.Length ?? 0,
-                parameters.D, parameters.D == null ? 0 : parameters.D.Length,
-                parameters.Curve.Polynomial!, parameters.Curve.Polynomial!.Length,
-                parameters.Curve.A!, parameters.Curve.A!.Length,
-                parameters.Curve.B!, parameters.Curve.B!.Length,
-                parameters.Curve.G.X!, parameters.Curve.G.X!.Length,
-                parameters.Curve.G.Y!, parameters.Curve.G.Y!.Length,
-                parameters.Curve.Order!, parameters.Curve.Order!.Length,
-                parameters.Curve.Cofactor, parameters.Curve.Cofactor!.Length,
-                parameters.Curve.Seed, parameters.Curve.Seed == null ? 0 : parameters.Curve.Seed.Length);
-
-            return key;
-        }
-
         private static void CheckInvalidKey(SafeEcKeyHandle key)
         {
             if (key == null || key.IsInvalid)
@@ -181,14 +84,13 @@ public static ECParameters ExportParameters(SafeEvpPKeyHandle pkey, bool include
         {
             CheckInvalidKey(pkey);
 
-            using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey))
+            if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0)
             {
-                if (ecKey == null || ecKey.IsInvalid)
-                {
-                    // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible.
-                    return ExportECParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
-                }
+                return ExportECParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
+            }
 
+            using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey))
+            {
                 return ECOpenSsl.ExportParameters(ecKey, includePrivateParameters);
             }
         }
@@ -197,14 +99,13 @@ public static ECParameters ExportExplicitParameters(SafeEvpPKeyHandle pkey, bool
         {
             CheckInvalidKey(pkey);
 
-            using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey))
+            if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0)
             {
-                if (ecKey == null || ecKey.IsInvalid)
-                {
-                    // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible.
-                    return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
-                }
+                return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
+            }
 
+            using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(pkey))
+            {
                 return ECOpenSsl.ExportExplicitParameters(ecKey, includePrivateParameters);
             }
         }
@@ -216,15 +117,21 @@ public static ECParameters ExportExplicitParameters(SafeEvpPKeyHandle pkey, bool
         /// </summary>
         private static ECParameters ExportECParametersFromEvpPKeyUsingParams(SafeEvpPKeyHandle pkey, bool includePrivateParameters)
         {
-            string? curveName = Interop.Crypto.EvpPKeyGetCurveName(pkey);
-            if (curveName == null)
+            // Check encoding first — explicit-encoding keys must be exported with
+            // explicit curve parameters even if OpenSSL can match a named curve.
+            if (Interop.Crypto.EvpPKeyEcHasExplicitEncoding(pkey))
             {
                 return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
             }
-            else
+
+            string? curveName = Interop.Crypto.EvpPKeyGetCurveName(pkey);
+
+            if (curveName is null)
             {
-                return ExportNamedCurveParametersFromEvpPKeyUsingParams(pkey, curveName, includePrivateParameters);
+                return ExportExplicitCurveParametersFromEvpPKeyUsingParams(pkey, includePrivateParameters);
             }
+
+            return ExportNamedCurveParametersFromEvpPKeyUsingParams(pkey, curveName, includePrivateParameters);
         }
 
         private static ECParameters ExportNamedCurveParametersFromEvpPKeyUsingParams(SafeEvpPKeyHandle pkey, string curveName, bool includePrivateParameters)
@@ -257,35 +164,5 @@ private static ECParameters ExportExplicitCurveParametersFromEvpPKeyUsingParams(
 
             return parameters;
         }
-
-        public static SafeEcKeyHandle GenerateKeyByKeySize(int keySize)
-        {
-            string oid;
-            switch (keySize)
-            {
-                case 256: oid = ECDSA_P256_OID_VALUE; break;
-                case 384: oid = ECDSA_P384_OID_VALUE; break;
-                case 521: oid = ECDSA_P521_OID_VALUE; break;
-                default:
-                    // Only above three sizes supported for backwards compatibility; named curves should be used instead
-                    throw new InvalidOperationException(SR.Cryptography_InvalidKeySize);
-            }
-
-            SafeEcKeyHandle? key = Interop.Crypto.EcKeyCreateByOid(oid);
-
-            if (key == null || key.IsInvalid)
-            {
-                key?.Dispose();
-                throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
-            }
-
-            if (!Interop.Crypto.EcKeyGenerateKey(key))
-            {
-                key.Dispose();
-                throw Interop.Crypto.CreateOpenSslCryptographicException();
-            }
-
-            return key;
-        }
     }
 }
diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
index 20fd8788b1053e..7a6f1861d9d696 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
@@ -6,140 +6,79 @@
 
 namespace System.Security.Cryptography
 {
-    internal sealed partial class ECOpenSsl : IDisposable
+    internal static partial class ECOpenSsl
     {
-        private Lazy<SafeEcKeyHandle> _key = null!; // Always initialized
-
-        public ECOpenSsl(ECCurve curve)
-        {
-            GenerateKey(curve);
-        }
-
-        public ECOpenSsl(int keySizeBits)
-        {
-            _key = new Lazy<SafeEcKeyHandle>(() => GenerateKeyByKeySize(keySizeBits));
-        }
-
-        public ECOpenSsl(ECParameters ecParameters)
-        {
-            ImportParameters(ecParameters);
-        }
-
-        // Takes ownership of the key
-        public ECOpenSsl(SafeEcKeyHandle key)
-        {
-            _key = new Lazy<SafeEcKeyHandle>(key);
-        }
-
-        internal SafeEcKeyHandle Value => _key.Value;
-
-        public void Dispose()
-        {
-            FreeKey();
-        }
-
-        internal int KeySize => Interop.Crypto.EcKeyGetSize(_key.Value);
-
-        internal SafeEvpPKeyHandle CreateEvpPKeyHandle()
-        {
-            SafeEcKeyHandle currentKey = _key.Value;
-            Debug.Assert(currentKey != null, "key is null");
-
-            return Interop.Crypto.CreateEvpPkeyFromEcKey(currentKey);
-        }
-
-        private void SetKey(SafeEcKeyHandle key)
+        internal static SafeEvpPKeyHandle GenerateECKey(int keySize)
         {
-            Debug.Assert(key != null);
-            Debug.Assert(!key.IsInvalid);
-            Debug.Assert(!key.IsClosed);
+            string oid = keySize switch
+            {
+                256 => ECOpenSsl.ECDSA_P256_OID_VALUE,
+                384 => ECOpenSsl.ECDSA_P384_OID_VALUE,
+                521 => ECOpenSsl.ECDSA_P521_OID_VALUE,
+                _ => throw new InvalidOperationException(SR.Cryptography_InvalidKeySize),
+            };
 
-            FreeKey();
-            _key = new Lazy<SafeEcKeyHandle>(key);
+            SafeEvpPKeyHandle pkey = Interop.Crypto.EvpPKeyGenerateByEcCurveOid(oid, out int createdKeySize);
+            Debug.Assert(keySize == createdKeySize);
+            return pkey;
         }
 
-        internal int GenerateKey(ECCurve curve)
+        internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize)
         {
             curve.Validate();
-            FreeKey();
 
             if (curve.IsNamed)
             {
-                // Use oid Value first if present, otherwise FriendlyName because Oid maintains a hard-coded
-                // cache that may have different casing for FriendlyNames than OpenSsl
                 string oid = !string.IsNullOrEmpty(curve.Oid.Value) ? curve.Oid.Value : curve.Oid.FriendlyName!;
 
-                SafeEcKeyHandle? key = Interop.Crypto.EcKeyCreateByOid(oid);
-
-                if (key == null || key.IsInvalid)
-                {
-                    key?.Dispose();
-                    throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
-                }
-
-                if (!Interop.Crypto.EcKeyGenerateKey(key))
-                {
-                    throw Interop.Crypto.CreateOpenSslCryptographicException();
-                }
-
-                SetKey(key);
+                return Interop.Crypto.EvpPKeyGenerateByEcCurveOid(oid, out keySize);
             }
             else if (curve.IsExplicit)
             {
-                SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitCurve(curve);
-
-                if (!Interop.Crypto.EcKeyGenerateKey(key))
-                    throw Interop.Crypto.CreateOpenSslCryptographicException();
-
-                SetKey(key);
+                // Pass null Q and null D to trigger key generation instead of import.
+                return Interop.Crypto.EvpPKeyCreateByEcExplicitParameters(
+                    curve,
+                    null,
+                    null,
+                    null,
+                    out keySize);
             }
             else
             {
                 throw new PlatformNotSupportedException(
                     SR.Format(SR.Cryptography_CurveNotSupported, curve.CurveType.ToString()));
             }
-
-            return KeySize;
-        }
-
-        private void FreeKey()
-        {
-            if (_key != null)
-            {
-                if (_key.IsValueCreated)
-                {
-                    _key.Value?.Dispose();
-                }
-
-                _key = null!;
-            }
-        }
-
-        internal static SafeEvpPKeyHandle GenerateECKey(int keySize)
-        {
-            SafeEvpPKeyHandle ret = ImportECKeyCore(new ECOpenSsl(keySize), out int createdKeySize);
-            Debug.Assert(keySize == createdKeySize);
-            return ret;
-        }
-
-        internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize)
-        {
-            return  ImportECKeyCore(new ECOpenSsl(curve), out keySize);
         }
 
         internal static SafeEvpPKeyHandle ImportECKey(ECParameters parameters, out int keySize)
         {
-            return ImportECKeyCore(new ECOpenSsl(parameters), out keySize);
-        }
+            parameters.Validate();
 
-        // Note: This method takes ownership of ecOpenSsl and disposes it
-        private static SafeEvpPKeyHandle ImportECKeyCore(ECOpenSsl ecOpenSsl, out int keySize)
-        {
-            using (ECOpenSsl ec = ecOpenSsl)
+            if (parameters.Curve.IsNamed)
             {
-                SafeEvpPKeyHandle handle = Interop.Crypto.CreateEvpPkeyFromEcKey(ec.Value);
-                keySize = ec.KeySize;
-                return handle;
+                string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ?
+                    parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!;
+
+                return Interop.Crypto.EvpPKeyCreateByEcParameters(
+                    oid,
+                    parameters.Q.X,
+                    parameters.Q.Y,
+                    parameters.D,
+                    out keySize);
+            }
+            else if (parameters.Curve.IsExplicit)
+            {
+                return Interop.Crypto.EvpPKeyCreateByEcExplicitParameters(
+                    parameters.Curve,
+                    parameters.Q.X,
+                    parameters.Q.Y,
+                    parameters.D,
+                    out keySize);
+            }
+            else
+            {
+                throw new PlatformNotSupportedException(
+                    SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.CurveType.ToString()));
             }
         }
     }
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs
index ece11f82401232..c8e9cd762da117 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs
@@ -12,7 +12,10 @@ public interface IECDiffieHellmanProvider
 #endif
         bool IsCurveValid(Oid oid);
         bool ExplicitCurvesSupported { get; }
-        bool ExplicitCurvesSupportFailOnUseOnly => PlatformDetection.IsSymCryptOpenSsl;
+
+        // In OSSL 3+ we use EVP_PKEY APIs instead of EC_KEY APIs so import and export of explicit curves also fails for SymCrypt.
+        bool ExplicitCurvesSupportFailOnUseOnly => PlatformDetection.IsSymCryptOpenSsl && SafeEvpPKeyHandle.OpenSslVersion < 0x3_00_00_00_0;
+
         bool CanDeriveNewPublicKey { get; }
         bool SupportsRawDerivation { get; }
         bool SupportsSha3 { get; }
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs
index 27bb25e7d6674e..ff4cb7cc792238 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs
@@ -510,6 +510,72 @@ private static void VerifyExplicitCurve(ECParameters parameters, ECDiffieHellman
             ECParameters paramSecondExport = ec.ExportExplicitParameters(curveDef.IncludePrivate);
             AssertEqual(parameters, paramSecondExport);
         }
+
+        [Theory]
+        [MemberData(nameof(NistEccCdhPrimeCurveVectors))]
+        public static void EcdhKeyAgreement_PrimeCurve_MatchesKnownSharedSecret(
+            string curveName, string curveOid,
+            string qCavsXHex, string qCavsYHex,
+            string dIutHex, string qIutXHex, string qIutYHex,
+            string expectedZHex)
+        {
+            _ = curveName;
+            ECCurve curve = ECCurve.CreateFromValue(curveOid);
+
+            using (ECDiffieHellman iut = ECDiffieHellmanFactory.Create())
+            using (ECDiffieHellman cavs = ECDiffieHellmanFactory.Create())
+            {
+                iut.ImportParameters(new ECParameters
+                {
+                    Curve = curve,
+                    D = dIutHex.HexToByteArray(),
+                    Q = new ECPoint
+                    {
+                        X = qIutXHex.HexToByteArray(),
+                        Y = qIutYHex.HexToByteArray(),
+                    },
+                });
+
+                cavs.ImportParameters(new ECParameters
+                {
+                    Curve = curve,
+                    Q = new ECPoint
+                    {
+                        X = qCavsXHex.HexToByteArray(),
+                        Y = qCavsYHex.HexToByteArray(),
+                    },
+                });
+
+                byte[] derivedZ = iut.DeriveRawSecretAgreement(cavs.PublicKey);
+                Assert.Equal(expectedZHex.HexToByteArray(), derivedZ);
+            }
+        }
+
+        // NIST SP 800-56A ECCCDH vectors (CAVS 14.1), P-256
+        // Source: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program
+        public static TheoryData<string, string, string, string, string, string, string, string> NistEccCdhPrimeCurveVectors => new()
+        {
+            // P-256, COUNT=0
+            {
+                "P-256", "1.2.840.10045.3.1.7",
+                "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287",
+                "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
+                "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
+                "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230",
+                "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
+                "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
+            },
+            // P-256, COUNT=1
+            {
+                "P-256", "1.2.840.10045.3.1.7",
+                "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae",
+                "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3",
+                "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5",
+                "119f2f047902782ab0c9e27a54aff5eb9b964829ca99c06b02ddba95b0a3f6d0",
+                "8f52b726664cac366fc98ac7a012b2682cbd962e5acb544671d41b9445704d1d",
+                "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67"
+            },
+        };
     }
 #endif
 }
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
index 244fcfe9c0c73b..6a46e313741b00 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
@@ -12,7 +12,9 @@ public interface IECDsaProvider
 #endif
         bool IsCurveValid(Oid oid);
         bool ExplicitCurvesSupported { get; }
-        bool ExplicitCurvesSupportFailOnUseOnly => PlatformDetection.IsSymCryptOpenSsl;
+
+        // In OSSL 3+ we use EVP_PKEY APIs instead of EC_KEY APIs so import and export of explicit curves also fails for SymCrypt.
+        bool ExplicitCurvesSupportFailOnUseOnly => PlatformDetection.IsSymCryptOpenSsl && SafeEvpPKeyHandle.OpenSslVersion < 0x3_00_00_00_0;
     }
 
     public static partial class ECDsaFactory
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
index baf4d7580fd049..08fe32b17260a6 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
@@ -354,6 +354,90 @@ public static void ImportFromPrivateOnlyKey()
             }
         }
 
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Named_P256()
+        {
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP256ReferenceKey(), explicitCurve: false);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Named_P521_DiminishedCoords()
+        {
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP521DiminishedCoordsParameters(), explicitCurve: false);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Named_Sect163k1()
+        {
+            if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid))
+                return;
+
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.Sect163k1Key1, explicitCurve: false);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Named_C2pnb163v1()
+        {
+            if (!ECDsaFactory.IsCurveValid(EccTestData.C2pnb163v1Key1.Curve.Oid))
+                return;
+
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.C2pnb163v1Key1, explicitCurve: false);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Explicit_P256()
+        {
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.GetNistP256ReferenceKeyExplicit(), explicitCurve: true);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Explicit_P521_DiminishedCoords()
+        {
+            ECParameters p521 = EccTestData.GetNistP521DiminishedCoordsParameters();
+            p521.Curve = EccTestData.GetNistP521ExplicitCurve();
+            VerifyPrivateKeyDerivesPublicKey(p521, explicitCurve: true);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Explicit_Sect163k1()
+        {
+            if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid))
+                return;
+
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.Sect163k1Key1Explicit, explicitCurve: true);
+        }
+
+        [ConditionalFact(typeof(ECDsaImportExportTests), nameof(ECExplicitCurvesSupported), nameof(CanDeriveNewPublicKey))]
+        public static void DerivePublicKey_Explicit_C2pnb163v1()
+        {
+            if (!ECDsaFactory.IsCurveValid(EccTestData.C2pnb163v1Key1.Curve.Oid))
+                return;
+
+            VerifyPrivateKeyDerivesPublicKey(EccTestData.C2pnb163v1Key1Explicit, explicitCurve: true);
+        }
+
+        private static void VerifyPrivateKeyDerivesPublicKey(ECParameters knownKey, bool explicitCurve)
+        {
+            ECParameters importParams = new ECParameters
+            {
+                Curve = knownKey.Curve,
+                D = knownKey.D,
+                Q = default,
+            };
+
+            using (ECDsa ecdsa = ECDsaFactory.Create())
+            {
+                ecdsa.ImportParameters(importParams);
+
+                ECParameters exported = explicitCurve
+                    ? ecdsa.ExportExplicitParameters(true)
+                    : ecdsa.ExportParameters(true);
+
+                Assert.Equal(knownKey.Q.X, exported.Q.X);
+                Assert.Equal(knownKey.Q.Y, exported.Q.Y);
+            }
+        }
+
         [Theory]
         [MemberData(nameof(NamedCurves))]
         public static void OidPresentOnCurveMiscased(ECCurve curve)
diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs
index 11ea3645092058..a9c120cca8d47a 100644
--- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs
+++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Security.Cryptography.EcDiffieHellman.Tests;
+using System.Security.Cryptography.Tests;
 using Test.Cryptography;
 using Xunit;
 
@@ -9,6 +10,9 @@ namespace System.Security.Cryptography.EcDiffieHellman.OpenSsl.Tests
 {
     public class EcDiffieHellmanOpenSslTests : ECDiffieHellmanTests
     {
+        public static bool ECExplicitCurvesSupported => ECDiffieHellmanFactory.ExplicitCurvesSupported;
+        public static bool SupportsExplicitCurves => ECDiffieHellmanFactory.ExplicitCurvesSupported || ECDiffieHellmanFactory.ExplicitCurvesSupportFailOnUseOnly;
+
         [Fact]
         public void DefaultCtor()
         {
@@ -129,6 +133,26 @@ public void CtorHandleDuplicate()
             }
         }
 
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE, 256)]
+        [InlineData(ECDSA_P384_OID_VALUE, 384)]
+        [InlineData(ECDSA_P521_OID_VALUE, 521)]
+        public void CtorEvpPKeyHandle(string oid, int expectedKeySize)
+        {
+            int rc = Interop.Crypto.EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out int keySize);
+
+            Assert.Equal(1, rc);
+            Assert.False(pkey.IsInvalid);
+            Assert.Equal(expectedKeySize, keySize);
+
+            using (pkey)
+            using (ECDiffieHellmanOpenSsl e = new ECDiffieHellmanOpenSsl(pkey))
+            {
+                Assert.Equal(expectedKeySize, e.KeySize);
+                e.Exercise();
+            }
+        }
+
         [Fact]
         public void KeySizePropWithExercise()
         {
@@ -278,5 +302,221 @@ public void LookupCurveByOidFriendlyName()
             Assert.Equal("ECDSA_P521", param.Curve.Oid.FriendlyName); // OpenSsl maps secp521r1 to ECDSA_P521
             Assert.Equal(ECDSA_P521_OID_VALUE, param.Curve.Oid.Value);
         }
+
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE, 256)]
+        [InlineData(ECDSA_P384_OID_VALUE, 384)]
+        [InlineData(ECDSA_P521_OID_VALUE, 521)]
+        public void CtorEcKeyExportMatchesReimport(string oid, int expectedKeySize)
+        {
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey));
+
+                using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey))
+                {
+                    Assert.Equal(expectedKeySize, ecKeyBacked.KeySize);
+
+                    ECParameters privateParams = ecKeyBacked.ExportParameters(true);
+
+                    using (ECDiffieHellman evpBacked = ECDiffieHellman.Create(privateParams))
+                    {
+                        Assert.Equal(expectedKeySize, evpBacked.KeySize);
+
+                        ECParameters evpPrivateParams = evpBacked.ExportParameters(true);
+
+                        ComparePublicKey(privateParams.Q, evpPrivateParams.Q);
+                        ComparePrivateKey(privateParams, evpPrivateParams);
+                    }
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE)]
+        [InlineData(ECDSA_P384_OID_VALUE)]
+        [InlineData(ECDSA_P521_OID_VALUE)]
+        public void CtorEcKeyDeriveCrossCompatibleWithReimport(string oid)
+        {
+            IntPtr rawKey1 = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, rawKey1);
+            IntPtr rawKey2 = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, rawKey2);
+
+            try
+            {
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(rawKey1));
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(rawKey2));
+
+                using (ECDiffieHellmanOpenSsl ecKeyBacked1 = new ECDiffieHellmanOpenSsl(rawKey1))
+                using (ECDiffieHellmanOpenSsl ecKeyBacked2 = new ECDiffieHellmanOpenSsl(rawKey2))
+                using (ECDiffieHellman evpBacked1 = ECDiffieHellman.Create(ecKeyBacked1.ExportParameters(true)))
+                using (ECDiffieHellman evpBacked2 = ECDiffieHellman.Create(ecKeyBacked2.ExportParameters(true)))
+                using (ECDiffieHellmanPublicKey ecPub2 = ecKeyBacked2.PublicKey)
+                using (ECDiffieHellmanPublicKey evpPub2 = evpBacked2.PublicKey)
+                {
+                    byte[] ecEc = ecKeyBacked1.DeriveKeyFromHash(ecPub2, HashAlgorithmName.SHA256);
+                    byte[] ecEvp = ecKeyBacked1.DeriveKeyFromHash(evpPub2, HashAlgorithmName.SHA256);
+                    byte[] evpEc = evpBacked1.DeriveKeyFromHash(ecPub2, HashAlgorithmName.SHA256);
+                    byte[] evpEvp = evpBacked1.DeriveKeyFromHash(evpPub2, HashAlgorithmName.SHA256);
+
+                    Assert.Equal(ecEc, ecEvp);
+                    Assert.Equal(ecEc, evpEc);
+                    Assert.Equal(ecEc, evpEvp);
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(rawKey1);
+                Interop.Crypto.EcKeyDestroy(rawKey2);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyDeriveLeftAndRightSide()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                testData.Curve.Oid.Value!,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                testData.D, testData.D!.Length);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey))
+                using (ECDiffieHellman peer = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
+                using (ECDiffieHellmanPublicKey ecKeyPub = ecKeyBacked.PublicKey)
+                using (ECDiffieHellmanPublicKey peerPub = peer.PublicKey)
+                {
+                    byte[] derivedLeft = ecKeyBacked.DeriveKeyFromHash(peerPub, HashAlgorithmName.SHA256);
+                    byte[] derivedRight = peer.DeriveKeyFromHash(ecKeyPub, HashAlgorithmName.SHA256);
+
+                    Assert.Equal(derivedLeft, derivedRight);
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyPublicOnlyFailsDerive()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                testData.Curve.Oid.Value!,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                null, 0);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDiffieHellmanOpenSsl ecKeyBacked = new ECDiffieHellmanOpenSsl(ecKey))
+                using (ECDiffieHellman peer = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256))
+                using (ECDiffieHellmanPublicKey peerPub = peer.PublicKey)
+                {
+                    Assert.ThrowsAny<CryptographicException>(() =>
+                        ecKeyBacked.DeriveKeyFromHash(peerPub, HashAlgorithmName.SHA256));
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [ConditionalFact(nameof(SupportsExplicitCurves))]
+        public void ExplicitCurveImportExportProducesSameExplicitParams()
+        {
+            ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+            using (ECDiffieHellman original = ECDiffieHellman.Create(explicitCurve))
+            {
+                ECParameters explicitParams = original.ExportExplicitParameters(true);
+
+                using (ECDiffieHellman reimported = ECDiffieHellman.Create(explicitParams))
+                {
+                    ECParameters reimportedParams = reimported.ExportExplicitParameters(true);
+
+                    ComparePublicKey(explicitParams.Q, reimportedParams.Q);
+                    ComparePrivateKey(explicitParams, reimportedParams);
+                }
+            }
+        }
+
+        [ConditionalFact(nameof(ECExplicitCurvesSupported))]
+        public void ExplicitCurveImportAndOriginalDeriveCrossCompatible()
+        {
+            ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+            using (ECDiffieHellman key1 = ECDiffieHellman.Create(explicitCurve))
+            using (ECDiffieHellman key2 = ECDiffieHellman.Create(explicitCurve))
+            {
+                ECParameters key1Params = key1.ExportExplicitParameters(true);
+                ECParameters key2Params = key2.ExportExplicitParameters(true);
+
+                using (ECDiffieHellman key1Reimported = ECDiffieHellman.Create(key1Params))
+                using (ECDiffieHellman key2Reimported = ECDiffieHellman.Create(key2Params))
+                using (ECDiffieHellmanPublicKey pub2 = key2.PublicKey)
+                using (ECDiffieHellmanPublicKey pub2Reimported = key2Reimported.PublicKey)
+                {
+                    byte[] derive1 = key1.DeriveKeyFromHash(pub2, HashAlgorithmName.SHA256);
+                    byte[] derive2 = key1.DeriveKeyFromHash(pub2Reimported, HashAlgorithmName.SHA256);
+                    byte[] derive3 = key1Reimported.DeriveKeyFromHash(pub2, HashAlgorithmName.SHA256);
+                    byte[] derive4 = key1Reimported.DeriveKeyFromHash(pub2Reimported, HashAlgorithmName.SHA256);
+
+                    Assert.Equal(derive1, derive2);
+                    Assert.Equal(derive1, derive3);
+                    Assert.Equal(derive1, derive4);
+                }
+            }
+        }
+
+        [Fact]
+        public void GenerateKeyImplicitCurveThrows()
+        {
+            ECCurve implicitCurve = default;
+
+            using (ECDiffieHellman ecdh = new ECDiffieHellmanOpenSsl())
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => ecdh.GenerateKey(implicitCurve));
+            }
+        }
+
+        [Fact]
+        public void ImportParametersImplicitCurveThrows()
+        {
+            ECParameters parameters = new ECParameters
+            {
+                Curve = default,
+                D = new byte[32],
+            };
+
+            using (ECDiffieHellman ecdh = new ECDiffieHellmanOpenSsl())
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => ecdh.ImportParameters(parameters));
+            }
+        }
     }
 }
diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
index 6f1139a8630629..91eadd045a9f89 100644
--- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
+++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
@@ -3,7 +3,9 @@
 
 using System;
 using System.Runtime.InteropServices;
+using System.Security.Cryptography;
 using System.Security.Cryptography.EcDsa.Tests;
+using System.Security.Cryptography.Tests;
 using Test.Cryptography;
 using Xunit;
 
@@ -11,6 +13,8 @@ namespace System.Security.Cryptography.EcDsa.OpenSsl.Tests
 {
     public class EcDsaOpenSslTests : ECDsaTestsBase
     {
+        public static bool SupportsExplicitCurves => ECDsaFactory.ExplicitCurvesSupported || ECDsaFactory.ExplicitCurvesSupportFailOnUseOnly;
+
         [Fact]
         public void DefaultCtor()
         {
@@ -131,6 +135,26 @@ public void CtorHandleDuplicate()
             }
         }
 
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE, 256)]
+        [InlineData(ECDSA_P384_OID_VALUE, 384)]
+        [InlineData(ECDSA_P521_OID_VALUE, 521)]
+        public void CtorEvpPKeyHandle(string oid, int expectedKeySize)
+        {
+            int rc = Interop.Crypto.EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, oid, out int keySize);
+
+            Assert.Equal(1, rc);
+            Assert.False(pkey.IsInvalid);
+            Assert.Equal(expectedKeySize, keySize);
+
+            using (pkey)
+            using (ECDsaOpenSsl e = new ECDsaOpenSsl(pkey))
+            {
+                Assert.Equal(expectedKeySize, e.KeySize);
+                e.Exercise();
+            }
+        }
+
         [Fact]
         public void KeySizePropWithExercise()
         {
@@ -297,6 +321,447 @@ public void LookupCurveByOidFriendlyName()
             Assert.Equal("ECDSA_P521", param.Curve.Oid.FriendlyName); // OpenSsl maps secp521r1 to ECDSA_P521
             Assert.Equal(ECDSA_P521_OID_VALUE, param.Curve.Oid.Value);
         }
+
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE, 256)]
+        [InlineData(ECDSA_P384_OID_VALUE, 384)]
+        [InlineData(ECDSA_P521_OID_VALUE, 521)]
+        public void CtorEcKeyExportMatchesReimport(string oid, int expectedKeySize)
+        {
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey));
+
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.Equal(expectedKeySize, ecKeyBacked.KeySize);
+
+                    ECParameters privateParams = ecKeyBacked.ExportParameters(true);
+
+                    using (ECDsa evpBacked = ECDsa.Create(privateParams))
+                    {
+                        Assert.Equal(expectedKeySize, evpBacked.KeySize);
+
+                        ECParameters evpPrivateParams = evpBacked.ExportParameters(true);
+
+                        ComparePublicKey(privateParams.Q, evpPrivateParams.Q);
+                        ComparePrivateKey(privateParams, evpPrivateParams);
+                    }
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE)]
+        [InlineData(ECDSA_P384_OID_VALUE)]
+        [InlineData(ECDSA_P521_OID_VALUE)]
+        public void CtorEcKeySignVerifyCrossCompatible(string oid)
+        {
+            byte[] data = ByteUtils.RepeatByte(0x42, 64);
+
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey));
+
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                using (ECDsa evpBacked = ECDsa.Create(ecKeyBacked.ExportParameters(true)))
+                {
+                    byte[] sig1 = ecKeyBacked.SignData(data, HashAlgorithmName.SHA256);
+                    Assert.True(evpBacked.VerifyData(data, sig1, HashAlgorithmName.SHA256));
+
+                    byte[] sig2 = evpBacked.SignData(data, HashAlgorithmName.SHA256);
+                    Assert.True(ecKeyBacked.VerifyData(data, sig2, HashAlgorithmName.SHA256));
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        private const string ECDSA_WTLS7_OID_VALUE = "2.23.43.1.4.7";
+
+        private static bool IsWtls7Available => ECDsaFactory.IsCurveValid(new Oid(ECDSA_WTLS7_OID_VALUE));
+
+        [Theory]
+        [InlineData(ECDSA_P256_OID_VALUE, 256)]
+        [InlineData(ECDSA_P384_OID_VALUE, 384)]
+        [InlineData(ECDSA_P521_OID_VALUE, 521)]
+        public void CtorEcKeyNamedCurveExportIsCorrect(string oid, int expectedKeySize)
+        {
+            VerifyCtorEcKeyNamedCurveExport(oid, expectedKeySize);
+        }
+
+        [ConditionalFact(nameof(IsWtls7Available))]
+        public void CtorEcKeyFieldDegreeNotEqualOrderBits()
+        {
+            // wap-wsg-idm-ecid-wtls7 has field=160 bits but order=161 bits.
+            // Verify KeySize uses field degree (160), not EVP_PKEY_bits (161).
+            VerifyCtorEcKeyNamedCurveExport(ECDSA_WTLS7_OID_VALUE, 160);
+        }
+
+        private static void VerifyCtorEcKeyNamedCurveExport(string oid, int expectedKeySize)
+        {
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByOid(oid);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                Assert.NotEqual(0, Interop.Crypto.EcKeyGenerateKey(ecKey));
+
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.Equal(expectedKeySize, ecKeyBacked.KeySize);
+
+                    ECParameters exportedParams = ecKeyBacked.ExportParameters(true);
+
+                    Assert.True(exportedParams.Curve.IsNamed);
+                    Assert.Equal(oid, exportedParams.Curve.Oid.Value);
+                    Assert.NotNull(exportedParams.Q.X);
+                    Assert.NotNull(exportedParams.Q.Y);
+                    Assert.NotNull(exportedParams.D);
+                    exportedParams.Validate();
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyNamedCurveImportExportIsCorrect()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                testData.Curve.Oid.Value!,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                testData.D, testData.D!.Length);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.Equal(256, ecKeyBacked.KeySize);
+
+                    ECParameters exported = ecKeyBacked.ExportParameters(true);
+
+                    Assert.True(exported.Curve.IsNamed);
+                    Assert.Equal(testData.Curve.Oid.Value, exported.Curve.Oid.Value);
+
+                    ComparePublicKey(testData.Q, exported.Q);
+                    ComparePrivateKey(testData, exported);
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [ConditionalFact(nameof(ECExplicitCurvesSupported))]
+        public void CtorEcKeyExplicitPrimeCurveExportIsCorrect()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKeyExplicit();
+            ECCurve curve = testData.Curve;
+
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByExplicitParameters(
+                curve.CurveType,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                testData.D, testData.D?.Length ?? 0,
+                curve.Prime, curve.Prime!.Length,
+                curve.A, curve.A!.Length,
+                curve.B, curve.B!.Length,
+                curve.G.X, curve.G.X!.Length,
+                curve.G.Y, curve.G.Y!.Length,
+                curve.Order, curve.Order!.Length,
+                curve.Cofactor, curve.Cofactor!.Length,
+                curve.Seed, curve.Seed?.Length ?? 0);
+
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.Equal(256, ecKeyBacked.KeySize);
+
+                    ECParameters exported = ecKeyBacked.ExportExplicitParameters(true);
+
+                    Assert.True(exported.Curve.IsPrime);
+                    Assert.Equal(curve.Prime, exported.Curve.Prime);
+                    Assert.Equal(curve.A, exported.Curve.A);
+                    Assert.Equal(curve.B, exported.Curve.B);
+                    Assert.Equal(curve.G.X, exported.Curve.G.X);
+                    Assert.Equal(curve.G.Y, exported.Curve.G.Y);
+                    Assert.Equal(curve.Order, exported.Curve.Order);
+                    Assert.Equal(curve.Cofactor, exported.Curve.Cofactor);
+
+                    ComparePublicKey(testData.Q, exported.Q);
+                    ComparePrivateKey(testData, exported);
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [ConditionalFact(nameof(ECExplicitCurvesSupported))]
+        public void CtorEcKeyExplicitChar2CurveExportIsCorrect()
+        {
+            ECParameters testData = EccTestData.Sect163k1Key1Explicit;
+
+            if (!ECDsaFactory.IsCurveValid(EccTestData.Sect163k1Key1.Curve.Oid))
+            {
+                return;
+            }
+
+            ECCurve curve = testData.Curve;
+
+            IntPtr ecKey = Interop.Crypto.EcKeyCreateByExplicitParameters(
+                curve.CurveType,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                testData.D, testData.D?.Length ?? 0,
+                curve.Polynomial, curve.Polynomial!.Length,
+                curve.A, curve.A!.Length,
+                curve.B, curve.B!.Length,
+                curve.G.X, curve.G.X!.Length,
+                curve.G.Y, curve.G.Y!.Length,
+                curve.Order, curve.Order!.Length,
+                curve.Cofactor, curve.Cofactor!.Length,
+                null, 0);
+
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.Equal(163, ecKeyBacked.KeySize);
+
+                    ECParameters exported = ecKeyBacked.ExportExplicitParameters(true);
+
+                    Assert.True(exported.Curve.IsCharacteristic2);
+                    Assert.Equal(curve.Polynomial, exported.Curve.Polynomial);
+                    Assert.Equal(curve.A, exported.Curve.A);
+                    Assert.Equal(curve.B, exported.Curve.B);
+                    Assert.Equal(curve.G.X, exported.Curve.G.X);
+                    Assert.Equal(curve.G.Y, exported.Curve.G.Y);
+                    Assert.Equal(curve.Order, exported.Curve.Order);
+                    Assert.Equal(curve.Cofactor, exported.Curve.Cofactor);
+
+                    ComparePublicKey(testData.Q, exported.Q);
+                    ComparePrivateKey(testData, exported);
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyNamedCurveImportPublicKey()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+            byte[] data = ByteUtils.RepeatByte(0x42, 64);
+
+            byte[] signature;
+            using (ECDsa signer = ECDsa.Create(testData))
+            {
+                signature = signer.SignData(data, HashAlgorithmName.SHA256);
+            }
+
+            ECParameters publicOnly = new ECParameters
+            {
+                Curve = testData.Curve,
+                Q = testData.Q,
+            };
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                publicOnly.Curve.Oid.Value!,
+                publicOnly.Q.X, publicOnly.Q.X!.Length,
+                publicOnly.Q.Y, publicOnly.Q.Y!.Length,
+                null, 0);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    Assert.True(ecKeyBacked.VerifyData(data, signature, HashAlgorithmName.SHA256));
+
+                    byte[] wrongData = ByteUtils.RepeatByte(0x43, 64);
+                    Assert.False(ecKeyBacked.VerifyData(wrongData, signature, HashAlgorithmName.SHA256));
+
+                    Assert.ThrowsAny<CryptographicException>(() =>
+                        ecKeyBacked.SignData(data, HashAlgorithmName.SHA256));
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyNamedCurveImportPrivateKey()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+            byte[] data = ByteUtils.RepeatByte(0x42, 64);
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                testData.Curve.Oid.Value!,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                testData.D, testData.D!.Length);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    byte[] signature = ecKeyBacked.SignData(data, HashAlgorithmName.SHA256);
+
+                    using (ECDsa verifier = ECDsa.Create(testData))
+                    {
+                        Assert.True(verifier.VerifyData(data, signature, HashAlgorithmName.SHA256));
+                    }
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [Fact]
+        public void CtorEcKeyPublicOnlyExportPrivateThrows()
+        {
+            ECParameters testData = EccTestData.GetNistP256ReferenceKey();
+
+            IntPtr ecKey;
+            int rc = Interop.Crypto.EcKeyCreateByKeyParameters(
+                out ecKey,
+                testData.Curve.Oid.Value!,
+                testData.Q.X, testData.Q.X!.Length,
+                testData.Q.Y, testData.Q.Y!.Length,
+                null, 0);
+
+            Assert.Equal(1, rc);
+            Assert.NotEqual(IntPtr.Zero, ecKey);
+
+            try
+            {
+                using (ECDsaOpenSsl ecKeyBacked = new ECDsaOpenSsl(ecKey))
+                {
+                    ECParameters publicParams = ecKeyBacked.ExportParameters(false);
+                    Assert.NotNull(publicParams.Q.X);
+                    Assert.Null(publicParams.D);
+
+                    Assert.ThrowsAny<CryptographicException>(() =>
+                        ecKeyBacked.ExportParameters(true));
+                }
+            }
+            finally
+            {
+                Interop.Crypto.EcKeyDestroy(ecKey);
+            }
+        }
+
+        [ConditionalFact(nameof(SupportsExplicitCurves))]
+        public void ExplicitCurveImportExportProducesSameExplicitParams()
+        {
+            ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+            using (ECDsa original = ECDsa.Create(explicitCurve))
+            {
+                ECParameters explicitParams = original.ExportExplicitParameters(true);
+
+                using (ECDsa reimported = ECDsa.Create(explicitParams))
+                {
+                    ECParameters reimportedParams = reimported.ExportExplicitParameters(true);
+
+                    ComparePublicKey(explicitParams.Q, reimportedParams.Q);
+                    ComparePrivateKey(explicitParams, reimportedParams);
+                }
+            }
+        }
+
+        [ConditionalFact(nameof(ECExplicitCurvesSupported))]
+        public void ExplicitCurveImportAndOriginalSignVerifyCrossCompatible()
+        {
+            byte[] data = ByteUtils.RepeatByte(0x42, 64);
+            ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+            using (ECDsa key1 = ECDsa.Create(explicitCurve))
+            {
+                ECParameters explicitParams = key1.ExportExplicitParameters(true);
+
+                using (ECDsa key2 = ECDsa.Create(explicitParams))
+                {
+                    byte[] sig1 = key1.SignData(data, HashAlgorithmName.SHA256);
+                    Assert.True(key2.VerifyData(data, sig1, HashAlgorithmName.SHA256));
+
+                    byte[] sig2 = key2.SignData(data, HashAlgorithmName.SHA256);
+                    Assert.True(key1.VerifyData(data, sig2, HashAlgorithmName.SHA256));
+                }
+            }
+        }
+
+        [Fact]
+        public void GenerateKeyImplicitCurveThrows()
+        {
+            ECCurve implicitCurve = default;
+
+            using (ECDsa ecdsa = new ECDsaOpenSsl())
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => ecdsa.GenerateKey(implicitCurve));
+            }
+        }
+
+        [Fact]
+        public void ImportParametersImplicitCurveThrows()
+        {
+            ECParameters parameters = new ECParameters
+            {
+                Curve = default,
+                D = new byte[32],
+            };
+
+            using (ECDsa ecdsa = new ECDsaOpenSsl())
+            {
+                Assert.Throws<PlatformNotSupportedException>(() => ecdsa.ImportParameters(parameters));
+            }
+        }
     }
 }
 
@@ -307,6 +772,14 @@ internal static partial class Crypto
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByOid")]
         internal static extern IntPtr EcKeyCreateByOid(string oid);
 
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByKeyParameters")]
+        internal static extern int EcKeyCreateByKeyParameters(
+            out IntPtr key,
+            string oid,
+            byte[]? qx, int qxLength,
+            byte[]? qy, int qyLength,
+            byte[]? d, int dLength);
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyGenerateKey")]
         internal static extern int EcKeyGenerateKey(IntPtr ecKey);
 
@@ -315,5 +788,23 @@ internal static partial class Crypto
 
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_OpenSslVersionNumber")]
         internal static extern uint OpenSslVersionNumber();
+
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcCurveOid")]
+        internal static extern int EvpPKeyGenerateByEcCurveOid(out SafeEvpPKeyHandle pkey, string oid, out int keySize);
+
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcKeyCreateByExplicitParameters")]
+        internal static extern IntPtr EcKeyCreateByExplicitParameters(
+            ECCurve.ECCurveType curveType,
+            byte[]? qx, int qxLength,
+            byte[]? qy, int qyLength,
+            byte[]? d, int dLength,
+            byte[] p, int pLength,
+            byte[] a, int aLength,
+            byte[] b, int bLength,
+            byte[] gx, int gxLength,
+            byte[] gy, int gyLength,
+            byte[] order, int orderLength,
+            byte[] cofactor, int cofactorLength,
+            byte[]? seed, int seedLength);
     }
 }
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs
index 20f6d539f708fc..32e1b65bb2dd2a 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.cs
@@ -37,7 +37,15 @@ public ECDiffieHellmanOpenSsl(SafeEvpPKeyHandle pkeyHandle)
             }
 
             _key = new Lazy<SafeEvpPKeyHandle>(pkeyHandle.DuplicateHandle());
-            KeySizeValue = Interop.Crypto.EvpPKeyBits(_key.Value);
+
+            int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value);
+
+            if (keySize == 0)
+            {
+                throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+            }
+
+            KeySizeValue = keySize;
         }
 
         /// <summary>
@@ -68,7 +76,14 @@ public ECDiffieHellmanOpenSsl(IntPtr handle)
                 _key = new Lazy<SafeEvpPKeyHandle>(Interop.Crypto.CreateEvpPkeyFromEcKey(ecKeyHandle));
             }
 
-            KeySizeValue = Interop.Crypto.EvpPKeyBits(_key.Value);
+            int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value);
+
+            if (keySize == 0)
+            {
+                throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+            }
+
+            KeySizeValue = keySize;
         }
 
         /// <summary>
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs
index b8318f56d2ff5e..6719d28d228bd5 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ECDsaOpenSsl.cs
@@ -37,7 +37,15 @@ public ECDsaOpenSsl(SafeEvpPKeyHandle pkeyHandle)
             }
 
             _key = new Lazy<SafeEvpPKeyHandle>(pkeyHandle.DuplicateHandle());
-            ForceSetKeySize(Interop.Crypto.EvpPKeyBits(pkeyHandle));
+
+            int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(pkeyHandle);
+
+            if (keySize == 0)
+            {
+                throw new CryptographicException(SR.Cryptography_InvalidHandle);
+            }
+
+            ForceSetKeySize(keySize);
         }
 
         /// <summary>
@@ -68,7 +76,14 @@ public ECDsaOpenSsl(IntPtr handle)
                 _key = new Lazy<SafeEvpPKeyHandle>(Interop.Crypto.CreateEvpPkeyFromEcKey(ecKeyHandle));
             }
 
-            ForceSetKeySize(Interop.Crypto.EvpPKeyBits(_key.Value));
+            int keySize = Interop.Crypto.EvpPKeyGetEcKeySize(_key.Value);
+
+            if (keySize == 0)
+            {
+                throw new CryptographicException(SR.Cryptography_InvalidHandle);
+            }
+
+            ForceSetKeySize(keySize);
         }
 
         /// <summary>
diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c
index 59b0a6207f815b..55b9ea4bf95357 100644
--- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c
+++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c
@@ -190,6 +190,11 @@ static const Entry s_cryptoNative[] =
     DllImportEntry(CryptoNative_EvpPKeyGetEcKeyParameters)
     DllImportEntry(CryptoNative_EvpPKeyGetEcGroupNid)
     DllImportEntry(CryptoNative_EvpPKeyGetEcCurveParameters)
+    DllImportEntry(CryptoNative_EvpPKeyCreateByEcParameters)
+    DllImportEntry(CryptoNative_EvpPKeyCreateByEcExplicitParameters)
+    DllImportEntry(CryptoNative_EvpPKeyGenerateByEcCurveOid)
+    DllImportEntry(CryptoNative_EvpPKeyEcHasExplicitEncoding)
+    DllImportEntry(CryptoNative_EvpPKeyGetEcKeySize)
     DllImportEntry(CryptoNative_EvpRC2Cbc)
     DllImportEntry(CryptoNative_EvpRC2Ecb)
     DllImportEntry(CryptoNative_EvpSha1)
diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
index c07abd0cca1eb2..9b51a7044cea56 100644
--- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
@@ -54,6 +54,7 @@
 #include <openssl/provider.h>
 #include <openssl/store.h>
 #include <openssl/params.h>
+#include <openssl/param_build.h>
 #include <openssl/core_names.h>
 #include <openssl/kdf.h>
 #endif
@@ -195,6 +196,7 @@ void InitializeOpenSSLShim(void);
 #define HAVE_OPENSSL_EC2M 1
 const EC_METHOD* EC_GF2m_simple_method(void);
 int EC_GROUP_get_curve_GF2m(const EC_GROUP* group, BIGNUM* p, BIGNUM* a, BIGNUM* b, BN_CTX* ctx);
+EC_GROUP* EC_GROUP_new_curve_GF2m(const BIGNUM* p, const BIGNUM* a, const BIGNUM* b, BN_CTX* ctx);
 int EC_GROUP_set_curve_GF2m(EC_GROUP* group, const BIGNUM* p, const BIGNUM* a, const BIGNUM* b, BN_CTX* ctx);
 int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP* group, const EC_POINT* p, BIGNUM* x, BIGNUM* y, BN_CTX* ctx);
 int EC_POINT_set_affine_coordinates_GF2m(
@@ -384,6 +386,7 @@ extern bool g_libSslUses32BitTime;
     REQUIRED_FUNCTION(EC_GROUP_get0_seed) \
     REQUIRED_FUNCTION(EC_GROUP_get_cofactor) \
     REQUIRED_FUNCTION(EC_GROUP_get_curve_GFp) \
+    REQUIRED_FUNCTION(EC_GROUP_get_curve) \
     REQUIRED_FUNCTION(EC_GROUP_get_curve_name) \
     REQUIRED_FUNCTION(EC_GROUP_get_degree) \
     REQUIRED_FUNCTION(EC_GROUP_get_order) \
@@ -391,30 +394,35 @@ extern bool g_libSslUses32BitTime;
     LIGHTUP_FUNCTION(EC_GROUP_get_field_type) \
     REQUIRED_FUNCTION(EC_GROUP_method_of) \
     REQUIRED_FUNCTION(EC_GROUP_new) \
-    LIGHTUP_FUNCTION(EC_GROUP_new_by_curve_name) \
+    REQUIRED_FUNCTION(EC_GROUP_new_by_curve_name) \
+    REQUIRED_FUNCTION(EC_GROUP_new_curve_GFp) \
     REQUIRED_FUNCTION(EC_GROUP_set_curve_GFp) \
+    REQUIRED_FUNCTION(EC_GROUP_set_curve) \
     REQUIRED_FUNCTION(EC_GROUP_set_generator) \
     REQUIRED_FUNCTION(EC_GROUP_set_seed) \
-    REQUIRED_FUNCTION(EC_KEY_check_key) \
-    REQUIRED_FUNCTION(EC_KEY_free) \
-    REQUIRED_FUNCTION(EC_KEY_generate_key) \
-    REQUIRED_FUNCTION(EC_KEY_get0_group) \
-    REQUIRED_FUNCTION(EC_KEY_get0_private_key) \
-    REQUIRED_FUNCTION(EC_KEY_get0_public_key) \
-    REQUIRED_FUNCTION(EC_KEY_new) \
-    REQUIRED_FUNCTION(EC_KEY_new_by_curve_name) \
-    REQUIRED_FUNCTION(EC_KEY_set_group) \
-    REQUIRED_FUNCTION(EC_KEY_set_private_key) \
-    REQUIRED_FUNCTION(EC_KEY_set_public_key) \
-    REQUIRED_FUNCTION(EC_KEY_set_public_key_affine_coordinates) \
-    REQUIRED_FUNCTION(EC_KEY_up_ref) \
-    REQUIRED_FUNCTION(EC_METHOD_get_field_type) \
+    LIGHTUP_FUNCTION(EC_KEY_check_key) \
+    LIGHTUP_FUNCTION(EC_KEY_free) \
+    LIGHTUP_FUNCTION(EC_KEY_generate_key) \
+    LIGHTUP_FUNCTION(EC_KEY_get0_group) \
+    LIGHTUP_FUNCTION(EC_KEY_get0_private_key) \
+    LIGHTUP_FUNCTION(EC_KEY_get0_public_key) \
+    LIGHTUP_FUNCTION(EC_KEY_new) \
+    LIGHTUP_FUNCTION(EC_KEY_new_by_curve_name) \
+    LIGHTUP_FUNCTION(EC_KEY_set_group) \
+    LIGHTUP_FUNCTION(EC_KEY_set_private_key) \
+    LIGHTUP_FUNCTION(EC_KEY_set_public_key) \
+    LIGHTUP_FUNCTION(EC_KEY_set_public_key_affine_coordinates) \
+    LIGHTUP_FUNCTION(EC_KEY_up_ref) \
+    LIGHTUP_FUNCTION(EC_METHOD_get_field_type) \
     REQUIRED_FUNCTION(EC_POINT_free) \
     REQUIRED_FUNCTION(EC_POINT_get_affine_coordinates_GFp) \
+    REQUIRED_FUNCTION(EC_POINT_get_affine_coordinates) \
     REQUIRED_FUNCTION(EC_POINT_mul) \
     REQUIRED_FUNCTION(EC_POINT_new) \
+    REQUIRED_FUNCTION(EC_POINT_point2oct) \
     REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates_GFp) \
-    LIGHTUP_FUNCTION(EC_POINT_oct2point) \
+    REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates) \
+    REQUIRED_FUNCTION(EC_POINT_oct2point) \
     LIGHTUP_FUNCTION(ENGINE_by_id) \
     LIGHTUP_FUNCTION(ENGINE_finish) \
     LIGHTUP_FUNCTION(ENGINE_free) \
@@ -515,6 +523,7 @@ extern bool g_libSslUses32BitTime;
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
     LIGHTUP_FUNCTION(EVP_PKEY_CTX_new_from_name) \
     LIGHTUP_FUNCTION(EVP_PKEY_CTX_new_from_pkey) \
+    LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_group_name) \
     REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \
     REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \
     LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_params) \
@@ -543,8 +552,10 @@ extern bool g_libSslUses32BitTime;
     REQUIRED_FUNCTION(EVP_PKEY_get_raw_private_key) \
     REQUIRED_FUNCTION(EVP_PKEY_get_raw_public_key) \
     LIGHTUP_FUNCTION(EVP_PKEY_get0_RSA) \
+    LIGHTUP_FUNCTION(EVP_PKEY_get0_provider) \
     LIGHTUP_FUNCTION(EVP_PKEY_get0_type_name) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
+    LIGHTUP_FUNCTION(EVP_PKEY_generate) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
     LIGHTUP_FUNCTION(EVP_PKEY_is_a) \
     REQUIRED_FUNCTION(EVP_PKEY_keygen) \
@@ -644,6 +655,13 @@ extern bool g_libSslUses32BitTime;
     LIGHTUP_FUNCTION(OSSL_PARAM_construct_int) \
     LIGHTUP_FUNCTION(OSSL_PARAM_construct_int32) \
     LIGHTUP_FUNCTION(OSSL_PARAM_construct_end) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_new) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_free) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_utf8_string) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_octet_string) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_push_BN) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_BLD_to_param) \
+    LIGHTUP_FUNCTION(OSSL_PARAM_free) \
     REQUIRED_FUNCTION(PKCS8_PRIV_KEY_INFO_free) \
     REQUIRED_FUNCTION(PEM_read_bio_PKCS7) \
     REQUIRED_FUNCTION(PEM_read_bio_X509) \
@@ -837,6 +855,7 @@ extern bool g_libSslUses32BitTime;
     REQUIRED_FUNCTION(X509_VERIFY_PARAM_set_time) \
     LIGHTUP_FUNCTION(EC_GF2m_simple_method) \
     LIGHTUP_FUNCTION(EC_GROUP_get_curve_GF2m) \
+    LIGHTUP_FUNCTION(EC_GROUP_new_curve_GF2m) \
     LIGHTUP_FUNCTION(EC_GROUP_set_curve_GF2m) \
     LIGHTUP_FUNCTION(EC_POINT_get_affine_coordinates_GF2m) \
     LIGHTUP_FUNCTION(EC_POINT_set_affine_coordinates_GF2m) \
@@ -947,6 +966,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define EC_GROUP_get0_seed EC_GROUP_get0_seed_ptr
 #define EC_GROUP_get_cofactor EC_GROUP_get_cofactor_ptr
 #define EC_GROUP_get_curve_GFp EC_GROUP_get_curve_GFp_ptr
+#define EC_GROUP_get_curve EC_GROUP_get_curve_ptr
 #define EC_GROUP_get_curve_name EC_GROUP_get_curve_name_ptr
 #define EC_GROUP_get_degree EC_GROUP_get_degree_ptr
 #define EC_GROUP_get_order EC_GROUP_get_order_ptr
@@ -955,7 +975,9 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define EC_GROUP_method_of EC_GROUP_method_of_ptr
 #define EC_GROUP_new EC_GROUP_new_ptr
 #define EC_GROUP_new_by_curve_name EC_GROUP_new_by_curve_name_ptr
+#define EC_GROUP_new_curve_GFp EC_GROUP_new_curve_GFp_ptr
 #define EC_GROUP_set_curve_GFp EC_GROUP_set_curve_GFp_ptr
+#define EC_GROUP_set_curve EC_GROUP_set_curve_ptr
 #define EC_GROUP_set_generator EC_GROUP_set_generator_ptr
 #define EC_GROUP_set_seed EC_GROUP_set_seed_ptr
 #define EC_KEY_check_key EC_KEY_check_key_ptr
@@ -974,9 +996,12 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define EC_METHOD_get_field_type EC_METHOD_get_field_type_ptr
 #define EC_POINT_free EC_POINT_free_ptr
 #define EC_POINT_get_affine_coordinates_GFp EC_POINT_get_affine_coordinates_GFp_ptr
+#define EC_POINT_get_affine_coordinates EC_POINT_get_affine_coordinates_ptr
 #define EC_POINT_mul EC_POINT_mul_ptr
 #define EC_POINT_new EC_POINT_new_ptr
+#define EC_POINT_point2oct EC_POINT_point2oct_ptr
 #define EC_POINT_set_affine_coordinates_GFp EC_POINT_set_affine_coordinates_GFp_ptr
+#define EC_POINT_set_affine_coordinates EC_POINT_set_affine_coordinates_ptr
 #define EC_POINT_oct2point EC_POINT_oct2point_ptr
 #define ENGINE_by_id ENGINE_by_id_ptr
 #define ENGINE_finish ENGINE_finish_ptr
@@ -1076,6 +1101,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr
 #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
 #define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
+#define EVP_PKEY_CTX_set_group_name EVP_PKEY_CTX_set_group_name_ptr
 #define EVP_PKEY_CTX_set_params EVP_PKEY_CTX_set_params_ptr
 #define EVP_PKEY_CTX_set_rsa_keygen_bits EVP_PKEY_CTX_set_rsa_keygen_bits_ptr
 #define EVP_PKEY_CTX_set_rsa_oaep_md EVP_PKEY_CTX_set_rsa_oaep_md_ptr
@@ -1102,8 +1128,10 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define EVP_PKEY_get_raw_private_key EVP_PKEY_get_raw_private_key_ptr
 #define EVP_PKEY_get_raw_public_key EVP_PKEY_get_raw_public_key_ptr
 #define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr
+#define EVP_PKEY_get0_provider EVP_PKEY_get0_provider_ptr
 #define EVP_PKEY_get0_type_name EVP_PKEY_get0_type_name_ptr
 #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
+#define EVP_PKEY_generate EVP_PKEY_generate_ptr
 #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
 #define EVP_PKEY_is_a EVP_PKEY_is_a_ptr
 #define EVP_PKEY_keygen EVP_PKEY_keygen_ptr
@@ -1210,6 +1238,13 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define OSSL_PARAM_construct_int OSSL_PARAM_construct_int_ptr
 #define OSSL_PARAM_construct_int32 OSSL_PARAM_construct_int32_ptr
 #define OSSL_PARAM_construct_end OSSL_PARAM_construct_end_ptr
+#define OSSL_PARAM_BLD_new OSSL_PARAM_BLD_new_ptr
+#define OSSL_PARAM_BLD_free OSSL_PARAM_BLD_free_ptr
+#define OSSL_PARAM_BLD_push_utf8_string OSSL_PARAM_BLD_push_utf8_string_ptr
+#define OSSL_PARAM_BLD_push_octet_string OSSL_PARAM_BLD_push_octet_string_ptr
+#define OSSL_PARAM_BLD_push_BN OSSL_PARAM_BLD_push_BN_ptr
+#define OSSL_PARAM_BLD_to_param OSSL_PARAM_BLD_to_param_ptr
+#define OSSL_PARAM_free OSSL_PARAM_free_ptr
 #define PKCS8_PRIV_KEY_INFO_free PKCS8_PRIV_KEY_INFO_free_ptr
 #define PEM_read_bio_PKCS7 PEM_read_bio_PKCS7_ptr
 #define PEM_read_bio_X509 PEM_read_bio_X509_ptr
@@ -1405,6 +1440,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
 #define X509_VERIFY_PARAM_set_time X509_VERIFY_PARAM_set_time_ptr
 #define EC_GF2m_simple_method EC_GF2m_simple_method_ptr
 #define EC_GROUP_get_curve_GF2m EC_GROUP_get_curve_GF2m_ptr
+#define EC_GROUP_new_curve_GF2m EC_GROUP_new_curve_GF2m_ptr
 #define EC_GROUP_set_curve_GF2m EC_GROUP_set_curve_GF2m_ptr
 #define EC_POINT_get_affine_coordinates_GF2m EC_POINT_get_affine_coordinates_GF2m_ptr
 #define EC_POINT_set_affine_coordinates_GF2m EC_POINT_set_affine_coordinates_GF2m_ptr
diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
index ec8d0c43a6f314..6da9eb3c1b6a02 100644
--- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
+++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
@@ -25,17 +25,24 @@
 #define OSSL_MAC_PARAM_XOF    "xof"
 #define OSSL_MAC_PARAM_SIZE   "size"
 
-#define OSSL_PKEY_PARAM_GROUP_NAME   "group"
-#define OSSL_PKEY_PARAM_PRIV_KEY     "priv"
-#define OSSL_PKEY_PARAM_EC_PUB_X     "qx"
-#define OSSL_PKEY_PARAM_EC_PUB_Y     "qy"
-#define OSSL_PKEY_PARAM_EC_P         "p"
-#define OSSL_PKEY_PARAM_EC_A         "a"
-#define OSSL_PKEY_PARAM_EC_B         "b"
-#define OSSL_PKEY_PARAM_EC_GENERATOR "generator"
-#define OSSL_PKEY_PARAM_EC_ORDER     "order"
-#define OSSL_PKEY_PARAM_EC_COFACTOR  "cofactor"
-#define OSSL_PKEY_PARAM_EC_SEED      "seed"
+#define OSSL_PKEY_PARAM_GROUP_NAME    "group"
+#define OSSL_PKEY_PARAM_PRIV_KEY      "priv"
+#define OSSL_PKEY_PARAM_EC_PUB_X      "qx"
+#define OSSL_PKEY_PARAM_EC_PUB_Y      "qy"
+#define OSSL_PKEY_PARAM_EC_P          "p"
+#define OSSL_PKEY_PARAM_EC_A          "a"
+#define OSSL_PKEY_PARAM_EC_B          "b"
+#define OSSL_PKEY_PARAM_EC_GENERATOR  "generator"
+#define OSSL_PKEY_PARAM_EC_ORDER      "order"
+#define OSSL_PKEY_PARAM_EC_COFACTOR   "cofactor"
+#define OSSL_PKEY_PARAM_EC_SEED       "seed"
+#define OSSL_PKEY_PARAM_EC_ENCODING   "encoding"
+#define OSSL_PKEY_PARAM_EC_FIELD_TYPE "field-type"
+
+#define OSSL_PKEY_EC_ENCODING_EXPLICIT "explicit"
+#define OSSL_PKEY_EC_ENCODING_GROUP    "named_curve"
+
+#define EVP_PKEY_KEY_PARAMETERS 4
 
 #define OSSL_PKEY_PARAM_RSA_N            "n"
 #define OSSL_PKEY_PARAM_RSA_E            "e"
@@ -52,6 +59,7 @@
 
 typedef struct ossl_lib_ctx_st OSSL_LIB_CTX;
 typedef struct ossl_param_st OSSL_PARAM;
+typedef struct ossl_param_bld_st OSSL_PARAM_BLD;
 typedef struct ossl_provider_st OSSL_PROVIDER;
 typedef struct ossl_store_ctx_st OSSL_STORE_CTX;
 typedef struct ossl_store_info_st OSSL_STORE_INFO;
@@ -98,6 +106,7 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_name(OSSL_LIB_CTX *libctx, const char *name,
 EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_pkey(OSSL_LIB_CTX *libctx, EVP_PKEY *pkey, const char *propquery);
 int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, const OSSL_PARAM *params);
 int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
+int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name);
 int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
 int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX* ctx, int pad_mode);
 int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX* ctx, int saltlen);
@@ -119,6 +128,7 @@ int EVP_PKEY_fromdata(EVP_PKEY_CTX *ctx,
                       EVP_PKEY **ppkey,
                       int selection,
                       OSSL_PARAM params[]);
+int EVP_PKEY_generate(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
 int EVP_PKEY_get_base_id(const EVP_PKEY* pkey);
 int EVP_PKEY_get_bits(const EVP_PKEY* pkey);
 int EVP_PKEY_get_bn_param(const EVP_PKEY *pkey, const char *key_name, BIGNUM **bn);
@@ -135,11 +145,20 @@ OSSL_PARAM OSSL_PARAM_construct_int32(const char *key, int32_t *buf);
 OSSL_PARAM OSSL_PARAM_construct_octet_string(const char *key, void *buf, size_t bsize);
 OSSL_PARAM OSSL_PARAM_construct_utf8_string(const char *key, char *buf, size_t bsize);
 
+OSSL_PARAM_BLD *OSSL_PARAM_BLD_new(void);
+void OSSL_PARAM_BLD_free(OSSL_PARAM_BLD *bld);
+int OSSL_PARAM_BLD_push_utf8_string(OSSL_PARAM_BLD *bld, const char *key, const char *buf, size_t bsize);
+int OSSL_PARAM_BLD_push_octet_string(OSSL_PARAM_BLD *bld, const char *key, const void *buf, size_t bsize);
+int OSSL_PARAM_BLD_push_BN(OSSL_PARAM_BLD *bld, const char *key, const BIGNUM *bn);
+OSSL_PARAM *OSSL_PARAM_BLD_to_param(OSSL_PARAM_BLD *bld);
+void OSSL_PARAM_free(OSSL_PARAM *params);
+
 void OSSL_LIB_CTX_free(OSSL_LIB_CTX*);
 OSSL_LIB_CTX* OSSL_LIB_CTX_new(void);
 OSSL_PROVIDER* OSSL_PROVIDER_load(OSSL_LIB_CTX*, const char* name);
 OSSL_PROVIDER* OSSL_PROVIDER_try_load(OSSL_LIB_CTX*, const char* name, int retain_fallbacks);
 int OSSL_PROVIDER_unload(OSSL_PROVIDER* prov);
+const OSSL_PROVIDER* EVP_PKEY_get0_provider(const EVP_PKEY* pkey);
 int OSSL_STORE_close(OSSL_STORE_CTX* ctx);
 int OSSL_STORE_eof(OSSL_STORE_CTX* ctx);
 OSSL_STORE_INFO* OSSL_STORE_load(OSSL_STORE_CTX* ctx);
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c
index 2dc23d98036760..268d6f3e7f46d2 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.c
@@ -2,8 +2,121 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #include "pal_ecc_import_export.h"
+#include "pal_eckey.h"
 #include "pal_utilities.h"
 
+#ifdef NEED_OPENSSL_3_0
+
+// Serialize an EC_POINT to uncompressed octet format.
+// Returns an OPENSSL_zalloc'd buffer (caller must OPENSSL_free), or NULL on failure.
+static uint8_t* EncodeEcPointFromPoint(const EC_GROUP* group, const EC_POINT* point, size_t* outLength)
+{
+    size_t len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
+    if (len == 0)
+        return NULL;
+
+    uint8_t* buf = (uint8_t*)OPENSSL_zalloc(len);
+    if (buf == NULL)
+        return NULL;
+
+    if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, buf, len, NULL) != len)
+    {
+        OPENSSL_free(buf);
+        return NULL;
+    }
+
+    *outLength = len;
+    return buf;
+}
+
+// Encode two coordinate byte arrays as an uncompressed EC point via EC_POINT.
+// Uses EC_POINT_set_affine_coordinates + EC_POINT_point2oct so OpenSSL handles
+// field-size-aware serialization (tolerates leading-zero-padded inputs).
+// Returns an OPENSSL_zalloc'd buffer (caller must OPENSSL_free), or NULL on failure.
+static uint8_t* EncodeEcPointFromCoordinates(
+    const uint8_t* x, int32_t xLength,
+    const uint8_t* y, int32_t yLength,
+    const EC_GROUP* group,
+    size_t* outLength)
+{
+    uint8_t* buf = NULL;
+    BIGNUM* xBn = BN_bin2bn(x, xLength, NULL);
+    BIGNUM* yBn = BN_bin2bn(y, yLength, NULL);
+    EC_POINT* point = NULL;
+
+    if (!xBn || !yBn)
+        goto done;
+
+    point = EC_POINT_new(group);
+    if (!point)
+        goto done;
+
+    if (!EC_POINT_set_affine_coordinates(group, point, xBn, yBn, NULL))
+    {
+        goto done;
+    }
+
+    buf = EncodeEcPointFromPoint(group, point, outLength);
+
+done:
+    if (point) EC_POINT_free(point);
+    if (xBn) BN_free(xBn);
+    if (yBn) BN_free(yBn);
+    return buf;
+}
+
+// Encodes the public key (from coordinates or derived from private key) and pushes
+// it to the OSSL_PARAM_BLD. Returns 1 on success, 0 on failure.
+// If a public-key buffer is allocated (either on success or if the push fails),
+// *outPubKeyBuf is assigned and the caller must OPENSSL_free it.
+static int PushPublicKeyParam(
+    OSSL_PARAM_BLD* bld,
+    const EC_GROUP* group,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const BIGNUM* dBn,
+    uint8_t** outPubKeyBuf)
+{
+    int hasPublicKey = (qx != NULL && qy != NULL);
+    int hasPrivateKey = (dBn != NULL);
+
+    if (!hasPublicKey && !hasPrivateKey)
+        return 1; // Nothing to push
+
+    uint8_t* pubKeyBuf = NULL;
+    size_t pubKeyLen = 0;
+
+    if (hasPublicKey)
+    {
+        pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, group, &pubKeyLen);
+    }
+    else
+    {
+        // Derive Q = d * G
+        EC_POINT* pubPoint = EC_POINT_new(group);
+        if (pubPoint == NULL ||
+            !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL))
+        {
+            EC_POINT_free(pubPoint);
+            return 0;
+        }
+
+        pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen);
+        EC_POINT_free(pubPoint);
+    }
+
+    if (pubKeyBuf == NULL)
+        return 0;
+
+    *outPubKeyBuf = pubKeyBuf;
+
+    if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen))
+        return 0;
+
+    return 1;
+}
+#endif
+
 static ECCurveType MethodToCurveType(const EC_METHOD* method)
 {
     if (method == EC_GFp_mont_method())
@@ -49,26 +162,9 @@ static ECCurveType EcKeyGetCurveType(
     return MethodToCurveType(method);
 }
 
-static int EcPointGetAffineCoordinates(const EC_GROUP *group, ECCurveType curveType, const EC_POINT *p, BIGNUM *x, BIGNUM *y)
+static int EcPointGetAffineCoordinates(const EC_GROUP *group, const EC_POINT *p, BIGNUM *x, BIGNUM *y)
 {
-#if HAVE_OPENSSL_EC2M
-    if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (curveType == Characteristic2))
-    {
-        if (!EC_POINT_get_affine_coordinates_GF2m(group, p, x, y, NULL))
-            return 0;
-    }
-    else
-#endif
-    {
-        if (!EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL))
-            return 0;
-    }
-
-#if !HAVE_OPENSSL_EC2M
-    (void)curveType;
-#endif
-
-    return 1;
+    return EC_POINT_get_affine_coordinates(group, p, x, y, NULL) ? 1 : 0;
 }
 
 int32_t CryptoNative_GetECKeyParameters(
@@ -104,7 +200,7 @@ int32_t CryptoNative_GetECKeyParameters(
     if (!xBn || !yBn)
         goto error;
 
-    if (!EcPointGetAffineCoordinates(group, curveType, Q, xBn, yBn))
+    if (!EcPointGetAffineCoordinates(group, Q, xBn, yBn))
         goto error;
 
     // Success; assign variables
@@ -225,35 +321,13 @@ int32_t CryptoNative_GetECCurveParameters(
         goto error;
 
     // Extract p, a, b
-#if HAVE_OPENSSL_EC2M
-    if (API_EXISTS(EC_GROUP_get_curve_GF2m) && (*curveType == Characteristic2))
-    {
-        // pBn represents the binary polynomial
-        if (!EC_GROUP_get_curve_GF2m(group, pBn, aBn, bBn, NULL))
-            goto error;
-    }
-    else
-#endif
-    {
-        // pBn represents the prime
-        if (!EC_GROUP_get_curve_GFp(group, pBn, aBn, bBn, NULL))
-            goto error;
-    }
+    if (!EC_GROUP_get_curve(group, pBn, aBn, bBn, NULL))
+        goto error;
 
     // Extract gx and gy
     G = EC_GROUP_get0_generator(group);
-#if HAVE_OPENSSL_EC2M
-    if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (*curveType == Characteristic2))
-    {
-        if (!EC_POINT_get_affine_coordinates_GF2m(group, G, xBn, yBn, NULL))
-            goto error;
-    }
-    else
-#endif
-    {
-        if (!EC_POINT_get_affine_coordinates_GFp(group, G, xBn, yBn, NULL))
-            goto error;
-    }
+    if (!EcPointGetAffineCoordinates(group, G, xBn, yBn))
+        goto error;
 
     // Extract order (n)
     if (!EC_GROUP_get_order(group, orderBn, NULL))
@@ -322,7 +396,7 @@ int32_t CryptoNative_GetECCurveParameters(
     return rc;
 }
 
-int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, uint8_t* qx, int32_t qxLength, uint8_t* qy, int32_t qyLength, uint8_t* d, int32_t dLength)
+int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, const uint8_t* qx, int32_t qxLength, const uint8_t* qy, int32_t qyLength, const uint8_t* d, int32_t dLength)
 {
     if (!key || !oid)
     {
@@ -336,7 +410,7 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u
 
     // oid can be friendly name or value
     int nid = OBJ_txt2nid(oid);
-    if (!nid)
+    if (nid == NID_undef)
         return -1;
 
     EC_KEY* tmpKey = EC_KEY_new_by_curve_name(nid);
@@ -424,7 +498,7 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u
     return ret;
 }
 
-int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName)
+int32_t CryptoNative_EvpPKeyGetEcGroupNid(EVP_PKEY *pkey, int32_t* nidName)
 {
     if (!nidName)
         return 0;
@@ -434,28 +508,150 @@ int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName
     if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
         return 0;
 
+#ifdef NEED_OPENSSL_3_0
 #ifdef FEATURE_DISTRO_AGNOSTIC_SSL
-    if (!API_EXISTS(EVP_PKEY_get_utf8_string_param))
+    if (API_EXISTS(EVP_PKEY_get_utf8_string_param))
+#endif
     {
+        // Retrieve the textual name of the EC group (e.g., "prime256v1")
+        // In all known cases this should be exactly 10 characters + 1 null byte but leaving some room in case it changes in the future
+        // versions of OpenSSL. This length also matches with what OpenSSL uses in their demo code:
+        // https://github.com/openssl/openssl/blob/ac80e1e15dcd13c61392a706170c427250c7bb69/demos/pkey/EVP_PKEY_EC_keygen.c#L88
+        char curveName[80] = {0};
+
+        if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL))
+            return 0;
+
+        *nidName = OBJ_txt2nid(curveName);
+        return 1;
+    }
+#endif
+
+#ifdef NEED_OPENSSL_1_1
+    EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey);
+    if (!ecKey)
         return 0;
+
+    const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+    if (group)
+    {
+        *nidName = EC_GROUP_get_curve_name(group);
     }
+
+    EC_KEY_free(ecKey);
+    return (*nidName != NID_undef) ? 1 : 0;
+#endif
+
+#if !defined(NEED_OPENSSL_1_1) && !defined(NEED_OPENSSL_3_0)
+    return 0;
 #endif
+}
+
+int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(EVP_PKEY* pkey)
+{
+    if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
+        return -1;
 
 #ifdef NEED_OPENSSL_3_0
-    // Retrieve the textual name of the EC group (e.g., "prime256v1")
-    // In all known cases this should be exactly 10 characters + 1 null byte but leaving some room in case it changes in the future
-    // versions of OpenSSL. This length also matches with what OpenSSL uses in their demo code:
-    // https://github.com/openssl/openssl/blob/ac80e1e15dcd13c61392a706170c427250c7bb69/demos/pkey/EVP_PKEY_EC_keygen.c#L88
-    char curveName[80] = {0};
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (API_EXISTS(EVP_PKEY_get_utf8_string_param))
+#endif
+    {
+        char encoding[32] = {0};
+        if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_ENCODING, encoding, sizeof(encoding), NULL))
+            return 0;
+        
+        return (strcmp(encoding, OSSL_PKEY_EC_ENCODING_EXPLICIT) == 0) ? 1 : 0;
+    }
+#endif
+
+#ifdef NEED_OPENSSL_1_1
+    EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey);
+    if (!ecKey)
+        return -1;
+
+    const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+    int nid = group ? EC_GROUP_get_curve_name(group) : NID_undef;
+    EC_KEY_free(ecKey);
+
+    return (nid == NID_undef) ? 1 : 0;
+#endif
+
+#if !defined(NEED_OPENSSL_1_1) && !defined(NEED_OPENSSL_3_0)
+    return -1;
+#endif
+}
 
-    if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL))
+// Returns the EC field degree (bits in the underlying field) for the specified EVP_PKEY.
+// This is the value used as KeySize for EC keys in .NET, matching Windows CNG's
+// BCRYPT_PUBLIC_KEY_LENGTH semantics for EC keys (field modulus bit-size, e.g. 256
+// for P-256). For some uncommon curves this differs from the subgroup order's bit length
+// (e.g. wap-wsg-idm-ecid-wtls7 has field=160, order=161).
+// Works for both provider-backed and legacy EC_KEY-backed EVP_PKEYs.
+int32_t CryptoNative_EvpPKeyGetEcKeySize(EVP_PKEY* pkey)
+{
+    if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
         return 0;
 
-    *nidName = OBJ_txt2nid(curveName);
-    return 1;
+#ifdef NEED_OPENSSL_3_0
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (API_EXISTS(EVP_PKEY_get_bn_param) &&
+        API_EXISTS(EVP_PKEY_get_utf8_string_param) &&
+        API_EXISTS(EVP_PKEY_get0_provider) &&
+        EVP_PKEY_get0_provider(pkey))
 #else
-    return 0;
+    if (EVP_PKEY_get0_provider(pkey))
+#endif
+    {
+        // Provider-backed key: use OSSL 3.0 params API.
+        // Some providers (e.g. TPM2) don't expose OSSL_PKEY_PARAM_EC_FIELD_TYPE,
+        // so try EC_GROUP from the curve name first, then fall back to the param.
+
+        // For named curves, EC_GROUP_get_degree returns the field degree directly.
+        int nid = 0;
+        if (CryptoNative_EvpPKeyGetEcGroupNid(pkey, &nid) && nid != NID_undef)
+        {
+            EC_GROUP* group = EC_GROUP_new_by_curve_name(nid);
+            if (group)
+            {
+                int degree = EC_GROUP_get_degree(group);
+                EC_GROUP_free(group);
+                return degree;
+            }
+        }
+
+        // Fallback for explicit curves: fetch p and compute degree manually.
+        BIGNUM* p = NULL;
+        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &p) || !p)
+            return 0;
+
+        // For GF(2^m): p is the irreducible polynomial, degree = BN_num_bits(p) - 1.
+        // For GF(p): degree = BN_num_bits(p).
+        int degree = BN_num_bits(p);
+
+        char fieldType[32] = {0};
+        if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, sizeof(fieldType), NULL))
+        {
+            if (strcmp(fieldType, SN_X9_62_characteristic_two_field) == 0)
+                degree = degree > 0 ? degree - 1 : 0;
+        }
+
+        BN_free(p);
+        return degree;
+    }
 #endif
+
+    // Legacy or OSSL 1.1 path: get degree from the EC_KEY's group directly.
+    EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey);
+    if (ecKey)
+    {
+        const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+        int degree = group ? EC_GROUP_get_degree(group) : 0;
+        EC_KEY_free(ecKey);
+        return degree;
+    }
+
+    return 0;
 }
 
 int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
@@ -473,7 +669,9 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
     assert(cbD != NULL);
 
 #ifdef FEATURE_DISTRO_AGNOSTIC_SSL
-    if (!API_EXISTS(EVP_PKEY_get_bn_param))
+    if (!API_EXISTS(EVP_PKEY_get_bn_param) ||
+        !API_EXISTS(EVP_PKEY_get_octet_string_param) ||
+        !API_EXISTS(EVP_PKEY_get_utf8_string_param))
     {
         *cbQx = *cbQy = 0;
         *qx = *qy = 0;
@@ -484,34 +682,114 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
 #endif
 
     int rc = 0;
+
+#ifdef NEED_OPENSSL_3_0
     BIGNUM *xBn = NULL;
     BIGNUM *yBn = NULL;
     BIGNUM *dBn = NULL;
+    BIGNUM *ecP = NULL;
+    BIGNUM *ecA = NULL;
+    BIGNUM *ecB = NULL;
+    uint8_t* pubKeyBuf = NULL;
+    size_t pubKeyLen = 0;
+    EC_GROUP* group = NULL;
+    EC_POINT* point = NULL;
+    char curveName[80] = {0};
 
-#ifdef NEED_OPENSSL_3_0
     // Ensure we have an EC key
     if (EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
         goto error;
 
     ERR_clear_error();
 
-    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &xBn))
+    // Get the public key as an encoded point (may be compressed or uncompressed).
+    // We use OSSL_PKEY_PARAM_PUB_KEY instead of OSSL_PKEY_PARAM_EC_PUB_X/Y
+    // because the individual X/Y components may not be materialized yet.
+    if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &pubKeyLen))
         goto error;
-    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &yBn))
+
+    pubKeyBuf = (uint8_t*)OPENSSL_zalloc(pubKeyLen);
+    if (pubKeyBuf == NULL)
         goto error;
 
-    *qx = xBn;
-    *cbQx = BN_num_bytes(xBn);
-    *qy = yBn;
-    *cbQy = BN_num_bytes(yBn);
+    if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen, &pubKeyLen))
+        goto error;
 
-    if (includePrivate)
+    // Decode the encoded point (compressed or uncompressed) to extract X and Y.
+    // Build an EC_GROUP from the key's parameters to perform the decoding.
+
+    // Try named curve first.
+    if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curveName, sizeof(curveName), NULL))
+    {
+        int nid = OBJ_txt2nid(curveName);
+        if (nid != NID_undef)
+        {
+            group = EC_GROUP_new_by_curve_name(nid);
+        }
+    }
+
+    if (group == NULL)
     {
-        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &dBn))
+        // Explicit curve — build EC_GROUP from the key's field params.
+        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &ecP) ||
+            !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_A, &ecA) ||
+            !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_B, &ecB))
+        {
+            goto error;
+        }
+
+        char fieldType[64] = {0};
+        int isChar2 = 0;
+
+        if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, sizeof(fieldType), NULL))
+        {
+            isChar2 = (strcmp(fieldType, SN_X9_62_characteristic_two_field) == 0);
+        }
+
+#if HAVE_OPENSSL_EC2M
+        if (isChar2 && API_EXISTS(EC_GROUP_new_curve_GF2m))
+        {
+            group = EC_GROUP_new_curve_GF2m(ecP, ecA, ecB, NULL);
+        }
+        else
+#endif
+        if (!isChar2)
+        {
+            group = EC_GROUP_new_curve_GFp(ecP, ecA, ecB, NULL);
+        }
+
+        if (group == NULL)
             goto error;
+    }
+
+    point = EC_POINT_new(group);
+    if (point == NULL ||
+        !EC_POINT_oct2point(group, point, pubKeyBuf, pubKeyLen, NULL))
+    {
+        goto error;
+    }
+
+    xBn = BN_new();
+    yBn = BN_new();
+
+    if (xBn == NULL || yBn == NULL)
+        goto error;
+
+    if (!EcPointGetAffineCoordinates(group, point, xBn, yBn))
+        goto error;
 
+    if (includePrivate && !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &dBn))
+    {
+        rc = -1;
+        goto error;
+    }
+
+    // Success; assign variables
+    if (includePrivate)
+    {
         *d = dBn;
-        *cbD = BN_num_bytes(dBn);
+        dBn = NULL;
+        *cbD = BN_num_bytes(*d);
     }
     else
     {
@@ -519,36 +797,57 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
         *cbD = 0;
     }
 
-    // success
-    return 1;
+    *qx = xBn;
+    xBn = NULL;
+    *cbQx = BN_num_bytes(*qx);
+    *qy = yBn;
+    yBn = NULL;
+    *cbQy = BN_num_bytes(*qy);
+
+    rc = 1;
+    goto exit;
 
 error:
-#else
-    (void)pkey;
-    (void)includePrivate;
-#endif
     *cbQx = *cbQy = 0;
     *qx = *qy = 0;
     if (d) *d = NULL;
     if (cbD) *cbD = 0;
+
+exit:
     if (xBn) BN_free(xBn);
     if (yBn) BN_free(yBn);
+    if (dBn) BN_clear_free(dBn);
+    if (pubKeyBuf) OPENSSL_free(pubKeyBuf);
+    if (point) EC_POINT_free(point);
+    if (group) EC_GROUP_free(group);
+    if (ecP) BN_free(ecP);
+    if (ecA) BN_free(ecA);
+    if (ecB) BN_free(ecB);
     return rc;
+#else
+    (void)pkey;
+    (void)includePrivate;
+    *cbQx = *cbQy = 0;
+    *qx = *qy = 0;
+    if (d) *d = NULL;
+    if (cbD) *cbD = 0;
+    return 0;
+#endif
 }
 
 EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters(
     ECCurveType curveType,
-    uint8_t* qx, int32_t qxLength,
-    uint8_t* qy, int32_t qyLength,
-    uint8_t* d,  int32_t dLength,
-    uint8_t* p,  int32_t pLength,
-    uint8_t* a,  int32_t aLength,
-    uint8_t* b,  int32_t bLength,
-    uint8_t* gx, int32_t gxLength,
-    uint8_t* gy, int32_t gyLength,
-    uint8_t* order,  int32_t orderLength,
-    uint8_t* cofactor,  int32_t cofactorLength,
-    uint8_t* seed,  int32_t seedLength)
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d,  int32_t dLength,
+    const uint8_t* p,  int32_t pLength,
+    const uint8_t* a,  int32_t aLength,
+    const uint8_t* b,  int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order,  int32_t orderLength,
+    const uint8_t* cofactor,  int32_t cofactorLength,
+    const uint8_t* seed,  int32_t seedLength)
 {
     if (!p || !a || !b || !gx || !gy || !order || !cofactor)
     {
@@ -588,38 +887,20 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters(
     aBn = BN_bin2bn(a, aLength, NULL);
     bBn = BN_bin2bn(b, bLength, NULL);
 
-#if HAVE_OPENSSL_EC2M
-    if (API_EXISTS(EC_GROUP_set_curve_GF2m) && (curveType == Characteristic2))
-    {
-        if (!EC_GROUP_set_curve_GF2m(group, pBn, aBn, bBn, NULL))
-            goto error;
-    }
-    else
-#endif
-    {
-        if (!EC_GROUP_set_curve_GFp(group, pBn, aBn, bBn, NULL))
-            goto error;
-    }
+    if (!EC_GROUP_set_curve(group, pBn, aBn, bBn, NULL))
+        goto error;
 
     // Set generator, order and cofactor
     G = EC_POINT_new(group);
     gxBn = BN_bin2bn(gx, gxLength, NULL);
     gyBn = BN_bin2bn(gy, gyLength, NULL);
 
-#if HAVE_OPENSSL_EC2M
-    if (API_EXISTS(EC_POINT_set_affine_coordinates_GF2m) && (curveType == Characteristic2))
-    {
-        EC_POINT_set_affine_coordinates_GF2m(group, G, gxBn, gyBn, NULL);
-    }
-    else
-#endif
-    {
-        EC_POINT_set_affine_coordinates_GFp(group, G, gxBn, gyBn, NULL);
-    }
+    EC_POINT_set_affine_coordinates(group, G, gxBn, gyBn, NULL);
 
     orderBn = BN_bin2bn(order, orderLength, NULL);
     cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL);
-    EC_GROUP_set_generator(group, G, orderBn, cofactorBn);
+    if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn))
+        goto error;
 
     // Set seed (optional)
     if (seed && seedLength > 0)
@@ -728,8 +1009,12 @@ static ECCurveType NIDToCurveType(int fieldType)
     return Unspecified;
 }
 
-int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
-    const EVP_PKEY* pkey,
+#ifdef NEED_OPENSSL_3_0
+// Extracts EC curve parameters from a legacy EC_KEY-backed EVP_PKEY by getting
+// the EC_KEY and delegating to CryptoNative_GetECCurveParameters.
+// On success, the caller owns the returned BIGNUMs and must free them.
+static int32_t EvpPKeyGetEcCurveParameters_FromEcKey(
+    EVP_PKEY* pkey,
     int32_t includePrivate,
     ECCurveType* curveType,
     BIGNUM** qx, int32_t* cbQx,
@@ -744,37 +1029,139 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
     BIGNUM** cofactor, int32_t* cbCofactor,
     BIGNUM** seed, int32_t* cbSeed)
 {
-    assert(p != NULL);
-    assert(cbP != NULL);
-    assert(a != NULL);
-    assert(cbA != NULL);
-    assert(b != NULL);
-    assert(cbB != NULL);
-    assert(gx != NULL);
-    assert(cbGx != NULL);
-    assert(gy != NULL);
-    assert(cbGy != NULL);
-    assert(order != NULL);
-    assert(cbOrder != NULL);
-    assert(cofactor != NULL);
-    assert(cbCofactor != NULL);
-    assert(seed != NULL);
-    assert(cbSeed != NULL);
+    EC_KEY* ecKey = EVP_PKEY_get1_EC_KEY(pkey);
+    if (!ecKey)
+        return 0;
 
-#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
-    if (!API_EXISTS(EC_GROUP_new_by_curve_name) ||
-        !API_EXISTS(EC_GROUP_get_field_type) ||
-        !API_EXISTS(EVP_PKEY_get_octet_string_param) ||
-        !API_EXISTS(EC_POINT_oct2point))
+    // CryptoNative_GetECCurveParameters returns freshly-allocated BIGNUMs for everything
+    // except d, which is a borrowed reference into the EC_KEY (EC_KEY_get0_private_key).
+    const BIGNUM* qxOwned = NULL;
+    const BIGNUM* qyOwned = NULL;
+    const BIGNUM* dBorrowed = NULL;
+    const BIGNUM* pOwned = NULL;
+    const BIGNUM* aOwned = NULL;
+    const BIGNUM* bOwned = NULL;
+    const BIGNUM* gxOwned = NULL;
+    const BIGNUM* gyOwned = NULL;
+    const BIGNUM* orderOwned = NULL;
+    const BIGNUM* cofactorOwned = NULL;
+    const BIGNUM* seedOwned = NULL;
+
+    int32_t rc = CryptoNative_GetECCurveParameters(
+        ecKey, includePrivate, curveType,
+        &qxOwned, cbQx,
+        &qyOwned, cbQy,
+        &dBorrowed, cbD,
+        &pOwned, cbP,
+        &aOwned, cbA,
+        &bOwned, cbB,
+        &gxOwned, cbGx,
+        &gyOwned, cbGy,
+        &orderOwned, cbOrder,
+        &cofactorOwned, cbCofactor,
+        &seedOwned, cbSeed);
+
+    if (rc == 1)
     {
-        return 0;
+        // Transfer ownership of the allocated BIGNUMs (cast away const via uintptr_t to avoid -Wcast-qual).
+        *qx = (BIGNUM*)(uintptr_t)qxOwned;
+        *qy = (BIGNUM*)(uintptr_t)qyOwned;
+        *p = (BIGNUM*)(uintptr_t)pOwned;
+        *a = (BIGNUM*)(uintptr_t)aOwned;
+        *b = (BIGNUM*)(uintptr_t)bOwned;
+        *gx = (BIGNUM*)(uintptr_t)gxOwned;
+        *gy = (BIGNUM*)(uintptr_t)gyOwned;
+        *order = (BIGNUM*)(uintptr_t)orderOwned;
+        *cofactor = (BIGNUM*)(uintptr_t)cofactorOwned;
+        *seed = (BIGNUM*)(uintptr_t)seedOwned;
+
+        // d is borrowed from the EC_KEY, so we must duplicate before EC_KEY_free below.
+        if (includePrivate && dBorrowed)
+        {
+            *d = BN_dup(dBorrowed);
+            if (*d == NULL)
+            {
+                BN_free(*qx); BN_free(*qy); BN_free(*p); BN_free(*a); BN_free(*b);
+                BN_free(*gx); BN_free(*gy); BN_free(*order); BN_free(*cofactor);
+                if (*seed) BN_free(*seed);
+                *qx = *qy = *p = *a = *b = *gx = *gy = *order = *cofactor = *seed = NULL;
+                *cbQx = *cbQy = *cbP = *cbA = *cbB = *cbGx = *cbGy = *cbOrder = *cbCofactor = *cbSeed = 0;
+                if (cbD) *cbD = 0;
+                rc = 0;
+            }
+        }
+        else
+        {
+            *d = NULL;
+        }
+    }
+
+    EC_KEY_free(ecKey);
+    return rc;
+}
+#endif
+
+int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
+    EVP_PKEY* pkey,
+    int32_t includePrivate,
+    ECCurveType* curveType,
+    BIGNUM** qx, int32_t* cbQx,
+    BIGNUM** qy, int32_t* cbQy,
+    BIGNUM** d, int32_t* cbD,
+    BIGNUM** p, int32_t* cbP,
+    BIGNUM** a, int32_t* cbA,
+    BIGNUM** b, int32_t* cbB,
+    BIGNUM** gx, int32_t* cbGx,
+    BIGNUM** gy, int32_t* cbGy,
+    BIGNUM** order, int32_t* cbOrder,
+    BIGNUM** cofactor, int32_t* cbCofactor,
+    BIGNUM** seed, int32_t* cbSeed)
+{
+    assert(p != NULL);
+    assert(cbP != NULL);
+    assert(a != NULL);
+    assert(cbA != NULL);
+    assert(b != NULL);
+    assert(cbB != NULL);
+    assert(gx != NULL);
+    assert(cbGx != NULL);
+    assert(gy != NULL);
+    assert(cbGy != NULL);
+    assert(order != NULL);
+    assert(cbOrder != NULL);
+    assert(cofactor != NULL);
+    assert(cbCofactor != NULL);
+    assert(seed != NULL);
+    assert(cbSeed != NULL);
+
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (!API_EXISTS(EC_GROUP_get_field_type) ||
+        !API_EXISTS(EVP_PKEY_get_octet_string_param) ||
+        !API_EXISTS(EVP_PKEY_get_utf8_string_param) ||
+        !API_EXISTS(EVP_PKEY_get_bn_param) ||
+        !API_EXISTS(EVP_PKEY_get0_provider))
+    {
+        return 0;
     }
 #endif
 
 #ifdef NEED_OPENSSL_3_0
+    // For legacy EC_KEY-backed EVP_PKEYs, the OSSL 3.0 params API (get_bn_param etc.)
+    // does not expose curve parameters. Use EC_KEY APIs directly instead.
+    if (!EVP_PKEY_get0_provider(pkey))
+    {
+        return EvpPKeyGetEcCurveParameters_FromEcKey(
+            pkey, includePrivate, curveType,
+            qx, cbQx, qy, cbQy, d, cbD,
+            p, cbP, a, cbA, b, cbB,
+            gx, cbGx, gy, cbGy,
+            order, cbOrder, cofactor, cbCofactor,
+            seed, cbSeed);
+    }
+
     ERR_clear_error();
 
-    // Get the public key parameters first in case any of its 'out' parameters are not initialized
+    // Provider-backed key: use OSSL 3.0 params API
     int32_t rc = CryptoNative_EvpPKeyGetEcKeyParameters(pkey, includePrivate, qx, cbQx, qy, cbQy, d, cbD);
 
     EC_POINT* G = NULL;
@@ -800,8 +1187,31 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
     if (!xBn || !yBn)
         goto error;
 
-    if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || !curveTypeNID)
-        goto error;
+    if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || curveTypeNID == NID_undef)
+    {
+        // For explicit curves, the group name may not be available.
+        // Get the field type directly instead.
+        char fieldTypeStr[32] = {0};
+        if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldTypeStr, sizeof(fieldTypeStr), NULL))
+            goto error;
+
+        fieldTypeNID = OBJ_txt2nid(fieldTypeStr);
+        if (fieldTypeNID == NID_undef)
+            goto error;
+    }
+    else
+    {
+        // Named curve: create group from the curve NID to get the field type.
+        group = EC_GROUP_new_by_curve_name(curveTypeNID);
+        if (!group)
+            goto error;
+
+        // In some cases EVP_PKEY_get_field_type can return NID_undef
+        // and some providers seem to be ignoring OSSL_PKEY_PARAM_EC_FIELD_TYPE.
+        // This is specifically true for tpm2 provider.
+        // We can reliably get the field type from the EC_GROUP.
+        fieldTypeNID = EC_GROUP_get_field_type(group);
+    }
 
     // Extract p, a, b
     if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_P, &pBn))
@@ -813,19 +1223,32 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
     if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_B, &bBn))
         goto error;
 
-    // curveTypeNID will be always NID_X9_62_characteristic_two_field or NID_X9_62_prime_field
-    group = EC_GROUP_new_by_curve_name(curveTypeNID);
-
-    // In some cases EVP_PKEY_get_field_type can return NID_undef
-    // and some providers seem to be ignoring OSSL_PKEY_PARAM_EC_FIELD_TYPE.
-    // This is specifically true for tpm2 provider.
-    // We can reliably get the field type from the EC_GROUP.
-    fieldTypeNID = EC_GROUP_get_field_type(group);
-
     *curveType = NIDToCurveType(fieldTypeNID);
     if (*curveType == Unspecified)
         goto error;
 
+    // For explicit curves where group was not created from a curve name,
+    // build it from the field parameters to decode the generator point.
+    if (!group)
+    {
+#if HAVE_OPENSSL_EC2M
+        if (fieldTypeNID == NID_X9_62_characteristic_two_field)
+        {
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+            if (API_EXISTS(EC_GROUP_new_curve_GF2m))
+#endif
+            {
+                group = EC_GROUP_new_curve_GF2m(pBn, aBn, bBn, NULL);
+            }
+        }
+        else
+#endif
+        if (fieldTypeNID == NID_X9_62_prime_field)
+        {
+            group = EC_GROUP_new_curve_GFp(pBn, aBn, bBn, NULL);
+        }
+    }
+
     if (!group)
         goto error;
 
@@ -844,7 +1267,7 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
     if (!EC_POINT_oct2point(group, G, generatorBuffer, generatorBufferSize, NULL))
         goto error;
 
-    if (!EcPointGetAffineCoordinates(group, *curveType, G, xBn, yBn))
+    if (!EcPointGetAffineCoordinates(group, G, xBn, yBn))
         goto error;
 
     // Extract order (n)
@@ -939,3 +1362,741 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
     return 0;
 #endif
 }
+
+#ifdef NEED_OPENSSL_1_1
+// Returns 1 on success, 2 if OID not recognized, and 0 on failure.
+static int32_t EvpPKeyGenerateByEcCurveOid_Legacy(
+    EVP_PKEY** pkey,
+    const char* oid,
+    int32_t* keySize)
+{
+    int rc = 0;
+    EC_KEY* ecKey = NULL;
+
+    ecKey = CryptoNative_EcKeyCreateByOid(oid);
+    if (ecKey == NULL)
+    {
+        rc = 2;
+        goto error;
+    }
+
+    if (!CryptoNative_EcKeyGenerateKey(ecKey))
+        goto error;
+
+    if (keySize != NULL)
+    {
+        const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+        *keySize = group ? EC_GROUP_get_degree(group) : 0;
+        if (*keySize == 0)
+            goto error;
+    }
+
+    *pkey = EVP_PKEY_new();
+    if (*pkey == NULL)
+        goto error;
+
+    if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey))
+        goto error;
+
+    rc = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (ecKey) EC_KEY_free(ecKey);
+
+    return rc;
+}
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+// Returns 1 on success, 2 if OID not recognized, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure.
+static int32_t EvpPKeyGenerateByEcCurveOid(
+    EVP_PKEY** pkey,
+    const char* oid,
+    int32_t* keySize)
+{
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (!API_EXISTS(EVP_PKEY_CTX_new_from_name) || !API_EXISTS(EVP_PKEY_CTX_set_group_name))
+    {
+        return 3;
+    }
+#endif
+    int rc = 0;
+    EVP_PKEY_CTX* ctx = NULL;
+    const char* groupName = NULL;
+
+    int nid = OBJ_txt2nid(oid);
+    if (nid == NID_undef)
+    {
+        rc = 2;
+        goto error;
+    }
+
+    // OpenSSL canonicalizes the nid to a short name internally, so we do the same:
+    // https://github.com/openssl/openssl/blob/7836b7d5b6a6b27a441c4e4c8564be6b270580c4/crypto/evp/ctrl_params_translate.c#L1154
+    groupName = OBJ_nid2sn(nid);
+    if (!groupName)
+        goto error;
+
+    ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+    if (ctx == NULL)
+        goto error;
+
+    if (EVP_PKEY_keygen_init(ctx) <= 0)
+        goto error;
+
+    if (EVP_PKEY_CTX_set_group_name(ctx, groupName) <= 0)
+    {
+        rc = 2;
+        goto error;
+    }
+
+    if (EVP_PKEY_keygen(ctx, pkey) <= 0)
+    {
+        rc = 2;
+        goto error;
+    }
+
+    if (keySize != NULL)
+    {
+        *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey);
+    }
+
+    rc = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (ctx) EVP_PKEY_CTX_free(ctx);
+
+    return rc;
+}
+#endif
+
+int32_t CryptoNative_EvpPKeyGenerateByEcCurveOid(
+    EVP_PKEY** pkey,
+    const char* oid,
+    int32_t* keySize)
+{
+    if (!pkey || !oid)
+    {
+        assert(false);
+        return 0;
+    }
+
+    *pkey = NULL;
+
+    ERR_clear_error();
+
+    int rc = 0;
+
+#ifdef NEED_OPENSSL_3_0
+    rc = EvpPKeyGenerateByEcCurveOid(pkey, oid, keySize);
+#endif
+
+#ifdef NEED_OPENSSL_1_1
+    // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present.
+#if FEATURE_DISTRO_AGNOSTIC_SSL
+    if (rc == 3)
+#endif
+    {
+        rc = EvpPKeyGenerateByEcCurveOid_Legacy(pkey, oid, keySize);
+    }
+#endif
+
+    return rc;
+}
+
+#ifdef NEED_OPENSSL_1_1
+static int32_t EvpPKeyCreateByEcParameters_Legacy(
+    EVP_PKEY** pkey,
+    const char* oid,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    int32_t* keySize)
+{
+    int rc = 0;
+    EC_KEY* ecKey = NULL;
+
+    rc = CryptoNative_EcKeyCreateByKeyParameters(&ecKey, oid, qx, qxLength, qy, qyLength, d, dLength);
+    if (rc == -1)
+    {
+        rc = 2;
+        goto error;
+    }
+
+    if (rc != 1 || ecKey == NULL)
+        goto error;
+
+    if (keySize != NULL)
+    {
+        const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+        *keySize = group ? EC_GROUP_get_degree(group) : 0;
+        if (*keySize == 0)
+            goto error;
+    }
+
+    *pkey = EVP_PKEY_new();
+    if (*pkey == NULL)
+        goto error;
+
+    if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey))
+        goto error;
+
+    rc = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (ecKey) EC_KEY_free(ecKey);
+
+    return rc;
+}
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+// Returns 1 on success, 2 if OID not recognized, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure.
+static int32_t EvpPKeyCreateByEcParameters(
+    EVP_PKEY** pkey,
+    const char* oid,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    int32_t* keySize)
+{
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (!API_EXISTS(EVP_PKEY_fromdata) ||
+        !API_EXISTS(EVP_PKEY_fromdata_init) ||
+        !API_EXISTS(EVP_PKEY_CTX_new_from_name) ||
+        !API_EXISTS(OSSL_PARAM_BLD_new) ||
+        !API_EXISTS(OSSL_PARAM_BLD_free) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_utf8_string) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_octet_string) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_BN) ||
+        !API_EXISTS(OSSL_PARAM_BLD_to_param) ||
+        !API_EXISTS(OSSL_PARAM_free))
+    {
+        return 3;
+    }
+#endif
+
+    int nid = OBJ_txt2nid(oid);
+    if (nid == NID_undef)
+        return 2;
+
+    // OpenSSL canonicalizes the nid to a short name internally, so we do the same:
+    // https://github.com/openssl/openssl/blob/7836b7d5b6a6b27a441c4e4c8564be6b270580c4/crypto/evp/ctrl_params_translate.c#L1154
+    const char* groupName = OBJ_nid2sn(nid);
+    if (!groupName)
+        return 0;
+
+    int ret = 0;
+    EVP_PKEY_CTX* ctx = NULL;
+    uint8_t* pubKeyBuf = NULL;
+    OSSL_PARAM_BLD* bld = NULL;
+    OSSL_PARAM* params = NULL;
+    BIGNUM* dBn = NULL;
+    EC_GROUP* group = NULL;
+    const int hasPrivateKey = (d != NULL && dLength > 0);
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL)
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, groupName, 0))
+        goto error;
+
+    if (hasPrivateKey)
+    {
+        dBn = BN_bin2bn(d, dLength, NULL);
+        if (dBn == NULL)
+            goto error;
+
+        if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn))
+            goto error;
+    }
+
+    // Build an EC_GROUP to (if needed) derive the public key or encode coordinates.
+    group = EC_GROUP_new_by_curve_name(nid);
+    if (group == NULL)
+        goto error;
+
+    // Push public key, deriving it from the private key if unavailable.
+    if (!PushPublicKeyParam(bld, group, qx, qxLength, qy, qyLength, dBn, &pubKeyBuf))
+        goto error;
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL)
+        goto error;
+
+    ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+    if (ctx == NULL)
+        goto error;
+
+    if (EVP_PKEY_fromdata_init(ctx) != 1)
+        goto error;
+
+    {
+        int selection = hasPrivateKey ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY;
+        if (EVP_PKEY_fromdata(ctx, pkey, selection, params) != 1)
+            goto error;
+    }
+
+    if (keySize != NULL)
+    {
+        *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey);
+    }
+
+    ret = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (params) OSSL_PARAM_free(params);
+    if (bld) OSSL_PARAM_BLD_free(bld);
+    if (ctx) EVP_PKEY_CTX_free(ctx);
+    if (dBn) BN_clear_free(dBn);
+    if (group) EC_GROUP_free(group);
+    if (pubKeyBuf) OPENSSL_free(pubKeyBuf);
+    return ret;
+}
+#endif
+
+int32_t CryptoNative_EvpPKeyCreateByEcParameters(
+    EVP_PKEY** pkey,
+    const char* oid,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    int32_t* keySize)
+{
+    if (!pkey || !oid)
+    {
+        assert(false);
+        return 0;
+    }
+
+    *pkey = NULL;
+
+    ERR_clear_error();
+
+    int rc = 0;
+
+#ifdef NEED_OPENSSL_3_0
+    rc = EvpPKeyCreateByEcParameters(pkey, oid, qx, qxLength, qy, qyLength, d, dLength, keySize);
+#endif
+
+#ifdef NEED_OPENSSL_1_1
+    // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present.
+#if FEATURE_DISTRO_AGNOSTIC_SSL
+    if (rc == 3)
+#endif
+    {
+        rc = EvpPKeyCreateByEcParameters_Legacy(pkey, oid, qx, qxLength, qy, qyLength, d, dLength, keySize);
+    }
+#endif
+
+    return rc;
+}
+
+#ifdef NEED_OPENSSL_1_1
+static int32_t EvpPKeyCreateByEcExplicitParameters_Legacy(
+    ECCurveType curveType,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    const uint8_t* p, int32_t pLength,
+    const uint8_t* a, int32_t aLength,
+    const uint8_t* b, int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order, int32_t orderLength,
+    const uint8_t* cofactor, int32_t cofactorLength,
+    const uint8_t* seed, int32_t seedLength,
+    EVP_PKEY** pkey,
+    int32_t* keySize)
+{
+    int rc = 0;
+    EC_KEY* ecKey = NULL;
+
+    ecKey = CryptoNative_EcKeyCreateByExplicitParameters(
+        curveType,
+        qx, qxLength, qy, qyLength,
+        d, dLength,
+        p, pLength, a, aLength, b, bLength,
+        gx, gxLength, gy, gyLength,
+        order, orderLength, cofactor, cofactorLength,
+        seed, seedLength);
+
+    if (ecKey == NULL)
+        goto error;
+
+    // If no public key and no private key were provided, generate a new key pair.
+    if (!qx && !d)
+    {
+        if (!EC_KEY_generate_key(ecKey))
+            goto error;
+    }
+
+    if (keySize != NULL)
+    {
+        const EC_GROUP* group = EC_KEY_get0_group(ecKey);
+        *keySize = group ? EC_GROUP_get_degree(group) : 0;
+        if (*keySize == 0)
+            goto error;
+    }
+
+    *pkey = EVP_PKEY_new();
+    if (*pkey == NULL)
+        goto error;
+
+    if (!EVP_PKEY_set1_EC_KEY(*pkey, ecKey))
+        goto error;
+
+    rc = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (ecKey) EC_KEY_free(ecKey);
+
+    return rc;
+}
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+// Returns 1 on success, 3 if the required OSSL 3.0+ APIs aren't present, and 0 on failure.
+static int32_t EvpPKeyCreateByEcExplicitParameters(
+    ECCurveType curveType,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    const uint8_t* p, int32_t pLength,
+    const uint8_t* a, int32_t aLength,
+    const uint8_t* b, int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order, int32_t orderLength,
+    const uint8_t* cofactor, int32_t cofactorLength,
+    const uint8_t* seed, int32_t seedLength,
+    EVP_PKEY** pkey,
+    int32_t* keySize)
+{
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+    if (!API_EXISTS(EVP_PKEY_fromdata) ||
+        !API_EXISTS(EVP_PKEY_fromdata_init) ||
+        !API_EXISTS(EVP_PKEY_CTX_new_from_name) ||
+        !API_EXISTS(EVP_PKEY_CTX_new_from_pkey) ||
+        !API_EXISTS(EVP_PKEY_generate) ||
+        !API_EXISTS(OSSL_PARAM_BLD_new) ||
+        !API_EXISTS(OSSL_PARAM_BLD_free) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_utf8_string) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_octet_string) ||
+        !API_EXISTS(OSSL_PARAM_BLD_push_BN) ||
+        !API_EXISTS(OSSL_PARAM_BLD_to_param) ||
+        !API_EXISTS(OSSL_PARAM_free))
+    {
+        return 3;
+    }
+#endif
+
+    int ret = 0;
+    EVP_PKEY_CTX* ctx = NULL;
+    OSSL_PARAM_BLD* bld = NULL;
+    OSSL_PARAM* params = NULL;
+    uint8_t* generatorBuf = NULL;
+    uint8_t* pubKeyBuf = NULL;
+    BIGNUM* pBn = NULL;
+    BIGNUM* aBn = NULL;
+    BIGNUM* bBn = NULL;
+    BIGNUM* orderBn = NULL;
+    BIGNUM* cofactorBn = NULL;
+    BIGNUM* dBn = NULL;
+    BIGNUM* gxBn = NULL;
+    BIGNUM* gyBn = NULL;
+    EC_GROUP* group = NULL;
+    EC_POINT* G = NULL;
+    size_t genLen = 0;
+
+    const int hasPublicKey = (qx != NULL && qy != NULL);
+    const int hasPrivateKey = (d != NULL && dLength > 0);
+
+    const char* fieldType = (curveType == Characteristic2)
+        ? SN_X9_62_characteristic_two_field
+        : SN_X9_62_prime_field;
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL)
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE, fieldType, 0))
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_ENCODING, OSSL_PKEY_EC_ENCODING_EXPLICIT, 0))
+        goto error;
+
+    pBn = BN_bin2bn(p, pLength, NULL);
+    aBn = BN_bin2bn(a, aLength, NULL);
+    bBn = BN_bin2bn(b, bLength, NULL);
+
+    if (!pBn || !aBn || !bBn)
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, pBn))
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, aBn))
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, bBn))
+        goto error;
+
+    // Construct the EC_GROUP from the explicit curve parameters so we can use
+    // EC_POINT_set_affine_coordinates for proper point encoding.
+#if HAVE_OPENSSL_EC2M
+    if (curveType == Characteristic2)
+    {
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+        if (API_EXISTS(EC_GROUP_new_curve_GF2m))
+#endif
+        {
+            group = EC_GROUP_new_curve_GF2m(pBn, aBn, bBn, NULL);
+        }
+    }
+    else
+#endif
+    if (curveType != Characteristic2)
+    {
+        group = EC_GROUP_new_curve_GFp(pBn, aBn, bBn, NULL);
+    }
+
+    // When HAVE_OPENSSL_EC2M is not defined and curveType is Characteristic2,
+    // group remains NULL here. This matches the old behavior where the legacy
+    // EcKeyCreateByExplicitParameters also returned NULL, surfacing as a
+    // CryptographicException on the managed side.
+    if (group == NULL)
+        goto error;
+
+    // Set the generator on the group (needed for point encoding and key derivation).
+    G = EC_POINT_new(group);
+    gxBn = BN_bin2bn(gx, gxLength, NULL);
+    gyBn = BN_bin2bn(gy, gyLength, NULL);
+
+    if (G == NULL || gxBn == NULL || gyBn == NULL)
+        goto error;
+
+    if (!EC_POINT_set_affine_coordinates(group, G, gxBn, gyBn, NULL))
+        goto error;
+
+    orderBn = BN_bin2bn(order, orderLength, NULL);
+    cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL);
+
+    if (!orderBn || !cofactorBn)
+        goto error;
+
+    if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn))
+        goto error;
+
+    // Encode generator as uncompressed point for OSSL_PARAM.
+    generatorBuf = EncodeEcPointFromPoint(group, G, &genLen);
+    if (generatorBuf == NULL)
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, generatorBuf, genLen))
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, orderBn))
+        goto error;
+
+    if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactorBn))
+        goto error;
+
+    if (seed && seedLength > 0)
+    {
+        if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED, seed, (size_t)seedLength))
+            goto error;
+    }
+
+    if (hasPrivateKey)
+    {
+        dBn = BN_bin2bn(d, dLength, NULL);
+        if (dBn == NULL)
+            goto error;
+
+        if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn))
+            goto error;
+    }
+
+    // Push public key, deriving it from the private key if unavailable.
+    if (!PushPublicKeyParam(bld, group, qx, qxLength, qy, qyLength, dBn, &pubKeyBuf))
+        goto error;
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL)
+        goto error;
+
+    ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+    if (ctx == NULL)
+        goto error;
+
+    if (!hasPublicKey && !hasPrivateKey)
+    {
+        // No key material — generate a new key from the domain parameters.
+        EVP_PKEY* templateKey = NULL;
+
+        if (EVP_PKEY_fromdata_init(ctx) != 1)
+            goto error;
+
+        if (EVP_PKEY_fromdata(ctx, &templateKey, EVP_PKEY_KEY_PARAMETERS, params) != 1)
+            goto error;
+
+        EVP_PKEY_CTX_free(ctx);
+        ctx = EVP_PKEY_CTX_new_from_pkey(NULL, templateKey, NULL);
+        EVP_PKEY_free(templateKey);
+
+        if (ctx == NULL)
+            goto error;
+
+        if (EVP_PKEY_keygen_init(ctx) != 1)
+            goto error;
+
+        if (EVP_PKEY_generate(ctx, pkey) != 1)
+            goto error;
+    }
+    else
+    {
+        if (EVP_PKEY_fromdata_init(ctx) != 1)
+            goto error;
+
+        int selection = hasPrivateKey ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY;
+        if (EVP_PKEY_fromdata(ctx, pkey, selection, params) != 1)
+            goto error;
+    }
+
+    if (keySize != NULL)
+    {
+        *keySize = CryptoNative_EvpPKeyGetEcKeySize(*pkey);
+    }
+
+    ret = 1;
+    goto exit;
+
+error:
+    if (keySize) *keySize = 0;
+    if (*pkey)
+    {
+        EVP_PKEY_free(*pkey);
+        *pkey = NULL;
+    }
+
+exit:
+    if (params) OSSL_PARAM_free(params);
+    if (bld) OSSL_PARAM_BLD_free(bld);
+    if (ctx) EVP_PKEY_CTX_free(ctx);
+    if (generatorBuf) OPENSSL_free(generatorBuf);
+    if (pubKeyBuf) OPENSSL_free(pubKeyBuf);
+    if (pBn) BN_free(pBn);
+    if (aBn) BN_free(aBn);
+    if (bBn) BN_free(bBn);
+    if (orderBn) BN_free(orderBn);
+    if (cofactorBn) BN_free(cofactorBn);
+    if (dBn) BN_clear_free(dBn);
+    if (gxBn) BN_free(gxBn);
+    if (gyBn) BN_free(gyBn);
+    if (G) EC_POINT_free(G);
+    if (group) EC_GROUP_free(group);
+    return ret;
+}
+#endif
+
+int32_t CryptoNative_EvpPKeyCreateByEcExplicitParameters(
+    ECCurveType curveType,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    const uint8_t* p, int32_t pLength,
+    const uint8_t* a, int32_t aLength,
+    const uint8_t* b, int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order, int32_t orderLength,
+    const uint8_t* cofactor, int32_t cofactorLength,
+    const uint8_t* seed, int32_t seedLength,
+    EVP_PKEY** pkey,
+    int32_t* keySize)
+{
+    if (!p || !a || !b || !gx || !gy || !order || !cofactor || !pkey)
+    {
+        assert(false);
+        return 0;
+    }
+
+    *pkey = NULL;
+
+    ERR_clear_error();
+
+    int rc = 0;
+
+#ifdef NEED_OPENSSL_3_0
+    rc = EvpPKeyCreateByEcExplicitParameters(
+        curveType, qx, qxLength, qy, qyLength, d, dLength,
+        p, pLength, a, aLength, b, bLength,
+        gx, gxLength, gy, gyLength,
+        order, orderLength, cofactor, cofactorLength, seed, seedLength,
+        pkey, keySize);
+#endif
+
+#ifdef NEED_OPENSSL_1_1
+    // The portable build should only use the legacy OSSL 1.1 code path if OSSL 3.0+ APIs aren't present.
+#if FEATURE_DISTRO_AGNOSTIC_SSL
+    if (rc == 3)
+#endif
+    {
+        rc = EvpPKeyCreateByEcExplicitParameters_Legacy(
+            curveType, qx, qxLength, qy, qyLength, d, dLength,
+            p, pLength, a, aLength, b, bLength,
+            gx, gxLength, gy, gyLength,
+            order, orderLength, cofactor, cofactorLength, seed, seedLength,
+            pkey, keySize);
+    }
+#endif
+
+    return rc;
+}
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h
index ba16b5450c80c8..084b6166cc85cb 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_ecc_import_export.h
@@ -51,19 +51,20 @@ Returns 1 upon success, -1 if oid was not found, otherwise 0.
 PALEXPORT int32_t CryptoNative_EcKeyCreateByKeyParameters(
     EC_KEY** key,
     const char* oid,
-    uint8_t* qx, int32_t qxLength, 
-    uint8_t* qy, int32_t qyLength, 
-    uint8_t* d, int32_t dLength);
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength);
 
 /*
 Gets the NID of the curve name as an oid value for the specified EVP_PKEY.
 
 Returns 1 upon success, otherwise 0.
 */
-PALEXPORT int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName);
+PALEXPORT int32_t CryptoNative_EvpPKeyGetEcGroupNid(EVP_PKEY *pkey, int32_t* nidName);
 
 /*
-Returns the EC curve type of the given EVP_PKEY.
+Returns the EC key parameters (public coordinates and optional private key) from the given EVP_PKEY.
+Returns 1 upon success, -1 if includePrivate is set but the key has no private component, otherwise 0.
 */
 PALEXPORT int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
     const EVP_PKEY* pkey,
@@ -77,23 +78,87 @@ Returns the new EC_KEY instance using the explicit parameters.
 */
 PALEXPORT EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters(
     ECCurveType curveType,
-    uint8_t* qx, int32_t qxLength,
-    uint8_t* qy, int32_t qyLength,
-    uint8_t* d, int32_t dLength,
-    uint8_t* p, int32_t pLength,
-    uint8_t* a, int32_t aLength,
-    uint8_t* b, int32_t bLength,
-    uint8_t* gx, int32_t gxLength,
-    uint8_t* gy, int32_t gyLength,
-    uint8_t* order, int32_t nLength,
-    uint8_t* cofactor, int32_t hLength,
-    uint8_t* seed, int32_t sLength);
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    const uint8_t* p, int32_t pLength,
+    const uint8_t* a, int32_t aLength,
+    const uint8_t* b, int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order, int32_t nLength,
+    const uint8_t* cofactor, int32_t hLength,
+    const uint8_t* seed, int32_t sLength);
+
+/*
+Generates a new EC key pair for a named curve using EVP_PKEY APIs.
+On OpenSSL 3.0+ uses EVP_PKEY_CTX, otherwise falls back to EC_KEY APIs.
+If keySize is not NULL, it receives the key size in bits.
+Returns 1 upon success, 2 if oid was not found, otherwise 0.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyGenerateByEcCurveOid(
+    EVP_PKEY** pkey,
+    const char* oid,
+    int32_t* keySize);
+
+/*
+Returns 1 if the EVP_PKEY EC key uses explicit encoding, 0 if it uses named curve encoding
+or the encoding could not be read (named curve is the default), or -1 if the API is unavailable
+(e.g. pre-3.0 OpenSSL) and the caller should use an alternative method.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(EVP_PKEY* pkey);
+
+/*
+Returns the EC field degree (bits in the underlying field) for the specified EVP_PKEY.
+This is the value used as KeySize for EC keys in .NET, matching Windows CNG's
+BCRYPT_PUBLIC_KEY_LENGTH semantics for EC keys (field modulus bit-size, e.g. 256
+for P-256). Note that for some uncommon curves this differs from the subgroup
+order's bit length (e.g. wap-wsg-idm-ecid-wtls7 has field=160, order=161).
+Works for both provider-backed and legacy EC_KEY-backed EVP_PKEYs.
+Returns 0 on failure.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyGetEcKeySize(EVP_PKEY* pkey);
+
+/*
+Creates a new EVP_PKEY for a named EC curve using the provided key parameters.
+On success, *pkey receives the new key and *keySize receives the key size in bits.
+Returns 1 upon success, 2 if oid was not found, otherwise 0.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyCreateByEcParameters(
+    EVP_PKEY** pkey,
+    const char* oid,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    int32_t* keySize);
+
+/*
+Creates a new EVP_PKEY for an EC key with explicit curve parameters.
+On success, *pkey receives the new key and *keySize receives the key size in bits.
+Returns 1 upon success, otherwise 0.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyCreateByEcExplicitParameters(
+    ECCurveType curveType,
+    const uint8_t* qx, int32_t qxLength,
+    const uint8_t* qy, int32_t qyLength,
+    const uint8_t* d, int32_t dLength,
+    const uint8_t* p, int32_t pLength,
+    const uint8_t* a, int32_t aLength,
+    const uint8_t* b, int32_t bLength,
+    const uint8_t* gx, int32_t gxLength,
+    const uint8_t* gy, int32_t gyLength,
+    const uint8_t* order, int32_t orderLength,
+    const uint8_t* cofactor, int32_t cofactorLength,
+    const uint8_t* seed, int32_t seedLength,
+    EVP_PKEY** pkey,
+    int32_t* keySize);
 
 /*
 Returns the ECC curve parameters of the given EVP_PKEY.
+Returns 1 upon success, -1 if includePrivate is set but the key has no private component, otherwise 0.
 */
 PALEXPORT int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
-    const EVP_PKEY* pkey,
+    EVP_PKEY* pkey,
     int32_t includePrivate,
     ECCurveType* curveType,
     BIGNUM** qx, int32_t* cbQx,
