"/src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs",
"/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1",
"/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs",
+ "/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs",
"/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs",
"/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs",
"/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs",
private static extern int EcKeyCreateByKeyParameters(
out SafeEcKeyHandle key,
string oid,
- byte[] qx, int qxLength,
- byte[] qy, int qyLength,
+ byte[]? qx, int qxLength,
+ byte[]? qy, int qyLength,
byte[]? d, int dLength);
internal static SafeEcKeyHandle EcKeyCreateByKeyParameters(
string oid,
- byte[] qx, int qxLength,
- byte[] qy, int qyLength,
+ byte[]? qx, int qxLength,
+ byte[]? qy, int qyLength,
byte[]? d, int dLength)
{
SafeEcKeyHandle key;
}
bytesRead = len;
- return ImportPkcs8(source.Slice(0, len));
+ ReadOnlySpan<byte> pkcs8Source = source.Slice(0, len);
+
+ try
+ {
+ return ImportPkcs8(pkcs8Source);
+ }
+ catch (CryptographicException)
+ {
+ AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(pkcs8Source);
+
+ if (pkcs8ZeroPublicKey == null)
+ {
+ throw;
+ }
+
+ return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan());
+ }
}
internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey(
}
catch (CryptographicException e)
{
- throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan);
+
+ if (pkcs8ZeroPublicKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ }
+
+ try
+ {
+ return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan());
+ }
+ catch (CryptographicException)
+ {
+ throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ }
}
finally
{
}
catch (CryptographicException e)
{
- throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ AsnWriter? pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan);
+
+ if (pkcs8ZeroPublicKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ }
+
+ try
+ {
+ bytesRead = len;
+ return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan());
+ }
+ catch (CryptographicException)
+ {
+ throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
+ }
}
finally
{
}
}
+ // CNG cannot import a PrivateKeyInfo with the following criteria:
+ // 1. Is a EC key with explicitly encoded parameters
+ // 2. Is missing the PublicKey from ECPrivateKey.
+ // CNG can import an explicit EC PrivateKeyInfo if the PublicKey
+ // is present. CNG will also re-compute the public key from the
+ // private key if they do not much. To help CNG be able to import
+ // these keys, we re-write the PKCS8 to contain a zeroed PublicKey.
+ //
+ // If the PKCS8 key does not meet the above criteria, null is returned,
+ // signaling the original exception should be thrown.
+ private static unsafe AsnWriter? RewritePkcs8ECPrivateKeyWithZeroPublicKey(ReadOnlySpan<byte> source)
+ {
+ fixed (byte* ptr = &MemoryMarshal.GetReference(source))
+ {
+ using (MemoryManager<byte> manager = new PointerMemoryManager<byte>(ptr, source.Length))
+ {
+ PrivateKeyInfoAsn privateKeyInfo = PrivateKeyInfoAsn.Decode(manager.Memory, AsnEncodingRules.BER);
+ AlgorithmIdentifierAsn privateAlgorithm = privateKeyInfo.PrivateKeyAlgorithm;
+
+ if (privateAlgorithm.Algorithm.Value != Oids.EcPublicKey)
+ {
+ return null;
+ }
+
+ ECPrivateKey privateKey = ECPrivateKey.Decode(privateKeyInfo.PrivateKey, AsnEncodingRules.BER);
+ EccKeyFormatHelper.FromECPrivateKey(privateKey, privateAlgorithm, out ECParameters ecParameters);
+
+ fixed (byte* pD = ecParameters.D)
+ {
+ try
+ {
+ if (!ecParameters.Curve.IsExplicit || ecParameters.Q.X != null || ecParameters.Q.Y != null)
+ {
+ return null;
+ }
+
+ byte[] zero = new byte[ecParameters.D!.Length];
+ ecParameters.Q.Y = zero;
+ ecParameters.Q.X = zero;
+ return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters, privateKeyInfo.Attributes);
+ }
+ finally
+ {
+ Array.Clear(ecParameters.D!, 0, ecParameters.D!.Length);
+ }
+ }
+ }
+ }
+ }
+
private static void FillRandomAsciiString(Span<char> destination)
{
Debug.Assert(destination.Length < 128);
ThrowIfDisposed();
ECCurve curve = parameters.Curve;
- bool includePrivateParamerters = (parameters.D != null);
+ bool includePrivateParameters = parameters.D != null;
+ bool hasPublicParameters = parameters.Q.X != null && parameters.Q.Y != null;
if (curve.IsPrime)
{
- byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref parameters, ecdh: true);
- ImportFullKeyBlob(ecExplicitBlob, includePrivateParamerters);
+ if (!hasPublicParameters && includePrivateParameters)
+ {
+ byte[] zero = new byte[parameters.D!.Length];
+ ECParameters ecParamsCopy = parameters;
+ ecParamsCopy.Q.X = zero;
+ ecParamsCopy.Q.Y = zero;
+ byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref ecParamsCopy, ecdh: true);
+ ImportFullKeyBlob(ecExplicitBlob, includePrivateParameters: true);
+ }
+ else
+ {
+ byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref parameters, ecdh: true);
+ ImportFullKeyBlob(ecExplicitBlob, includePrivateParameters);
+ }
}
else if (curve.IsNamed)
{
SR.Format(SR.Cryptography_InvalidCurveOid, curve.Oid.Value));
}
- byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref parameters, ecdh: true);
- ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParamerters);
+ if (!hasPublicParameters && includePrivateParameters)
+ {
+ byte[] zero = new byte[parameters.D!.Length];
+ ECParameters ecParamsCopy = parameters;
+ ecParamsCopy.Q.X = zero;
+ ecParamsCopy.Q.Y = zero;
+ byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref ecParamsCopy, ecdh: true);
+ ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParameters: true);
+ }
+ else
+ {
+ byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref parameters, ecdh: true);
+ ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParameters);
+ }
}
else
{
ThrowIfDisposed();
ECCurve curve = parameters.Curve;
- bool includePrivateParameters = (parameters.D != null);
+ bool includePrivateParameters = parameters.D != null;
+ bool hasPublicParameters = parameters.Q.X != null && parameters.Q.Y != null;
if (curve.IsPrime)
{
- byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref parameters, ecdh: false);
- ImportFullKeyBlob(ecExplicitBlob, includePrivateParameters);
+ if (!hasPublicParameters && includePrivateParameters)
+ {
+ byte[] zero = new byte[parameters.D!.Length];
+ ECParameters ecParamsCopy = parameters;
+ ecParamsCopy.Q.X = zero;
+ ecParamsCopy.Q.Y = zero;
+ byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref ecParamsCopy, ecdh: false);
+ ImportFullKeyBlob(ecExplicitBlob, includePrivateParameters: true);
+ }
+ else
+ {
+ byte[] ecExplicitBlob = ECCng.GetPrimeCurveBlob(ref parameters, ecdh: false);
+ ImportFullKeyBlob(ecExplicitBlob, includePrivateParameters);
+ }
}
else if (curve.IsNamed)
{
if (string.IsNullOrEmpty(curve.Oid.FriendlyName))
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_InvalidCurveOid, curve.Oid.Value!.ToString()));
- byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref parameters, ecdh: false);
- ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParameters);
+ if (!hasPublicParameters && includePrivateParameters)
+ {
+ byte[] zero = new byte[parameters.D!.Length];
+ ECParameters ecParamsCopy = parameters;
+ ecParamsCopy.Q.X = zero;
+ ecParamsCopy.Q.Y = zero;
+ byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref ecParamsCopy, ecdh: false);
+ ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParameters: true);
+ }
+ else
+ {
+ byte[] ecNamedCurveBlob = ECCng.GetNamedCurveBlob(ref parameters, ecdh: false);
+ ImportKeyBlob(ecNamedCurveBlob, curve.Oid.FriendlyName, includePrivateParameters);
+ }
}
else
{
SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByKeyParameters(
oid,
- parameters.Q.X!, parameters.Q.X!.Length,
- parameters.Q.Y!, parameters.Q.Y!.Length,
+ 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;
Debug.Assert(parameters.Curve.IsPrime);
SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
parameters.Curve.CurveType,
- parameters.Q.X, parameters.Q.X!.Length,
- parameters.Q.Y, parameters.Q.Y!.Length,
+ 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,
Debug.Assert(parameters.Curve.IsCharacteristic2);
SafeEcKeyHandle key = Interop.Crypto.EcKeyCreateByExplicitParameters(
parameters.Curve.CurveType,
- parameters.Q.X, parameters.Q.X!.Length,
- parameters.Q.Y, parameters.Q.Y!.Length,
+ 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,
using System.Buffers;
using System.Collections;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security.Cryptography.Asn1;
out ECParameters ret)
{
ECPrivateKey key = ECPrivateKey.Decode(keyData, AsnEncodingRules.BER);
+ FromECPrivateKey(key, algId, out ret);
+ }
+ internal static void FromECPrivateKey(
+ ECPrivateKey key,
+ in AlgorithmIdentifierAsn algId,
+ out ECParameters ret)
+ {
ValidateParameters(key.Parameters, algId);
if (key.Version != 1)
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
- // Implementation limitation
- if (key.PublicKey == null)
+ byte[]? x = null;
+ byte[]? y = null;
+
+ if (key.PublicKey != null)
{
- throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
- }
+ ReadOnlySpan<byte> publicKeyBytes = key.PublicKey.Value.Span;
- ReadOnlySpan<byte> publicKeyBytes = key.PublicKey.Value.Span;
+ if (publicKeyBytes.Length == 0)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
- if (publicKeyBytes.Length == 0)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
- }
+ // Implementation limitation
+ // 04 (Uncompressed ECPoint) is almost always used.
+ if (publicKeyBytes[0] != 0x04)
+ {
+ throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
+ }
- // Implementation limitation
- // 04 (Uncompressed ECPoint) is almost always used.
- if (publicKeyBytes[0] != 0x04)
- {
- throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
- }
+ // https://www.secg.org/sec1-v2.pdf, 2.3.4, #3 (M has length 2 * CEIL(log2(q)/8) + 1)
+ if (publicKeyBytes.Length != 2 * key.PrivateKey.Length + 1)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
- // https://www.secg.org/sec1-v2.pdf, 2.3.4, #3 (M has length 2 * CEIL(log2(q)/8) + 1)
- if (publicKeyBytes.Length != 2 * key.PrivateKey.Length + 1)
- {
- throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ x = publicKeyBytes.Slice(1, key.PrivateKey.Length).ToArray();
+ y = publicKeyBytes.Slice(1 + key.PrivateKey.Length).ToArray();
}
ECDomainParameters domainParameters;
domainParameters = ECDomainParameters.Decode(algId.Parameters!.Value, AsnEncodingRules.DER);
}
+ Debug.Assert((x == null) == (y == null));
+
ret = new ECParameters
{
Curve = GetCurve(domainParameters),
Q =
{
- X = publicKeyBytes.Slice(1, key.PrivateKey.Length).ToArray(),
- Y = publicKeyBytes.Slice(1 + key.PrivateKey.Length).ToArray(),
+ X = x,
+ Y = y,
},
D = key.PrivateKey.ToArray(),
};
writer.PopSequence();
}
- internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters)
+ internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters, AttributeAsn[]? attributes = null)
{
ecParameters.Validate();
// Don't need the domain parameters because they're contained in the algId.
using (AsnWriter ecPrivateKey = WriteEcPrivateKey(ecParameters, includeDomainParameters: false))
using (AsnWriter algorithmIdentifier = WriteAlgorithmIdentifier(ecParameters))
+ using (AsnWriter? attributeWriter = WritePrivateKeyInfoAttributes(attributes))
{
- return KeyFormatHelper.WritePkcs8(algorithmIdentifier, ecPrivateKey);
+ return KeyFormatHelper.WritePkcs8(algorithmIdentifier, ecPrivateKey, attributeWriter);
}
}
+ [return: NotNullIfNotNull("attributes")]
+ private static AsnWriter? WritePrivateKeyInfoAttributes(AttributeAsn[]? attributes)
+ {
+ if (attributes == null)
+ return null;
+
+ AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
+ Asn1Tag tag = new Asn1Tag(TagClass.ContextSpecific, 0);
+ writer.PushSetOf(tag);
+
+ for (int i = 0; i < attributes.Length; i++)
+ {
+ attributes[i].Encode(writer);
+ }
+
+ writer.PopSetOf(tag);
+ return writer;
+ }
+
private static void WriteEcParameters(ECParameters ecParameters, AsnWriter writer)
{
if (ecParameters.Curve.IsNamed)
}
// publicKey
+ if (ecParameters.Q.X != null)
{
+ Debug.Assert(ecParameters.Q.Y != null);
Asn1Tag explicit1 = new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true);
writer.PushSequence(explicit1);
}
}
- internal static AsnWriter WritePkcs8(AsnWriter algorithmIdentifierWriter, AsnWriter privateKeyWriter)
+ internal static AsnWriter WritePkcs8(
+ AsnWriter algorithmIdentifierWriter,
+ AsnWriter privateKeyWriter,
+ AsnWriter? attributesWriter = null)
{
- // Ensure both input writers are balanced.
+ // Ensure both algorithm identifier and key writers are balanced.
ReadOnlySpan<byte> algorithmIdentifier = algorithmIdentifierWriter.EncodeAsSpan();
ReadOnlySpan<byte> privateKey = privateKeyWriter.EncodeAsSpan();
// PKI.privateKey
writer.WriteOctetString(privateKey);
- // We don't currently accept attributes, so... done.
+ // PKI.Attributes
+ if (attributesWriter != null)
+ {
+ ReadOnlySpan<byte> attributes = attributesWriter.EncodeAsSpan();
+ writer.WriteEncodedValue(attributes);
+ }
+
writer.PopSequence();
return writer;
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+using Test.Cryptography;
+using Xunit;
+
+namespace System.Security.Cryptography.Tests
+{
+ public abstract partial class ECKeyFileTests<T>
+ {
+ private static bool LimitedPrivateKeySupported { get; } = EcDiffieHellman.Tests.ECDiffieHellmanFactory.LimitedPrivateKeySupported;
+ private const int NTE_PERM = unchecked((int)0x80090010);
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public void ReadWriteNistP256_PreservesKeyUsage_Explicit_LimitedPrivate()
+ {
+ if (!LimitedPrivateKeySupported || !SupportsExplicitCurves)
+ {
+ return;
+ }
+
+ // This key has a keyUsage set to 0b00000000 (no key usages are valid).
+ // Since the CNG PKCS8 import will re-write these keys with Q=(0,0)
+ // in the PrivateKeyInfo, we want to make sure that the Attributes
+ // are kept.
+ const string base64 = @"
+MIIBQgIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB
+AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA
+///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV
+AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg
+9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A
+AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBCcwJQIBAQQgcKEsLbFoRe1W
+/2jPwhpHKz8E19aFG/Y0ny19WzRSs4qgDTALBgNVHQ8xBAMCAAA=";
+
+ T key = CreateKey();
+ key.ImportPkcs8PrivateKey(Convert.FromBase64String(base64), out _);
+ CryptographicException ex = Assert.ThrowsAny<CryptographicException>(() => Exercise(key));
+ Assert.Equal(NTE_PERM, ex.HResult);
+ }
+
+ [Fact]
+ public void ReadWriteNistP521Pkcs8_LimitedPrivate()
+ {
+ const string base64 = @"
+MGACAQAwEAYHKoZIzj0CAQYFK4EEACMESTBHAgEBBEIBpV+HhaVzC67h1rPTAQaf
+f9ZNiwTM6lfv1ZYeaPM/q0NUUWbKZVPNOP9xPRKJxpi9fQhrVeAbW9XtJ+NjA3ax
+FmY=";
+
+ ReadWriteBase64Pkcs8(base64, EccTestData.GetNistP521Key2(), LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadNistP521EncryptedPkcs8_Pbes2_Aes128_LimitedPrivateKey()
+ {
+ const string base64 = @"
+MIHLMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiS8R2OYS+H4wICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEB8zZ4/4VXlh4WPKYssZeNEEcBsA
+EHOyooViqm3L/Zn04q+v1yzY+OvegfeTDpvSHCepckKEYklMB2K/O47PlH+jojKo
+TpRPFq9qLqOb+SrZVk4Ubljzr0u3pkpnJXczE+wGyATXgF1kfPTDKZR9qk5vaeAj
+PFzVQfJ396S+yx4IIC4=";
+
+ ReadWriteBase64EncryptedPkcs8(
+ base64,
+ "qwerty",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.TripleDes3KeyPkcs12,
+ HashAlgorithmName.SHA1,
+ 12321),
+ EccTestData.GetNistP521Key2(),
+ LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadNistP521EncryptedPkcs8_Pbes2_Aes128_LimitedPrivateKey_PasswordBytes()
+ {
+ const string base64 = @"
+MIHLMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiS8R2OYS+H4wICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEB8zZ4/4VXlh4WPKYssZeNEEcBsA
+EHOyooViqm3L/Zn04q+v1yzY+OvegfeTDpvSHCepckKEYklMB2K/O47PlH+jojKo
+TpRPFq9qLqOb+SrZVk4Ubljzr0u3pkpnJXczE+wGyATXgF1kfPTDKZR9qk5vaeAj
+PFzVQfJ396S+yx4IIC4=";
+
+ ReadWriteBase64EncryptedPkcs8(
+ base64,
+ Encoding.UTF8.GetBytes("qwerty"),
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes256Cbc,
+ HashAlgorithmName.SHA1,
+ 12321),
+ EccTestData.GetNistP521Key2(),
+ LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteNistP256ECPrivateKey_LimitedPrivateKey()
+ {
+ const string base64 = @"
+MDECAQEEIHChLC2xaEXtVv9oz8IaRys/BNfWhRv2NJ8tfVs0UrOKoAoGCCqGSM49
+AwEH";
+
+ ReadWriteBase64ECPrivateKey(
+ base64,
+ EccTestData.GetNistP256ReferenceKey(),
+ LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteNistP256ExplicitECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ @"
+MIIBIgIBAQQgcKEsLbFoRe1W/2jPwhpHKz8E19aFG/Y0ny19WzRSs4qggfowgfcC
+AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////
+MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr
+vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE
+axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W
+K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8
+YyVRAgEB",
+ EccTestData.GetNistP256ReferenceKeyExplicit(),
+ LimitedPrivateKeySupported && SupportsExplicitCurves);
+ }
+
+ [Fact]
+ public void ReadWriteNistP256ExplicitPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MIIBMwIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB
+AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA
+///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV
+AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg
+9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A
+AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBCcwJQIBAQQgcKEsLbFoRe1W
+/2jPwhpHKz8E19aFG/Y0ny19WzRSs4o=",
+ EccTestData.GetNistP256ReferenceKeyExplicit(),
+ LimitedPrivateKeySupported && SupportsExplicitCurves);
+ }
+
+ [Fact]
+ public void ReadWriteNistP256ExplicitEncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIIBnTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIS4D9Fbzp0gQCAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBNE0X1G2z4D96fhP/t6xc1BIIB
+QLKzXdUbVqnjlzUS7HQPTmgfxQkvieRm92ot4nTbEztKelQ3M9ijA4ToTaWz4crM
+RM4VTFzSAk6c3IIYzc5aFe33r76ootud+YnkKLMtT+zrQOxhYV4vT/dVsfqPaTjk
+yBN/spLA/AAetSqqxkG3jLvh3TSx/9ymLVRp10748aNMBK7136V0lOBT9VmJLD/R
+rtJTh6Lgx8JIAJpyR7Omjb6uaf0/QInS3bWOEnTHt2kRba4GEahQ/Fw8zDwuBX9V
+U4vrY201zbeyqVRsabSaru/xQwDUHA++FmiJuY8p0T3y7u0pKtPkdGTBnYjWqcDc
+BSJFRM1hEoL4pr7fCtb4mdnEoWGIG6O7SYr92M3TAxFcYEEMSUJi7TxEAmPAKpYe
+hjy6jYfLa1BCJhvq+WbNc7zEb2MfXVhnImaG+XTqXI0c",
+ "test",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes128Cbc,
+ HashAlgorithmName.SHA256,
+ 1234),
+ EccTestData.GetNistP256ReferenceKeyExplicit(),
+ LimitedPrivateKeySupported && SupportsExplicitCurves);
+ }
+
+ [Fact]
+ public void ReadWriteBrainpoolKey1ECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ "MCYCAQEEFMXZRFR94RXbJYjcb966O0c+nE2WoAsGCSskAwMCCAEBAQ==",
+ EccTestData.BrainpoolP160r1Key1,
+ SupportsBrainpool && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteBrainpoolKey1Pkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MDYCAQAwFAYHKoZIzj0CAQYJKyQDAwIIAQEBBBswGQIBAQQUxdlEVH3hFdsliNxv
+3ro7Rz6cTZY=",
+ EccTestData.BrainpoolP160r1Key1,
+ SupportsBrainpool && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteBrainpoolKey1EncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAibpes/q40kbQICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEKU1rOHbrpBkttHYwlM7e8gEQBNB
+7CJfOdSzyntp2X212/dU3Tu6pa1BEh6hdfljYPnBNRbrSFjzavRhjUoOOEzLgaqr
+heDtThcoFBJUsNhEHrc=",
+ "chicken",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes192Cbc,
+ HashAlgorithmName.SHA384,
+ 4096),
+ EccTestData.BrainpoolP160r1Key1,
+ SupportsBrainpool && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1ECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ "MCMCAQEEFQPBmVrfrowFGNwT3+YwS7AQF+akEqAHBgUrgQQAAQ==",
+ EccTestData.Sect163k1Key1,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1Pkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MDMCAQAwEAYHKoZIzj0CAQYFK4EEAAEEHDAaAgEBBBUDwZla366MBRjcE9/mMEuw
+EBfmpBI=",
+ EccTestData.Sect163k1Key1,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1ExplicitECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ @"
+MIHBAgEBBBUDwZla366MBRjcE9/mMEuwEBfmpBKggaQwgaECAQEwJQYHKoZIzj0B
+AjAaAgIAowYJKoZIzj0BAgMDMAkCAQMCAQYCAQcwLgQVAAAAAAAAAAAAAAAAAAAA
+AAAAAAABBBUAAAAAAAAAAAAAAAAAAAAAAAAAAAEEKwQC/hPAU3u8EayqB9eT3k5t
+XlyU7ugCiQcPsF04/1gyHy6ABTbVOMzao9kCFQQAAAAAAAAAAAACAQii4MwNmfil
+7wIBAg==",
+ EccTestData.Sect163k1Key1Explicit,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1ExplicitPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MIHRAgEAMIGtBgcqhkjOPQIBMIGhAgEBMCUGByqGSM49AQIwGgICAKMGCSqGSM49
+AQIDAzAJAgEDAgEGAgEHMC4EFQAAAAAAAAAAAAAAAAAAAAAAAAAAAQQVAAAAAAAA
+AAAAAAAAAAAAAAAAAAABBCsEAv4TwFN7vBGsqgfXk95ObV5clO7oAokHD7BdOP9Y
+Mh8ugAU21TjM2qPZAhUEAAAAAAAAAAAAAgEIouDMDZn4pe8CAQIEHDAaAgEBBBUD
+wZla366MBRjcE9/mMEuwEBfmpBI=",
+ EccTestData.Sect163k1Key1Explicit,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1EncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAihxqVEJNIIvgICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEENKfCUCiZgnSk3NJ1fYNsfsEQEiv
+8tmNavm0fpTJFrAikkaj4BOwz87uce+AoMHaI9kH0dHR4oX5L4euffHY9NwYjywd
+2OTmoam/Bux6qv2V1vM=",
+ "dinner",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes256Cbc,
+ HashAlgorithmName.SHA256,
+ 7),
+ EccTestData.Sect163k1Key1,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect163k1Key1ExplicitEncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIIBPDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIY8iZ0ZLe8O8CAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBB+R0cFaFSqsTlu68p1La4yBIHg
+NU0YrkKbg2TyKi62Uh410kgwE/IHqbfoeQZl9P7MDIrah1hR9yk6DTeJE8WRI2BX
++X5cInMazbVLOIO//WTY90MKq/PE9eJ3jch1VGI2VfHh2V5u/uwJT3z1d4fXTpXc
+2iP7btbXJhougcGiOtWMQrZtNdAi4OwIgnW1f4VkIWEf0TUjiC7A74AdgMwnu04u
+d4sHylN7CUBYGVAtZ7fHwK0CsyggK/7/IoexhoaTUvzXi3xS8rEjY+5w8OcweCnr
+RVA9DXUNz5+yUlfGzgErHYGwRLaLCACU6+WAC34Kkyk=",
+ "test",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes256Cbc,
+ HashAlgorithmName.SHA256,
+ 7),
+ EccTestData.Sect163k1Key1Explicit,
+ SupportsSect163k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect283k1Key1ECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ @"
+MDICAQEEJAC08a4ef9zUsOggU8CKkIhSsmIx5sAWcPzGw+osXT/tQO3wN6AHBgUr
+gQQAEA==",
+ EccTestData.Sect283k1Key1,
+ SupportsSect283k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1ExplicitECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ @"
+MIHYAgEBBBUA9NJKFAcSL0RZZ74dk8AJOmU2eYaggbswgbgCAQEwJQYHKoZIzj0B
+AjAaAgIAowYJKoZIzj0BAgMDMAkCAQECAQICAQgwRQQVByVGtUNSNKQi4HiWdfQy
+yJQ13lJCBBUAyVF9BtUkDTz/OMdLILbNTW+d1NkDFQDSwPsVdghg3vHu9NaW5naH
+VhUXVAQrBAevaZiVRhA9eTKfzD10iA8zu+gDywHsIyEbWWat6h0/h/fqWEiu8LfK
+nwIVBAAAAAAAAAAAAAHmD8iCHMdNrq/BAgEC",
+ EccTestData.C2pnb163v1Key1Explicit,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1ExplicitPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MIHoAgEAMIHEBgcqhkjOPQIBMIG4AgEBMCUGByqGSM49AQIwGgICAKMGCSqGSM49
+AQIDAzAJAgEBAgECAgEIMEUEFQclRrVDUjSkIuB4lnX0MsiUNd5SQgQVAMlRfQbV
+JA08/zjHSyC2zU1vndTZAxUA0sD7FXYIYN7x7vTWluZ2h1YVF1QEKwQHr2mYlUYQ
+PXkyn8w9dIgPM7voA8sB7CMhG1lmreodP4f36lhIrvC3yp8CFQQAAAAAAAAAAAAB
+5g/IghzHTa6vwQIBAgQcMBoCAQEEFQD00koUBxIvRFlnvh2TwAk6ZTZ5hg==",
+ EccTestData.C2pnb163v1Key1Explicit,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1ExplicitEncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIIBTDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIvcAOWkixD/4CAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCx4zH4H0Pf9XGdJMtik+XVBIHw
+y5JKEMkohGZgjTHkXUs9hSq9JtyJzz8VcSXpid7NkRXFAtEEcO1yIs2xUVxlPER7
+4loKRPmPR9GKCeTEsoUyQH9T+X6r0nKqvuoWq5iU8w3ZGrQ8FUBsODMdCAlmfJau
+cIB+jp8kGPDQckBBp+R4i2qPYRSKzANEHegDeu9s24IQk2+B3b5uqynkVJa2z+Dp
+fyL21cPvHEx04p39oKmWh7S5M6FjHAu/9eGHQtiJ/QKisMgE1ICf+OmO6nfFhNnZ
+AerBJbccwFJfDAXP+eW3qWtaMgulL0gUYZQ7FcXH+z5CAWwdarLOCDZGqvQFtZ16",
+ "meow",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes256Cbc,
+ HashAlgorithmName.SHA256,
+ 7),
+ EccTestData.C2pnb163v1Key1Explicit,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect283k1Key1Pkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MEICAQAwEAYHKoZIzj0CAQYFK4EEABAEKzApAgEBBCQAtPGuHn/c1LDoIFPAipCI
+UrJiMebAFnD8xsPqLF0/7UDt8Dc=",
+ EccTestData.Sect283k1Key1,
+ SupportsSect283k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteSect283k1Key1EncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIGrMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAjzxZBMGbGUIQICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEAkgh22WW899Po2QL5+Yz4gEUKHh
+/hrl7Ia0jUr5dJ++pEOwWgpdvn8zV+6pt2d0w8D3DAJaJNEqgpaqH6uHS/tYJxWS
+vW82QOEXDhi1gO24nhx2gUeqVTHjhFq14blAu5l5",
+ "Enter PEM pass phrase",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes192Cbc,
+ HashAlgorithmName.SHA384,
+ 4096),
+ EccTestData.Sect283k1Key1,
+ SupportsSect283k1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1ECPrivateKey_LimitedPrivate()
+ {
+ ReadWriteBase64ECPrivateKey(
+ "MCYCAQEEFQD00koUBxIvRFlnvh2TwAk6ZTZ5hqAKBggqhkjOPQMAAQ==",
+ EccTestData.C2pnb163v1Key1,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1Pkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64Pkcs8(
+ @"
+MDYCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAAEEHDAaAgEBBBUA9NJKFAcSL0RZZ74d
+k8AJOmU2eYY=",
+ EccTestData.C2pnb163v1Key1,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+
+ [Fact]
+ public void ReadWriteC2pnb163v1EncryptedPkcs8_LimitedPrivate()
+ {
+ ReadWriteBase64EncryptedPkcs8(
+ @"
+MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhXAZB3O0dcawICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEKWBssmLHI618uBvF0PA4VoEQIDy
+4luj/sC8xYPCCDX8YQ6ppmkq+5aBw9Rwxrp/1wsrkDUhrU1wCN3eV1sFu+OCEdzQ
+1N8AhXsRbbNjXWKX25U=",
+ "sleepy",
+ new PbeParameters(
+ PbeEncryptionAlgorithm.Aes192Cbc,
+ HashAlgorithmName.SHA512,
+ 1024),
+ EccTestData.C2pnb163v1Key1,
+ SupportsC2pnb163v1 && LimitedPrivateKeySupported);
+ }
+ }
+}
namespace System.Security.Cryptography.Tests
{
- public abstract class ECKeyFileTests<T> where T : AsymmetricAlgorithm
+ public abstract partial class ECKeyFileTests<T> where T : AsymmetricAlgorithm
{
protected abstract T CreateKey();
protected abstract byte[] ExportECPrivateKey(T key);
protected abstract void ImportECPrivateKey(T key, ReadOnlySpan<byte> source, out int bytesRead);
protected abstract void ImportParameters(T key, ECParameters ecParameters);
protected abstract ECParameters ExportParameters(T key, bool includePrivate);
+ protected abstract void Exercise(T key);
public static bool SupportsBrainpool { get; } = IsCurveSupported(ECCurve.NamedCurves.brainpoolP160r1.Oid);
public static bool SupportsSect163k1 { get; } = IsCurveSupported(EccTestData.Sect163k1Key1.Curve.Oid);
new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc,
HashAlgorithmName.SHA256,
- 7),
+ 7),
EccTestData.Sect163k1Key1,
SupportsSect163k1);
}
new PbeParameters(
PbeEncryptionAlgorithm.Aes128Cbc,
HashAlgorithmName.SHA256,
- 12),
+ 12),
EccTestData.Sect163k1Key1Explicit,
SupportsSect163k1);
}
{
return key.ExportParameters(includePrivate);
}
+
+ protected override void Exercise(ECDiffieHellman key) => key.Exercise();
}
}
#endif
bool IsCurveValid(Oid oid);
bool ExplicitCurvesSupported { get; }
+ bool LimitedPrivateKeySupported { get; }
}
public static partial class ECDiffieHellmanFactory
}
public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported;
+ public static bool LimitedPrivateKeySupported => s_provider.LimitedPrivateKeySupported;
}
}
}
}
+ [Fact]
+ public static void ImportFromPrivateOnlyKey()
+ {
+ if (!ECDiffieHellmanFactory.LimitedPrivateKeySupported)
+ return;
+
+ byte[] expectedX = "00d45615ed5d37fde699610a62cd43ba76bedd8f85ed31005fe00d6450fbbd101291abd96d4945a8b57bc73b3fe9f4671105309ec9b6879d0551d930dac8ba45d255".HexToByteArray();
+ byte[] expectedY = "01425332844e592b440c0027972ad1526431c06732df19cd46a242172d4dd67c2c8c99dfc22e49949a56cf90c6473635ce82f25b33682fb19bc33bd910ed8ce3a7fa".HexToByteArray();
+
+ ECParameters limitedPrivateParameters = new ECParameters
+ {
+ Curve = ECCurve.NamedCurves.nistP521,
+ Q = default,
+ D = "00816f19c1fb10ef94d4a1d81c156ec3d1de08b66761f03f06ee4bb9dcebbbfe1eaa1ed49a6a990838d8ed318c14d74cc872f95d05d07ad50f621ceb620cd905cfb8".HexToByteArray(),
+ };
+
+ using (ECDiffieHellman ecdh = ECDiffieHellmanFactory.Create())
+ {
+ ecdh.ImportParameters(limitedPrivateParameters);
+ ECParameters exportedParameters = ecdh.ExportParameters(true);
+
+ Assert.Equal(expectedX, exportedParameters.Q.X);
+ Assert.Equal(expectedY, exportedParameters.Q.Y);
+ Assert.Equal(limitedPrivateParameters.D, exportedParameters.D);
+ }
+ }
+
private static void VerifyNamedCurve(ECParameters parameters, ECDiffieHellman ec, int keySize, bool includePrivate)
{
parameters.Validate();
#endif
bool IsCurveValid(Oid oid);
bool ExplicitCurvesSupported { get; }
+ bool LimitedPrivateKeySupported { get; }
}
public static partial class ECDsaFactory
}
public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported;
+ public static bool LimitedPrivateKeySupported => s_provider.LimitedPrivateKeySupported;
}
}
}
}
+ [Fact]
+ public static void ImportFromPrivateOnlyKey()
+ {
+ if (!ECDsaFactory.LimitedPrivateKeySupported)
+ return;
+
+ byte[] expectedX = "00d45615ed5d37fde699610a62cd43ba76bedd8f85ed31005fe00d6450fbbd101291abd96d4945a8b57bc73b3fe9f4671105309ec9b6879d0551d930dac8ba45d255".HexToByteArray();
+ byte[] expectedY = "01425332844e592b440c0027972ad1526431c06732df19cd46a242172d4dd67c2c8c99dfc22e49949a56cf90c6473635ce82f25b33682fb19bc33bd910ed8ce3a7fa".HexToByteArray();
+
+ ECParameters limitedPrivateParameters = new ECParameters
+ {
+ Curve = ECCurve.NamedCurves.nistP521,
+ Q = default,
+ D = "00816f19c1fb10ef94d4a1d81c156ec3d1de08b66761f03f06ee4bb9dcebbbfe1eaa1ed49a6a990838d8ed318c14d74cc872f95d05d07ad50f621ceb620cd905cfb8".HexToByteArray(),
+ };
+
+ using (ECDsa ecdsa = ECDsaFactory.Create())
+ {
+ ecdsa.ImportParameters(limitedPrivateParameters);
+ ECParameters exportedParameters = ecdsa.ExportParameters(true);
+
+ Assert.Equal(expectedX, exportedParameters.Q.X);
+ Assert.Equal(expectedY, exportedParameters.Q.Y);
+ Assert.Equal(limitedPrivateParameters.D, exportedParameters.D);
+ }
+ }
+
private static void VerifyNamedCurve(ECParameters parameters, ECDsa ec, int keySize, bool includePrivate)
{
parameters.Validate();
{
return key.ExportParameters(includePrivate);
}
+
+ protected override void Exercise(ECDsa key) => key.Exercise();
}
}
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) \
REQUIRED_FUNCTION(EC_POINT_free) \
REQUIRED_FUNCTION(EC_POINT_get_affine_coordinates_GFp) \
+ REQUIRED_FUNCTION(EC_POINT_mul) \
REQUIRED_FUNCTION(EC_POINT_new) \
REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates_GFp) \
REQUIRED_FUNCTION(ERR_clear_error) \
#define EC_KEY_new_by_curve_name EC_KEY_new_by_curve_name_ptr
#define EC_KEY_set_group EC_KEY_set_group_ptr
#define EC_KEY_set_private_key EC_KEY_set_private_key_ptr
+#define EC_KEY_set_public_key EC_KEY_set_public_key_ptr
#define EC_KEY_set_public_key_affine_coordinates EC_KEY_set_public_key_affine_coordinates_ptr
#define EC_KEY_up_ref EC_KEY_up_ref_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_mul EC_POINT_mul_ptr
#define EC_POINT_new EC_POINT_new_ptr
#define EC_POINT_set_affine_coordinates_GFp EC_POINT_set_affine_coordinates_GFp_ptr
#define ERR_clear_error ERR_clear_error_ptr
ECCurveType curveType = EcKeyGetCurveType(key);
const EC_POINT* Q = EC_KEY_get0_public_key(key);
const EC_GROUP* group = EC_KEY_get0_group(key);
- if (curveType == Unspecified || !Q || !group)
+ if (curveType == Unspecified || !Q || !group)
goto error;
// Extract qx and qy
#if HAVE_OPENSSL_EC2M
if (API_EXISTS(EC_POINT_get_affine_coordinates_GF2m) && (curveType == Characteristic2))
{
- if (!EC_POINT_get_affine_coordinates_GF2m(group, Q, xBn, yBn, NULL))
+ if (!EC_POINT_get_affine_coordinates_GF2m(group, Q, xBn, yBn, NULL))
goto error;
}
else
#endif
{
- if (!EC_POINT_get_affine_coordinates_GFp(group, Q, xBn, yBn, NULL))
+ if (!EC_POINT_get_affine_coordinates_GFp(group, Q, xBn, yBn, NULL))
goto error;
}
BIGNUM* seedBn = NULL;
// Exit if CryptoNative_GetECKeyParameters failed
- if (rc != 1)
+ if (rc != 1)
goto error;
xBn = BN_new();
goto error;
group = EC_KEY_get0_group(key); // curve
- if (!group)
+ if (!group)
goto error;
curveMethod = EC_GROUP_method_of(group);
- if (!curveMethod)
+ if (!curveMethod)
goto error;
*curveType = MethodToCurveType(curveMethod);
- if (*curveType == Unspecified)
+ if (*curveType == Unspecified)
goto error;
// Extract p, a, b
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))
+ if (!EC_GROUP_get_curve_GF2m(group, pBn, aBn, bBn, NULL))
goto error;
}
else
#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))
+ 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))
+ if (!EC_POINT_get_affine_coordinates_GFp(group, G, xBn, yBn, NULL))
goto error;
}
// Extract seed (optional)
if (EC_GROUP_get0_seed(group))
{
- seedBn = BN_bin2bn(EC_GROUP_get0_seed(group),
+ seedBn = BN_bin2bn(EC_GROUP_get0_seed(group),
(int)EC_GROUP_get_seed_len(group), NULL);
-
+
*seed = seedBn;
*cbSeed = BN_num_bytes(seedBn);
BIGNUM* dBn = NULL;
BIGNUM* qxBn = NULL;
BIGNUM* qyBn = NULL;
+ EC_POINT* pubG = NULL;
// If key values specified, use them, otherwise a key will be generated later
if (qx && qy)
goto error;
}
+ // If we don't have the public key but we have the private key, we can
+ // re-derive the public key from d.
+ else if (qx == NULL && qy == NULL && qxLength == 0 && qyLength == 0 &&
+ d && dLength > 0)
+ {
+ dBn = BN_bin2bn(d, dLength, NULL);
+
+ if (!dBn)
+ goto error;
+
+ if (!EC_KEY_set_private_key(*key, dBn))
+ goto error;
+
+ const EC_GROUP* group = EC_KEY_get0_group(*key);
+
+ if (!group)
+ goto error;
+
+ pubG = EC_POINT_new(group);
+
+ if (!pubG)
+ goto error;
+
+ if (!EC_POINT_mul(group, pubG, dBn, NULL, NULL, NULL))
+ goto error;
+
+ if (!EC_KEY_set_public_key(*key, pubG))
+ goto error;
+
+ if (!EC_KEY_check_key(*key))
+ goto error;
+ }
+
// Success
return 1;
error:
if (qxBn) BN_free(qxBn);
if (qyBn) BN_free(qyBn);
- if (dBn) BN_free(dBn);
+ if (dBn) BN_clear_free(dBn);
+ if (pubG) EC_POINT_free(pubG);
if (*key)
{
EC_KEY_free(*key);
EC_KEY* key = NULL;
EC_POINT* G = NULL;
+ EC_POINT* pubG = NULL;
BIGNUM* qxBn = NULL;
BIGNUM* qyBn = 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))
+ 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))
+ if (!EC_GROUP_set_curve_GFp(group, pBn, aBn, bBn, NULL))
goto error;
}
if (!EC_KEY_check_key(key))
goto error;
}
+ // If we don't have the public key but we have the private key, we can
+ // re-derive the public key from d.
+ else if (qx == NULL && qy == NULL && qxLength == 0 && qyLength == 0 &&
+ d && dLength > 0)
+ {
+ dBn = BN_bin2bn(d, dLength, NULL);
+
+ if (!dBn)
+ goto error;
+
+ if (!EC_KEY_set_private_key(key, dBn))
+ goto error;
+
+ pubG = EC_POINT_new(group);
+
+ if (!pubG)
+ goto error;
+
+ if (!EC_POINT_mul(group, pubG, dBn, NULL, NULL, NULL))
+ goto error;
+
+ if (!EC_KEY_set_public_key(key, pubG))
+ goto error;
+
+ if (!EC_KEY_check_key(key))
+ goto error;
+ }
// Success
return key;
error:
if (qxBn) BN_free(qxBn);
if (qyBn) BN_free(qyBn);
- if (dBn) BN_free(dBn);
+ if (dBn) BN_clear_free(dBn);
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 (G) EC_POINT_free(G);
+ if (pubG) EC_POINT_free(pubG);
if (group) EC_GROUP_free(group);
if (key) EC_KEY_free(key);
return NULL;
/// </exception>
public void Validate()
{
- bool hasErrors = false;
+ bool hasErrors = true;
- if (Q.X == null ||
- Q.Y == null ||
- Q.X.Length != Q.Y.Length)
- {
- hasErrors = true;
- }
+ if (D != null && Q.Y is null && Q.X is null)
+ hasErrors = false;
+ if (Q.Y != null && Q.X != null && Q.Y.Length == Q.X.Length)
+ hasErrors = false;
if (!hasErrors)
{
// Explicit curves require D length to match Curve.Order
hasErrors = (D != null && (D.Length != Curve.Order!.Length));
}
- else if (Curve.IsNamed)
+ else if (Curve.IsNamed && Q.X != null)
{
- // Named curves require D length to match Q.X and Q.Y
- hasErrors = (D != null && (D.Length != Q.X!.Length));
+ // Named curves require D length to match Q.X and Q.Y if Q
+ // is present.
+ hasErrors = (D != null && (D.Length != Q.X.Length));
}
}
}
}
+ public bool LimitedPrivateKeySupported => !PlatformDetection.IsOSX;
+
private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
if (string.IsNullOrEmpty(friendlyNameOrValue))
}
}
+ public bool LimitedPrivateKeySupported => true;
+
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
if (string.IsNullOrEmpty(oidFriendlyName))
}
}
+ public bool LimitedPrivateKeySupported => !PlatformDetection.IsOSX;
+
private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue)
{
if (string.IsNullOrEmpty(friendlyNameOrValue))
}
}
+ public bool LimitedPrivateKeySupported => true;
+
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
if (string.IsNullOrEmpty(oidFriendlyName))
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs">
+ <Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs</Link>
</Compile>
<data name="PlatformNotSupported_CryptographyCng" xml:space="preserve">
<value>Windows Cryptography Next Generation (CNG) is not supported on this platform.</value>
</data>
+ <data name="Cryptography_ECC_NamedCurvesOnly" xml:space="preserve">
+ <value>Only named curves are supported on this platform.</value>
+ </data>
+ <data name="Cryptography_CSP_NoPrivateKey" xml:space="preserve">
+ <value>Object contains only the public half of a key pair. A private key must also be provided.</value>
+ </data>
+ <data name="Cryptography_InvalidECCharacteristic2Curve" xml:space="preserve">
+ <value>The specified Characteristic2 curve parameters are not valid. Polynomial, A, B, G.X, G.Y, and Order are required. A, B, G.X, G.Y must be the same length, and the same length as Q.X, Q.Y and D if those are specified. Seed, Cofactor and Hash are optional. Other parameters are not allowed.</value>
+ </data>
</root>
<Compile Include="$(CommonPath)System\Security\Cryptography\DSACng.SignVerify.cs">
<Link>Common\System\Security\Cryptography\DSACng.SignVerify.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\EccKeyFormatHelper.cs">
+ <Link>Common\System\Security\Cryptography\EccKeyFormatHelper.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)System\Security\Cryptography\ECCng.ImportExport.cs">
<Link>Common\System\Security\Cryptography\ECCng.ImportExport.cs</Link>
</Compile>
<Link>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml.cs</Link>
<DependentUpon>Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml</DependentUpon>
</Compile>
+ <AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\ECDomainParameters.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\Asn1\ECDomainParameters.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\SpecifiedECDomain.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\Asn1\SpecifiedECDomain.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\CurveAsn.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\CurveAsn.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\Asn1\CurveAsn.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\CurveAsn.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\CurveAsn.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\FieldID.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\FieldID.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\Asn1\FieldID.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\FieldID.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\FieldID.xml</DependentUpon>
+ </Compile>
+ <AsnXml Include="$(CommonPath)System\Security\Cryptography\Asn1\ECPrivateKey.xml">
+ <Link>Common\System\Security\Cryptography\Asn1\FieldID.xml</Link>
+ </AsnXml>
+ <Compile Include="$(CommonPath)System\Security\Cryptography\Asn1\ECPrivateKey.xml.cs">
+ <Link>Common\System\Security\Cryptography\Asn1\ECPrivateKey.xml.cs</Link>
+ <DependentUpon>Common\System\Security\Cryptography\Asn1\ECPrivateKey.xml</DependentUpon>
+ </Compile>
</ItemGroup>
<ItemGroup>
<None Include="@(AsnXml)" />
}
}
+ public bool LimitedPrivateKeySupported => true;
+
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
if (string.IsNullOrEmpty(oidFriendlyName))
}
}
+ public bool LimitedPrivateKeySupported => true;
+
private static bool NativeOidFriendlyNameExists(string oidFriendlyName)
{
if (string.IsNullOrEmpty(oidFriendlyName))
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs">
+ <Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs</Link>
</Compile>
public bool IsCurveValid(Oid oid) => _ecdsaProvider.IsCurveValid(oid);
public bool ExplicitCurvesSupported => _ecdsaProvider.ExplicitCurvesSupported;
+ public bool LimitedPrivateKeySupported => _ecdsaProvider.LimitedPrivateKeySupported;
}
public partial class ECDiffieHellmanFactory
return true;
}
}
+
+ public bool LimitedPrivateKeySupported => true;
}
public partial class ECDsaFactory
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs">
+ <Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs">
<Link>CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs</Link>
</Compile>