diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json index ce1383f883293..31bd3c0d81060 100644 --- a/.config/CredScanSuppressions.json +++ b/.config/CredScanSuppressions.json @@ -15,6 +15,7 @@ "/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", 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 85c256da2d97c..e059d1d6e815c 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 @@ -17,14 +17,14 @@ internal static partial class Crypto 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; diff --git a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs index 6f4beba67f06c..26022e2cb1d72 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/CngPkcs8.cs @@ -120,7 +120,23 @@ internal static unsafe Pkcs8Response ImportPkcs8PrivateKey(ReadOnlySpan so } bytesRead = len; - return ImportPkcs8(source.Slice(0, len)); + ReadOnlySpan 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( @@ -147,7 +163,21 @@ 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 { @@ -199,7 +229,22 @@ 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 + { + bytesRead = len; + return ImportPkcs8(pkcs8ZeroPublicKey.EncodeAsSpan()); + } + catch (CryptographicException) + { + throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e); + } } finally { @@ -305,6 +350,56 @@ private static AsnWriter RewriteEncryptedPkcs8PrivateKey( } } + // 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 source) + { + fixed (byte* ptr = &MemoryMarshal.GetReference(source)) + { + using (MemoryManager manager = new PointerMemoryManager(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 destination) { Debug.Assert(destination.Length < 128); diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs index 54eb1efe31867..8ffb7b1321e8d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanCng.ImportExport.cs @@ -19,12 +19,25 @@ public override void ImportParameters(ECParameters parameters) 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) { @@ -35,8 +48,20 @@ public override void ImportParameters(ECParameters parameters) 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 { diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs index 4d538c214daf0..6e34e4cb46282 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.ImportExport.cs @@ -34,12 +34,25 @@ public override void ImportParameters(ECParameters parameters) 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) { @@ -47,8 +60,20 @@ public override void ImportParameters(ECParameters parameters) 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 { 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 609ec1125a4cf..5d9d497c0e66d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECOpenSsl.ImportExport.cs @@ -114,8 +114,8 @@ private static SafeEcKeyHandle ImportNamedCurveParameters(ECParameters parameter 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; @@ -126,8 +126,8 @@ private static SafeEcKeyHandle ImportPrimeCurveParameters(ECParameters parameter 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, @@ -146,8 +146,8 @@ private static SafeEcKeyHandle ImportCharacteristic2CurveParameters(ECParameters 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, diff --git a/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs index 785fa560cf5e3..8452c502f7062 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/EccKeyFormatHelper.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Security.Cryptography.Asn1; @@ -97,7 +98,14 @@ internal static void FromECPrivateKey( 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) @@ -105,30 +113,33 @@ internal static void FromECPrivateKey( 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 publicKeyBytes = key.PublicKey.Value.Span; - ReadOnlySpan 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; @@ -142,13 +153,15 @@ internal static void FromECPrivateKey( 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(), }; @@ -463,7 +476,7 @@ private static void WriteAlgorithmIdentifier(in ECParameters ecParameters, AsnWr writer.PopSequence(); } - internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters) + internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters, AttributeAsn[]? attributes = null) { ecParameters.Validate(); @@ -475,11 +488,31 @@ internal static AsnWriter WritePkcs8PrivateKey(ECParameters ecParameters) // 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) @@ -776,7 +809,9 @@ private static AsnWriter WriteEcPrivateKey(in ECParameters ecParameters, bool in } // publicKey + if (ecParameters.Q.X != null) { + Debug.Assert(ecParameters.Q.Y != null); Asn1Tag explicit1 = new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true); writer.PushSequence(explicit1); diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs index 5ec20090be935..49e058f91a50e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/KeyFormatHelper.cs @@ -252,9 +252,12 @@ private static void ReadEncryptedPkcs8( } } - 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 algorithmIdentifier = algorithmIdentifierWriter.EncodeAsSpan(); ReadOnlySpan privateKey = privateKeyWriter.EncodeAsSpan(); @@ -287,7 +290,13 @@ internal static AsnWriter WritePkcs8(AsnWriter algorithmIdentifierWriter, AsnWri // PKI.privateKey writer.WriteOctetString(privateKey); - // We don't currently accept attributes, so... done. + // PKI.Attributes + if (attributesWriter != null) + { + ReadOnlySpan attributes = attributesWriter.EncodeAsSpan(); + writer.WriteEncodedValue(attributes); + } + writer.PopSequence(); return writer; } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs new file mode 100644 index 0000000000000..f85c8ce246618 --- /dev/null +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs @@ -0,0 +1,417 @@ +// 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 + { + 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(() => 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); + } + } +} diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs index a6c71572a3368..80b0d5adab4ed 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Tests { - public abstract class ECKeyFileTests where T : AsymmetricAlgorithm + public abstract partial class ECKeyFileTests where T : AsymmetricAlgorithm { protected abstract T CreateKey(); protected abstract byte[] ExportECPrivateKey(T key); @@ -16,6 +16,7 @@ public abstract class ECKeyFileTests where T : AsymmetricAlgorithm protected abstract void ImportECPrivateKey(T key, ReadOnlySpan 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); @@ -387,7 +388,7 @@ public void ReadWriteSect163k1Key1EncryptedPkcs8() new PbeParameters( PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, - 7), + 7), EccTestData.Sect163k1Key1, SupportsSect163k1); } @@ -446,7 +447,7 @@ public void ReadWriteSect163k1Key1ExplicitEncryptedPkcs8() new PbeParameters( PbeEncryptionAlgorithm.Aes128Cbc, HashAlgorithmName.SHA256, - 12), + 12), EccTestData.Sect163k1Key1Explicit, SupportsSect163k1); } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs index aee3bb9ae2c48..cc34d55de2239 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs @@ -37,5 +37,7 @@ protected override ECParameters ExportParameters(ECDiffieHellman key, bool inclu { return key.ExportParameters(includePrivate); } + + protected override void Exercise(ECDiffieHellman key) => key.Exercise(); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs index 41718a92a7843..c0b6fb508afa5 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanFactory.cs @@ -13,6 +13,7 @@ public interface IECDiffieHellmanProvider #endif bool IsCurveValid(Oid oid); bool ExplicitCurvesSupported { get; } + bool LimitedPrivateKeySupported { get; } } public static partial class ECDiffieHellmanFactory @@ -40,5 +41,6 @@ public static bool IsCurveValid(Oid oid) } public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported; + public static bool LimitedPrivateKeySupported => s_provider.LimitedPrivateKeySupported; } } 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 fa9d5290b89c5..085305d445e4d 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 @@ -386,6 +386,33 @@ public static void ExportIncludingPrivateOnPublicOnlyKey() } } + [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(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs index 221ad59486b2e..fe65d17420212 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs @@ -13,6 +13,7 @@ public interface IECDsaProvider #endif bool IsCurveValid(Oid oid); bool ExplicitCurvesSupported { get; } + bool LimitedPrivateKeySupported { get; } } public static partial class ECDsaFactory @@ -40,5 +41,6 @@ public static bool IsCurveValid(Oid oid) } public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported; + public static bool LimitedPrivateKeySupported => s_provider.LimitedPrivateKeySupported; } } 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 fbd7850b9f6c4..ee0eeef043f25 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 @@ -320,6 +320,33 @@ public static void ExportIncludingPrivateOnPublicOnlyKey() } } + [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(); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs index 3c2ef9c787599..4a81e807f44a4 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaKeyFileTests.cs @@ -37,5 +37,7 @@ protected override ECParameters ExportParameters(ECDsa key, bool includePrivate) { return key.ExportParameters(includePrivate); } + + protected override void Exercise(ECDsa key) => key.Exercise(); } } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h index 27bf8294f56f6..edf72362e6413 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h @@ -291,11 +291,13 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi 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) \ @@ -679,11 +681,13 @@ FOR_ALL_OPENSSL_FUNCTIONS #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 diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ecc_import_export.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ecc_import_export.c index 83ad91348c37f..aa1ced4d94f7f 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ecc_import_export.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ecc_import_export.c @@ -80,7 +80,7 @@ int32_t CryptoNative_GetECKeyParameters( 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 @@ -92,13 +92,13 @@ int32_t CryptoNative_GetECKeyParameters( #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; } @@ -199,7 +199,7 @@ int32_t CryptoNative_GetECCurveParameters( BIGNUM* seedBn = NULL; // Exit if CryptoNative_GetECKeyParameters failed - if (rc != 1) + if (rc != 1) goto error; xBn = BN_new(); @@ -214,15 +214,15 @@ int32_t CryptoNative_GetECCurveParameters( 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 @@ -230,7 +230,7 @@ int32_t CryptoNative_GetECCurveParameters( 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 @@ -246,13 +246,13 @@ int32_t CryptoNative_GetECCurveParameters( #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; } @@ -267,9 +267,9 @@ int32_t CryptoNative_GetECCurveParameters( // 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); @@ -345,6 +345,7 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u 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) @@ -373,13 +374,47 @@ int32_t CryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, u 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); @@ -411,6 +446,7 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( EC_KEY* key = NULL; EC_POINT* G = NULL; + EC_POINT* pubG = NULL; BIGNUM* qxBn = NULL; BIGNUM* qyBn = NULL; @@ -439,13 +475,13 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( #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; } @@ -514,6 +550,33 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( 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; @@ -521,7 +584,7 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( 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); @@ -530,6 +593,7 @@ EC_KEY* CryptoNative_EcKeyCreateByExplicitParameters( 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; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECParameters.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECParameters.cs index f325ba696fc41..942f0dbf1caa2 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECParameters.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECParameters.cs @@ -36,14 +36,12 @@ public struct ECParameters /// 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) { @@ -52,10 +50,11 @@ public void Validate() // 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)); } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Unix.cs index 21c3c5016e9fb..063c4e008b3cd 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Unix.cs @@ -35,6 +35,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => !PlatformDetection.IsOSX; + private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue) { if (string.IsNullOrEmpty(friendlyNameOrValue)) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Windows.cs index fdd5e11c733f3..5849b8b11f190 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDiffieHellmanProvider.Windows.cs @@ -23,6 +23,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => true; + private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { if (string.IsNullOrEmpty(oidFriendlyName)) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Unix.cs index db573a6cd03a2..7983e72698755 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Unix.cs @@ -35,6 +35,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => !PlatformDetection.IsOSX; + private static bool IsValueOrFriendlyNameValid(string friendlyNameOrValue) { if (string.IsNullOrEmpty(friendlyNameOrValue)) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Windows.cs index 981da0ddb45be..6b82d2b107e8f 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.Windows.cs @@ -22,6 +22,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => true; + private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { if (string.IsNullOrEmpty(oidFriendlyName)) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index aa25b8f7bab7d..0f71a474092d7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -144,6 +144,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs diff --git a/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx index 6c453bcc32893..514ccdf845cd2 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Cng/src/Resources/Strings.resx @@ -258,4 +258,13 @@ Windows Cryptography Next Generation (CNG) is not supported on this platform. + + Only named curves are supported on this platform. + + + Object contains only the public half of a key pair. A private key must also be provided. + + + 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. + diff --git a/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj b/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj index 658286dbfb998..0aae024d50dde 100644 --- a/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj @@ -253,6 +253,9 @@ Common\System\Security\Cryptography\DSACng.SignVerify.cs + + Common\System\Security\Cryptography\EccKeyFormatHelper.cs + Common\System\Security\Cryptography\ECCng.ImportExport.cs @@ -388,6 +391,41 @@ Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml.cs Common\System\Security\Cryptography\Asn1\SubjectPublicKeyInfoAsn.xml + + Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml + + + Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml.cs + Common\System\Security\Cryptography\Asn1\ECDomainParameters.xml + + + Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml + + + Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml.cs + Common\System\Security\Cryptography\Asn1\SpecifiedECDomain.xml + + + Common\System\Security\Cryptography\Asn1\CurveAsn.xml + + + Common\System\Security\Cryptography\Asn1\CurveAsn.xml.cs + Common\System\Security\Cryptography\Asn1\CurveAsn.xml + + + Common\System\Security\Cryptography\Asn1\FieldID.xml + + + Common\System\Security\Cryptography\Asn1\FieldID.xml.cs + Common\System\Security\Cryptography\Asn1\FieldID.xml + + + Common\System\Security\Cryptography\Asn1\FieldID.xml + + + Common\System\Security\Cryptography\Asn1\ECPrivateKey.xml.cs + Common\System\Security\Cryptography\Asn1\ECPrivateKey.xml + diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs b/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs index 1942f26a11a2b..8cfd7a4e7d377 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/ECDiffieHellmanCngProvider.cs @@ -37,6 +37,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => true; + private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { if (string.IsNullOrEmpty(oidFriendlyName)) diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/ECDsaCngProvider.cs b/src/libraries/System.Security.Cryptography.Cng/tests/ECDsaCngProvider.cs index b0dc50297f24d..200c61070ab8d 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/ECDsaCngProvider.cs +++ b/src/libraries/System.Security.Cryptography.Cng/tests/ECDsaCngProvider.cs @@ -37,6 +37,8 @@ public bool ExplicitCurvesSupported } } + public bool LimitedPrivateKeySupported => true; + private static bool NativeOidFriendlyNameExists(string oidFriendlyName) { if (string.IsNullOrEmpty(oidFriendlyName)) diff --git a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index b78e98c57e348..c0b950a15c25f 100644 --- a/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -135,6 +135,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslProvider.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslProvider.cs index 44c7fcb519616..8903cf8bd063a 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslProvider.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDiffieHellmanOpenSslProvider.cs @@ -26,6 +26,7 @@ public ECDiffieHellman Create(ECCurve curve) public bool IsCurveValid(Oid oid) => _ecdsaProvider.IsCurveValid(oid); public bool ExplicitCurvesSupported => _ecdsaProvider.ExplicitCurvesSupported; + public bool LimitedPrivateKeySupported => _ecdsaProvider.LimitedPrivateKeySupported; } public partial class ECDiffieHellmanFactory diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslProvider.cs b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslProvider.cs index a78cf83145371..3cb4095462504 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslProvider.cs +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/EcDsaOpenSslProvider.cs @@ -54,6 +54,8 @@ public bool ExplicitCurvesSupported return true; } } + + public bool LimitedPrivateKeySupported => true; } public partial class ECDsaFactory diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index 147d63cbfdd74..0084e99045416 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -143,6 +143,9 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\EC\ECKeyFileTests.LimitedPrivate.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\ECDiffieHellman\ECDhKeyFileTests.cs