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..47c55fb8699f1d 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
@@ -96,6 +96,119 @@ internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve)
return key;
}
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcKeyParameters", StringMarshalling = StringMarshalling.Utf8)]
+ private static partial int EvpPKeyCreateByEcKeyParameters(
+ out SafeEvpPKeyHandle pkey,
+ string oid,
+ byte[]? qx, int qxLength,
+ byte[]? qy, int qyLength,
+ byte[]? d, int dLength);
+
+ internal static SafeEvpPKeyHandle? EvpPKeyCreateByEcKeyParameters(
+ string oid,
+ byte[]? qx, int qxLength,
+ byte[]? qy, int qyLength,
+ byte[]? d, int dLength)
+ {
+ SafeEvpPKeyHandle pkey;
+ int rc = EvpPKeyCreateByEcKeyParameters(out pkey, oid, qx, qxLength, qy, qyLength, d, dLength);
+
+ if (rc == -1)
+ {
+ pkey.Dispose();
+ ErrClearError();
+ throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
+ }
+
+ if (rc != 1 || pkey.IsInvalid)
+ {
+ pkey.Dispose();
+ ErrClearError();
+ return null;
+ }
+
+ return pkey;
+ }
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyCreateByEcExplicitParameters")]
+ private static partial SafeEvpPKeyHandle CryptoNative_EvpPKeyCreateByEcExplicitParameters(
+ 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);
+
+ internal static SafeEvpPKeyHandle? EvpPKeyCreateByEcExplicitParameters(
+ 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)
+ {
+ SafeEvpPKeyHandle pkey = CryptoNative_EvpPKeyCreateByEcExplicitParameters(
+ curveType,
+ qx, qxLength,
+ qy, qyLength,
+ d, dLength,
+ p, pLength,
+ a, aLength,
+ b, bLength,
+ gx, gxLength,
+ gy, gyLength,
+ order, orderLength,
+ cofactor, cofactorLength,
+ seed, seedLength);
+
+ if (pkey.IsInvalid)
+ {
+ pkey.Dispose();
+ ErrClearError();
+ return null;
+ }
+
+ return pkey;
+ }
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcKeyOid", StringMarshalling = StringMarshalling.Utf8)]
+ private static partial int EvpPKeyGenerateByEcKeyOid(
+ out SafeEvpPKeyHandle pkey,
+ string oid);
+
+ internal static SafeEvpPKeyHandle? EvpPKeyGenerateByEcKeyOid(string oid)
+ {
+ int rc = EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid);
+
+ if (rc == -1)
+ {
+ pkey.Dispose();
+ ErrClearError();
+ throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
+ }
+
+ if (rc != 1 || pkey.IsInvalid)
+ {
+ pkey.Dispose();
+ ErrClearError();
+ return null;
+ }
+
+ return pkey;
+ }
+
[LibraryImport(Libraries.CryptoNative)]
private static partial int CryptoNative_EvpPKeyGetEcGroupNid(SafeEvpPKeyHandle pkey, out int nid);
@@ -104,11 +217,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;
}
///
@@ -122,7 +236,35 @@ 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);
+
+ ///
+ /// Returns true if the key has explicit encoding, false if named (or encoding unavailable),
+ /// null if the API is unavailable (pre-3.0) and the caller should use an alternative method.
+ ///
+ internal static bool? EvpPKeyEcHasExplicitEncoding(SafeEvpPKeyHandle pkey)
+ {
+ int result = CryptoNative_EvpPKeyEcHasExplicitEncoding(pkey);
+ return result switch
+ {
+ 1 => true,
+ 0 => false,
+ _ => null,
+ };
+ }
+
+ [LibraryImport(Libraries.CryptoNative)]
+ private static partial int CryptoNative_EvpPKeyGetEcFieldDegree(SafeEvpPKeyHandle pkey);
+
+ internal static int EvpPKeyGetEcFieldDegree(SafeEvpPKeyHandle pkey)
+ {
+ return CryptoNative_EvpPKeyGetEcFieldDegree(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 a064f682ca65c6..ae93353f36402b 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanOpenSsl.Derive.cs
@@ -89,17 +89,19 @@ public override byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPa
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)
+ bool? explicitEncoding = Interop.Crypto.EvpPKeyEcHasExplicitEncoding(_key.Value);
+ if (explicitEncoding.HasValue)
{
- // This may happen when EVP_PKEY was created by provider and getting EC_KEY is not possible.
- thisIsNamed = Interop.Crypto.EvpPKeyHasCurveName(_key.Value);
+ thisIsNamed = !explicitEncoding.Value;
}
else
{
- thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey);
+ // Pre-3.0 fallback: check via EC_KEY.
+ using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key.Value))
+ {
+ thisIsNamed = Interop.Crypto.EcKeyHasCurveName(ecKey);
+ }
}
}
@@ -118,7 +120,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 +145,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 +154,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..1afd236cbe8613 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,55 @@ 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();
+
+ bool? result = Interop.Crypto.EvpPKeyEcHasExplicitEncoding(GetKey());
+
+ if (result.HasValue)
+ {
+ return result.Value;
+ }
+
+ // Fallback for EC_KEY-backed handles: check via EC_KEY.
+ using (SafeEcKeyHandle ecKey = Interop.Crypto.EvpPkeyGetEcKey(GetKey()))
+ {
+ if (ecKey is null || ecKey.IsInvalid)
+ {
+ throw new CryptographicException(SR.Cryptography_InvalidHandle);
+ }
+
+ return !Interop.Crypto.EcKeyHasCurveName(ecKey);
+ }
+ }
+ }
internal int KeySize
{
get
{
ThrowIfDisposed();
- return _key.KeySize;
+
+ int keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(_key);
+
+ if (keySize != 0)
+ {
+ return keySize;
+ }
+
+ // Fallback for EC_KEY-backed handles: get size via EC_KEY.
+ using (SafeEcKeyHandle? ecKey = Interop.Crypto.EvpPkeyGetEcKey(_key))
+ {
+ if (ecKey is not null && !ecKey.IsInvalid)
+ return Interop.Crypto.EcKeyGetSize(ecKey);
+ }
+
+ throw new CryptographicException(SR.Cryptography_InvalidHandle);
}
}
@@ -84,8 +110,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 +119,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..5df77b7d874b99 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs
@@ -181,14 +181,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 +196,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 +214,21 @@ public static ECParameters ExportExplicitParameters(SafeEvpPKeyHandle pkey, bool
///
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) == true)
{
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)
diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
index 20fd8788b1053e..043bbd765586e8 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.cs
@@ -117,6 +117,22 @@ private void FreeKey()
internal static SafeEvpPKeyHandle GenerateECKey(int keySize)
{
+ 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),
+ };
+
+ SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(oid);
+
+ if (pkey is not null)
+ {
+ return pkey;
+ }
+
+ // Fallback to legacy EC_KEY path
SafeEvpPKeyHandle ret = ImportECKeyCore(new ECOpenSsl(keySize), out int createdKeySize);
Debug.Assert(keySize == createdKeySize);
return ret;
@@ -124,11 +140,121 @@ internal static SafeEvpPKeyHandle GenerateECKey(int keySize)
internal static SafeEvpPKeyHandle GenerateECKey(ECCurve curve, out int keySize)
{
- return ImportECKeyCore(new ECOpenSsl(curve), out keySize);
+ curve.Validate();
+
+ if (curve.IsNamed)
+ {
+ string oid = !string.IsNullOrEmpty(curve.Oid.Value) ? curve.Oid.Value : curve.Oid.FriendlyName!;
+
+ SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyGenerateByEcKeyOid(oid);
+
+ if (pkey is not null)
+ {
+ keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey);
+
+ if (keySize != 0)
+ {
+ return pkey;
+ }
+
+ pkey.Dispose();
+ }
+ }
+ else if (curve.IsPrime || curve.IsCharacteristic2)
+ {
+ byte[] pField = curve.IsPrime ? curve.Prime! : curve.Polynomial!;
+
+ // Pass null Q and null D to trigger key generation instead of import.
+ SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcExplicitParameters(
+ curve.CurveType,
+ null, 0,
+ null, 0,
+ null, 0,
+ pField, pField.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 is null ? 0 : curve.Seed.Length);
+
+ if (pkey is not null)
+ {
+ keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey);
+
+ if (keySize != 0)
+ {
+ return pkey;
+ }
+
+ pkey.Dispose();
+ }
+ }
+
+ // Fallback to legacy EC_KEY path (explicit curves or OpenSSL < 3.0)
+ return ImportECKeyCore(new ECOpenSsl(curve), out keySize);
}
internal static SafeEvpPKeyHandle ImportECKey(ECParameters parameters, out int keySize)
{
+ parameters.Validate();
+
+ if (parameters.Curve.IsNamed)
+ {
+ string oid = !string.IsNullOrEmpty(parameters.Curve.Oid.Value) ?
+ parameters.Curve.Oid.Value : parameters.Curve.Oid.FriendlyName!;
+
+ SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcKeyParameters(
+ oid,
+ parameters.Q.X, parameters.Q.X?.Length ?? 0,
+ parameters.Q.Y, parameters.Q.Y?.Length ?? 0,
+ parameters.D, parameters.D is null ? 0 : parameters.D.Length);
+
+ if (pkey is not null)
+ {
+ keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey);
+
+ if (keySize != 0)
+ {
+ return pkey;
+ }
+
+ pkey.Dispose();
+ }
+ }
+ else if (parameters.Curve.IsPrime || parameters.Curve.IsCharacteristic2)
+ {
+ byte[] pField = parameters.Curve.IsPrime ? parameters.Curve.Prime! : parameters.Curve.Polynomial!;
+
+ SafeEvpPKeyHandle? pkey = Interop.Crypto.EvpPKeyCreateByEcExplicitParameters(
+ parameters.Curve.CurveType,
+ parameters.Q.X, parameters.Q.X?.Length ?? 0,
+ parameters.Q.Y, parameters.Q.Y?.Length ?? 0,
+ parameters.D, parameters.D is null ? 0 : parameters.D.Length,
+ pField, pField.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 is null ? 0 : parameters.Curve.Seed.Length);
+
+ if (pkey is not null)
+ {
+ keySize = Interop.Crypto.EvpPKeyGetEcFieldDegree(pkey);
+
+ if (keySize != 0)
+ {
+ return pkey;
+ }
+
+ pkey.Dispose();
+ }
+ }
+
+ // Fallback to legacy EC_KEY path
return ImportECKeyCore(new ECOpenSsl(parameters), out keySize);
}
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 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/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..4e4f5ed3f62cd9 100644
--- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs
+++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslTests.cs
@@ -129,6 +129,32 @@ 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.EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid);
+
+ if (!PlatformDetection.IsOpenSsl3)
+ {
+ Assert.Equal(0, rc);
+ pkey.Dispose();
+ return;
+ }
+
+ Assert.Equal(1, rc);
+ Assert.False(pkey.IsInvalid);
+
+ using (pkey)
+ using (ECDiffieHellmanOpenSsl e = new ECDiffieHellmanOpenSsl(pkey))
+ {
+ Assert.Equal(expectedKeySize, e.KeySize);
+ e.Exercise();
+ }
+ }
+
[Fact]
public void KeySizePropWithExercise()
{
@@ -278,5 +304,128 @@ 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 EcKeyAndEvpPKeyProduceSameExport(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 EcKeyAndEvpPKeyDeriveCrossCompatible(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 ExplicitCurveEcKeyAndEvpPKeyProduceSameExport()
+ {
+ ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+ using (ECDiffieHellman ecKeyBacked = ECDiffieHellman.Create(explicitCurve))
+ {
+ ECParameters explicitParams = ecKeyBacked.ExportExplicitParameters(true);
+
+ using (ECDiffieHellman evpBacked = ECDiffieHellman.Create(explicitParams))
+ {
+ ECParameters evpExplicitParams = evpBacked.ExportExplicitParameters(true);
+
+ ComparePublicKey(explicitParams.Q, evpExplicitParams.Q);
+ ComparePrivateKey(explicitParams, evpExplicitParams);
+ }
+ }
+ }
+
+ [Fact]
+ public void ExplicitCurveEcKeyAndEvpPKeyDeriveCrossCompatible()
+ {
+ 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);
+ }
+ }
+ }
}
}
diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
index 6f1139a8630629..da87d0cef76de8 100644
--- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
+++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslTests.cs
@@ -131,6 +131,32 @@ 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.EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, oid);
+
+ if (!PlatformDetection.IsOpenSsl3)
+ {
+ Assert.Equal(0, rc);
+ pkey.Dispose();
+ return;
+ }
+
+ Assert.Equal(1, rc);
+ Assert.False(pkey.IsInvalid);
+
+ using (pkey)
+ using (ECDsaOpenSsl e = new ECDsaOpenSsl(pkey))
+ {
+ Assert.Equal(expectedKeySize, e.KeySize);
+ e.Exercise();
+ }
+ }
+
[Fact]
public void KeySizePropWithExercise()
{
@@ -297,6 +323,113 @@ 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 EcKeyAndEvpPKeyProduceSameExport(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 EcKeyAndEvpPKeySignVerifyCrossCompatible(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);
+ }
+ }
+
+ [Fact]
+ public void ExplicitCurveEcKeyAndEvpPKeyProduceSameExport()
+ {
+ ECCurve explicitCurve = EccTestData.GetNistP256ExplicitCurve();
+
+ using (ECDsa ecKeyBacked = ECDsa.Create(explicitCurve))
+ {
+ ECParameters explicitParams = ecKeyBacked.ExportExplicitParameters(true);
+
+ using (ECDsa evpBacked = ECDsa.Create(explicitParams))
+ {
+ ECParameters evpExplicitParams = evpBacked.ExportExplicitParameters(true);
+
+ ComparePublicKey(explicitParams.Q, evpExplicitParams.Q);
+ ComparePrivateKey(explicitParams, evpExplicitParams);
+ }
+ }
+ }
+
+ [Fact]
+ public void ExplicitCurveEcKeyAndEvpPKeySignVerifyCrossCompatible()
+ {
+ 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));
+ }
+ }
+ }
}
}
@@ -315,5 +448,8 @@ internal static partial class Crypto
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_OpenSslVersionNumber")]
internal static extern uint OpenSslVersionNumber();
+
+ [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeyGenerateByEcKeyOid", CharSet = CharSet.Ansi)]
+ internal static extern int EvpPKeyGenerateByEcKeyOid(out SafeEvpPKeyHandle pkey, string oid);
}
}
diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c
index 87a567832bfad0..e8c70a778fe21a 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_EvpPKeyCreateByEcKeyParameters)
+ DllImportEntry(CryptoNative_EvpPKeyCreateByEcExplicitParameters)
+ DllImportEntry(CryptoNative_EvpPKeyGenerateByEcKeyOid)
+ DllImportEntry(CryptoNative_EvpPKeyEcHasExplicitEncoding)
+ DllImportEntry(CryptoNative_EvpPKeyGetEcFieldDegree)
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..1d16e9c7380f36 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
#include
#include
+#include
#include
#include
#endif
@@ -384,6 +385,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,8 +393,10 @@ 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) \
@@ -411,10 +415,13 @@ extern bool g_libSslUses32BitTime;
REQUIRED_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,8 +522,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) \
- REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \
- REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \
+ LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_group_name) \
LIGHTUP_FUNCTION(EVP_PKEY_CTX_set_params) \
FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_keygen_bits) \
FALLBACK_FUNCTION(EVP_PKEY_CTX_set_rsa_oaep_md) \
@@ -545,11 +551,14 @@ extern bool g_libSslUses32BitTime;
LIGHTUP_FUNCTION(EVP_PKEY_get0_RSA) \
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) \
REQUIRED_FUNCTION(EVP_PKEY_keygen_init) \
REQUIRED_FUNCTION(EVP_PKEY_new) \
+ REQUIRED_FUNCTION(EVP_PKEY_new_raw_private_key) \
+ REQUIRED_FUNCTION(EVP_PKEY_new_raw_public_key) \
REQUIRED_FUNCTION(EVP_PKEY_public_check) \
REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
@@ -644,6 +653,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 +853,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 +964,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 +973,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 +994,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 +1099,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
@@ -1104,6 +1128,7 @@ extern TYPEOF(OPENSSL_gmtime)* OPENSSL_gmtime_ptr;
#define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_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 +1235,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 +1437,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..ab0a64445f5652 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,21 @@
#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 EVP_PKEY_KEY_PARAMETERS 4
#define OSSL_PKEY_PARAM_RSA_N "n"
#define OSSL_PKEY_PARAM_RSA_E "e"
@@ -52,6 +56,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 +103,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 +125,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,6 +142,14 @@ 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);
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..0b7c9ee598bab0 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
@@ -4,6 +4,57 @@
#include "pal_ecc_import_export.h"
#include "pal_utilities.h"
+#ifdef NEED_OPENSSL_3_0
+
+// Encode two coordinates as an uncompressed EC point: 0x04 || x || y.
+// Coordinates are zero-padded on the left to the larger of the two lengths.
+// 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,
+ int32_t fieldSize,
+ int32_t* outLength)
+{
+ int32_t coordLen = fieldSize;
+ if (xLength > coordLen || yLength > coordLen)
+ return NULL;
+
+ int32_t len = 1 + 2 * coordLen;
+ uint8_t* buf = (uint8_t*)OPENSSL_zalloc((size_t)len);
+ if (buf == NULL)
+ return NULL;
+
+ buf[0] = 0x04;
+ memcpy(buf + 1 + (coordLen - xLength), x, (size_t)xLength);
+ memcpy(buf + 1 + coordLen + (coordLen - yLength), y, (size_t)yLength);
+ *outLength = len;
+
+ return buf;
+}
+
+// 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;
+}
+#endif
+
static ECCurveType MethodToCurveType(const EC_METHOD* method)
{
if (method == EC_GFp_mont_method())
@@ -49,26 +100,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 +138,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 +259,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))
@@ -458,6 +470,83 @@ int32_t CryptoNative_EvpPKeyGetEcGroupNid(const EVP_PKEY *pkey, int32_t* nidName
#endif
}
+int32_t CryptoNative_EvpPKeyEcHasExplicitEncoding(const EVP_PKEY* pkey)
+{
+ if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
+ return -1;
+
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+ if (!API_EXISTS(EVP_PKEY_get_utf8_string_param))
+ {
+ return -1;
+ }
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+ 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, "explicit") == 0) ? 1 : 0;
+#else
+ return -1;
+#endif
+}
+
+int32_t CryptoNative_EvpPKeyGetEcFieldDegree(const EVP_PKEY* pkey)
+{
+ if (!pkey || EVP_PKEY_get_base_id(pkey) != EVP_PKEY_EC)
+ return 0;
+
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+ if (!API_EXISTS(EVP_PKEY_get_bn_param) || !API_EXISTS(EVP_PKEY_get_utf8_string_param))
+ {
+ return 0;
+ }
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+ // Determine whether this is a binary field (GF(2^m)) for the degree adjustment.
+ // 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.
+ int isChar2 = 0;
+
+ int nid = 0;
+ if (CryptoNative_EvpPKeyGetEcGroupNid(pkey, &nid) && nid != NID_undef)
+ {
+ EC_GROUP* group = EC_GROUP_new_by_curve_name(nid);
+ if (group)
+ {
+ isChar2 = (EC_GROUP_get_field_type(group) == NID_X9_62_characteristic_two_field);
+ EC_GROUP_free(group);
+ }
+ }
+ else
+ {
+ char fieldType[32] = {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);
+ }
+ }
+
+ 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);
+ if (isChar2)
+ degree = degree > 0 ? degree - 1 : 0;
+
+ BN_free(p);
+ return degree;
+#else
+ return 0;
+#endif
+}
+
int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
const EVP_PKEY* pkey,
int32_t includePrivate,
@@ -473,7 +562,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,26 +575,118 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
#endif
int rc = 0;
+
+#ifdef NEED_OPENSSL_3_0
BIGNUM *xBn = NULL;
BIGNUM *yBn = NULL;
BIGNUM *dBn = 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;
+
+ pubKeyBuf = (uint8_t*)OPENSSL_zalloc(pubKeyLen);
+ if (pubKeyBuf == NULL)
+ goto error;
+
+ if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen, &pubKeyLen))
+ goto error;
+
+ // 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)
+ {
+ // Explicit curve — build EC_GROUP from the key's field params.
+ BIGNUM* ecP = NULL;
+ BIGNUM* ecA = NULL;
+ BIGNUM* ecB = NULL;
+
+ 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);
+
+ if (ecP == NULL || ecA == NULL || ecB == NULL)
+ {
+ BN_free(ecP);
+ BN_free(ecA);
+ BN_free(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);
+ }
+
+ BN_free(ecP);
+ BN_free(ecA);
+ BN_free(ecB);
+
+ 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 (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &yBn))
+
+ if (!EcPointGetAffineCoordinates(group, point, xBn, yBn))
goto error;
*qx = xBn;
- *cbQx = BN_num_bytes(xBn);
+ xBn = NULL;
+ *cbQx = BN_num_bytes(*qx);
*qy = yBn;
- *cbQy = BN_num_bytes(yBn);
+ yBn = NULL;
+ *cbQy = BN_num_bytes(*qy);
if (includePrivate)
{
@@ -511,7 +694,8 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
goto error;
*d = dBn;
- *cbD = BN_num_bytes(dBn);
+ dBn = NULL;
+ *cbD = BN_num_bytes(*d);
}
else
{
@@ -519,21 +703,32 @@ int32_t CryptoNative_EvpPKeyGetEcKeyParameters(
*cbD = 0;
}
- // success
- return 1;
+ 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);
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(
@@ -588,34 +783,15 @@ 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);
@@ -762,10 +938,10 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
assert(cbSeed != NULL);
#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
- if (!API_EXISTS(EC_GROUP_new_by_curve_name) ||
- !API_EXISTS(EC_GROUP_get_field_type) ||
+ if (!API_EXISTS(EC_GROUP_get_field_type) ||
!API_EXISTS(EVP_PKEY_get_octet_string_param) ||
- !API_EXISTS(EC_POINT_oct2point))
+ !API_EXISTS(EVP_PKEY_get_utf8_string_param) ||
+ !API_EXISTS(EVP_PKEY_get_bn_param))
{
return 0;
}
@@ -801,7 +977,30 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
goto error;
if (!CryptoNative_EvpPKeyGetEcGroupNid(pkey, &curveTypeNID) || !curveTypeNID)
- goto error;
+ {
+ // 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 +1012,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 +1056,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 +1151,518 @@ int32_t CryptoNative_EvpPKeyGetEcCurveParameters(
return 0;
#endif
}
+
+int32_t CryptoNative_EvpPKeyGenerateByEcKeyOid(
+ EVP_PKEY** pkey,
+ const char* oid)
+{
+ if (!pkey || !oid)
+ {
+ assert(false);
+ return 0;
+ }
+
+ *pkey = NULL;
+
+ ERR_clear_error();
+
+#ifdef FEATURE_DISTRO_AGNOSTIC_SSL
+ if (!API_EXISTS(EVP_PKEY_CTX_new_from_name) || !API_EXISTS(EVP_PKEY_CTX_set_group_name))
+ {
+ return 0;
+ }
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+
+ int nid = OBJ_txt2nid(oid);
+ if (!nid)
+ {
+ return -1;
+ }
+
+ const char* groupName = OBJ_nid2sn(nid);
+ if (!groupName)
+ {
+ return -1;
+ }
+
+ EVP_PKEY_CTX* 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)
+ goto error;
+
+ if (EVP_PKEY_keygen(ctx, pkey) <= 0)
+ goto error;
+
+ EVP_PKEY_CTX_free(ctx);
+ return 1;
+
+error:
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+
+ if (*pkey != NULL)
+ {
+ EVP_PKEY_free(*pkey);
+ *pkey = NULL;
+ }
+
+ return 0;
+#else
+ (void)oid;
+ return 0;
+#endif
+}
+
+int32_t CryptoNative_EvpPKeyCreateByEcKeyParameters(
+ 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)
+{
+ if (!pkey || !oid)
+ {
+ assert(false);
+ return 0;
+ }
+
+ *pkey = NULL;
+
+ ERR_clear_error();
+
+#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 0;
+ }
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+
+ // Verify the OID is recognized before doing any work.
+ int nid = OBJ_txt2nid(oid);
+ if (!nid)
+ {
+ return -1;
+ }
+
+ // OBJ_nid2sn returns the short name OpenSSL expects for the group name param.
+ const char* groupName = OBJ_nid2sn(nid);
+ if (!groupName)
+ {
+ return -1;
+ }
+
+ 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;
+ EC_POINT* pubPoint = NULL;
+ int32_t fieldSize = 0;
+
+ const int hasPublicKey = (qx != NULL && qy != 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;
+ }
+
+ // Build an EC_GROUP to determine the field size and (if needed) derive the public key.
+ group = EC_GROUP_new_by_curve_name(nid);
+ if (group == NULL)
+ goto error;
+
+ fieldSize = (EC_GROUP_get_degree(group) + 7) / 8;
+
+ // Push public key, deriving it from the private key if unavailable.
+ if (hasPublicKey)
+ {
+ int32_t pubKeyLen;
+
+ pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, fieldSize, &pubKeyLen);
+ if (pubKeyBuf == NULL)
+ goto error;
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, (size_t)pubKeyLen))
+ goto error;
+ }
+ else if (hasPrivateKey)
+ {
+ // No public key provided, derive Q = d * G using EC_GROUP/EC_POINT (not deprecated).
+ pubPoint = EC_POINT_new(group);
+ if (pubPoint == NULL ||
+ !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL))
+ goto error;
+
+ size_t pubKeyLen;
+ pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen);
+ if (pubKeyBuf == NULL)
+ goto error;
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen))
+ goto error;
+ }
+
+ if (hasPrivateKey)
+ {
+ if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, dBn))
+ 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;
+ }
+
+ ret = 1;
+ goto exit;
+
+error:
+ 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 (pubPoint) EC_POINT_free(pubPoint);
+ if (group) EC_GROUP_free(group);
+ if (pubKeyBuf) OPENSSL_free(pubKeyBuf);
+ return ret;
+#else
+ (void)oid;
+ (void)qx; (void)qxLength;
+ (void)qy; (void)qyLength;
+ (void)d; (void)dLength;
+ return 0;
+#endif
+}
+
+EVP_PKEY* 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)
+{
+ if (!p || !a || !b || !gx || !gy || !order || !cofactor)
+ {
+ assert(false);
+ return NULL;
+ }
+
+ ERR_clear_error();
+
+#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 NULL;
+ }
+#endif
+
+#ifdef NEED_OPENSSL_3_0
+
+ EVP_PKEY* pkey = NULL;
+ 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;
+ EC_POINT* pubPoint = NULL;
+ int32_t fieldBits = 0;
+ int32_t fieldSize = 0;
+ int32_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, "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;
+
+ // For prime curves, BN_num_bits(pBn) is the bit-length of the prime.
+ // For characteristic-2 curves, p is the irreducible polynomial of degree m,
+ // so BN_num_bits(pBn) = m + 1. The field size (coordinate length) is ceil(m/8).
+ fieldBits = BN_num_bits(pBn);
+
+ if (curveType == Characteristic2)
+ fieldBits--;
+
+ fieldSize = (fieldBits + 7) / 8;
+
+ // Generator as uncompressed point: 0x04 || gx || gy, padded to field size.
+ generatorBuf = EncodeEcPointFromCoordinates(gx, gxLength, gy, gyLength, fieldSize, &genLen);
+ if (generatorBuf == NULL)
+ goto error;
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, generatorBuf, (size_t)genLen))
+ goto error;
+
+ orderBn = BN_bin2bn(order, orderLength, NULL);
+ cofactorBn = BN_bin2bn(cofactor, cofactorLength, NULL);
+
+ if (!orderBn || !cofactorBn)
+ 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 (hasPublicKey)
+ {
+ int32_t pubKeyLen;
+
+ pubKeyBuf = EncodeEcPointFromCoordinates(qx, qxLength, qy, qyLength, fieldSize, &pubKeyLen);
+ if (pubKeyBuf == NULL)
+ goto error;
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, (size_t)pubKeyLen))
+ goto error;
+ }
+ else if (hasPrivateKey)
+ {
+ // No public key provided, derive Q = d * G from the explicit curve parameters.
+#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);
+ }
+
+ if (group == NULL)
+ goto error;
+
+ // Set the generator
+ 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;
+
+ if (!EC_GROUP_set_generator(group, G, orderBn, cofactorBn))
+ goto error;
+
+ // Derive Q = d * G
+ pubPoint = EC_POINT_new(group);
+ if (pubPoint == NULL ||
+ !EC_POINT_mul(group, pubPoint, dBn, NULL, NULL, NULL))
+ goto error;
+
+ size_t pubKeyLen;
+ pubKeyBuf = EncodeEcPointFromPoint(group, pubPoint, &pubKeyLen);
+ if (pubKeyBuf == NULL)
+ goto error;
+
+ if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pubKeyBuf, pubKeyLen))
+ 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;
+ }
+
+ goto exit;
+
+error:
+ 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 (pubPoint) EC_POINT_free(pubPoint);
+ if (group) EC_GROUP_free(group);
+ return pkey;
+#else
+ (void)curveType;
+ (void)qx; (void)qxLength; (void)qy; (void)qyLength;
+ (void)d; (void)dLength;
+ (void)p; (void)pLength; (void)a; (void)aLength; (void)b; (void)bLength;
+ (void)gx; (void)gxLength; (void)gy; (void)gyLength;
+ (void)order; (void)orderLength; (void)cofactor; (void)cofactorLength;
+ (void)seed; (void)seedLength;
+ return NULL;
+#endif
+}
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..1353cfe93b5739 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
@@ -89,6 +89,58 @@ PALEXPORT EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters(
uint8_t* cofactor, int32_t hLength,
uint8_t* seed, int32_t sLength);
+/*
+Generates a new EC key pair for a named curve using EVP_PKEY APIs.
+Returns 1 upon success, -1 if oid was not found, otherwise 0.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyGenerateByEcKeyOid(
+ EVP_PKEY** pkey,
+ const char* oid);
+
+/*
+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(const EVP_PKEY* pkey);
+
+/*
+Returns the field degree (number of bits) of the EC group for the given EVP_PKEY.
+For prime fields this is BN_num_bits(p), for binary fields it is BN_num_bits(polynomial) - 1.
+Returns 0 on failure.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyGetEcFieldDegree(const EVP_PKEY* pkey);
+
+/*
+Creates a new EVP_PKEY for a named EC curve using the provided key parameters.
+qx/qy are the public key coordinates, d is the optional private key.
+Returns 1 upon success, -1 if oid was not found, otherwise 0.
+*/
+PALEXPORT int32_t CryptoNative_EvpPKeyCreateByEcKeyParameters(
+ 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);
+
+/*
+Creates a new EVP_PKEY for an EC key with explicit curve parameters.
+Returns the new EVP_PKEY instance, or NULL on failure.
+*/
+PALEXPORT EVP_PKEY* 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);
+
/*
Returns the ECC curve parameters of the given EVP_PKEY.
*/