File: System\Security\Cryptography\ECDiffieHellmanCng.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography {
    /// <summary>
    ///     Key derivation functions used to transform the raw secret agreement into key material
    /// </summary>
    public enum ECDiffieHellmanKeyDerivationFunction {
        Hash,
        Hmac,
        Tls
    }
 
    /// <summary>
    ///     Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
    /// </summary>
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public sealed class ECDiffieHellmanCng : ECDiffieHellman {
        private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
 
        private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
        private byte[] m_hmacKey;
        private CngKey m_key;
        private ECDiffieHellmanKeyDerivationFunction m_kdf = ECDiffieHellmanKeyDerivationFunction.Hash;
        private byte[] m_label;
        private byte[] m_secretAppend;
        private byte[] m_secretPrepend;
        private byte[] m_seed;
 
        //
        // Constructors
        //
 
        public ECDiffieHellmanCng() : this(521) {
            Contract.Ensures(LegalKeySizesValue != null);
        }
 
        public ECDiffieHellmanCng(int keySize) {
            Contract.Ensures(LegalKeySizesValue != null);
 
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            LegalKeySizesValue = s_legalKeySizes;
            KeySize = keySize;
        }
 
        public ECDiffieHellmanCng(ECCurve curve) {
            // GenerateKey will already do all of the validation we need.
            GenerateKey(curve);
        }
 
        [SecuritySafeCritical]
        public ECDiffieHellmanCng(CngKey key) {
            Contract.Ensures(LegalKeySizesValue != null);
            Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
 
            if (key == null) {
                throw new ArgumentNullException("key");
            }
            if (key.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "key");
            }
 
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            LegalKeySizesValue = s_legalKeySizes;
 
            // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
            //
            // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
            // and the handle constructor of CngKey.  The assert is safe since ECDiffieHellmanCng will never
            // expose the key handles to calling code (without first demanding UnmanagedCode via the Handle
            // property of CngKey).
            //
            // The bizzare looking disposal of the key.Handle property is intentional - Handle returns a
            // duplicate - without disposing it, we keep the key alive until the GC runs.
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
            using (SafeNCryptKeyHandle importHandle = key.Handle) {
                Key = CngKey.Open(importHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
            }
            CodeAccessPermission.RevertAssert();
 
            // Our LegalKeySizes value stores the values that we encoded as being the correct
            // legal key size limitations for this algorithm, as documented on MSDN.
            //
            // But on a new OS version we might not question if our limit is accurate, or MSDN
            // could have been innacurate to start with.
            //
            // Since the key is already loaded, we know that Windows thought it to be valid;
            // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
            // check.
            //
            // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
            // create a 384-bit RSA key, which we consider too small to be legal. It can also create
            // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
            // alignment requirement. (In both cases Windows loads it just fine)
            KeySizeValue = m_key.KeySize;
        }
 
        /// <summary>
        ///     Hash algorithm used with the Hash and HMAC KDFs
        /// </summary>
        public CngAlgorithm HashAlgorithm {
            get {
                Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
                return m_hashAlgorithm;
            }
 
            set {
                Contract.Ensures(m_hashAlgorithm != null);
 
                if (m_hashAlgorithm == null) {
                    throw new ArgumentNullException("value");
                }
 
                m_hashAlgorithm = value;
            }
        }
 
        /// <summary>
        ///     Key used with the HMAC KDF
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
        public byte[] HmacKey {
            get { return m_hmacKey; }
            set { m_hmacKey = value; }
        }
 
        /// <summary>
        ///     KDF used to transform the secret agreement into key material
        /// </summary>
        public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction {
            get {
                Contract.Ensures(Contract.Result<ECDiffieHellmanKeyDerivationFunction>() >= ECDiffieHellmanKeyDerivationFunction.Hash &&
                                 Contract.Result<ECDiffieHellmanKeyDerivationFunction>() <= ECDiffieHellmanKeyDerivationFunction.Tls);
 
                return m_kdf;
            }
 
            set {
                Contract.Ensures(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
                                        m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
 
                if (value < ECDiffieHellmanKeyDerivationFunction.Hash || value > ECDiffieHellmanKeyDerivationFunction.Tls) {
                    throw new ArgumentOutOfRangeException("value");
                }
 
                m_kdf = value;
            }
        }
 
        /// <summary>
        ///     Label bytes used for the TLS KDF
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
        public byte[] Label {
            get { return m_label; }
            set { m_label = value; }
        }
 
        /// <summary>
        ///     Bytes to append to the raw secret agreement before processing by the KDF
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
        public byte[] SecretAppend {
            get { return m_secretAppend; }
            set { m_secretAppend = value; }
        }
 
        /// <summary>
        ///     Bytes to prepend to the raw secret agreement before processing by the KDF
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
        public byte[] SecretPrepend {
            get { return m_secretPrepend; }
            set { m_secretPrepend = value; }
        }
 
        /// <summary>
        ///     Seed bytes used for the TLS KDF
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
        public byte[] Seed {
            get { return m_seed; }
            set { m_seed = value; }
        }
 
        /// <summary>
        ///     Full key pair being used for key generation
        /// </summary>
        public CngKey Key {
            get {
                Contract.Ensures(Contract.Result<CngKey>() != null);
                Contract.Ensures(Contract.Result<CngKey>().AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
                Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
 
                // If the size of the key no longer matches our stored value, then we need to replace it with
                // a new key of the correct size.
                if (m_key != null && m_key.KeySize != KeySize) {
                    m_key.Dispose();
                    m_key = null;
                }
 
                if (m_key == null) {
                    // Map the current key size to a CNG algorithm name
                    CngAlgorithm algorithm = null;
                    switch (KeySize) {
                        case 256:
                            algorithm = CngAlgorithm.ECDiffieHellmanP256;
                            break;
 
                        case 384:
                            algorithm = CngAlgorithm.ECDiffieHellmanP384;
                            break;
 
                        case 521:
                            algorithm = CngAlgorithm.ECDiffieHellmanP521;
                            break;
 
                        default:
                            Debug.Assert(false, "Illegal key size set");
                            break;
                    }
 
                    CngKeyCreationParameters creationParameters = new CngKeyCreationParameters()
                    {
                        ExportPolicy = CngExportPolicies.AllowPlaintextExport,
                    };
 
                    m_key = CngKey.Create(algorithm, null, creationParameters);
                }
 
                return m_key;
            }
 
            private set {
                Contract.Requires(value != null);
                Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
 
                if (value.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
                    throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
                }
 
                if (m_key != null) {
                    m_key.Dispose();
                }
 
                //
                // We do not duplicate the handle because the only time the user has access to the key itself
                // to dispose underneath us is when they construct via the CngKey constructor, which does a
                // duplication. Otherwise all key lifetimes are controlled directly by the ECDiffieHellmanCng
                // class.
                //
 
                m_key = value;
 
                // Our LegalKeySizes value stores the values that we encoded as being the correct
                // legal key size limitations for this algorithm, as documented on MSDN.
                //
                // But on a new OS version we might not question if our limit is accurate, or MSDN
                // could have been innacurate to start with.
                //
                // Since the key is already loaded, we know that Windows thought it to be valid;
                // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
                // check.
                //
                // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
                // create a 384-bit RSA key, which we consider too small to be legal. It can also create
                // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
                // alignment requirement. (In both cases Windows loads it just fine)
                KeySizeValue = m_key.KeySize;
            }
        }
 
        /// <summary>
        ///     Public key used to generate key material with the second party
        /// </summary>
        public override ECDiffieHellmanPublicKey PublicKey {
            get {
                Contract.Ensures(Contract.Result<ECDiffieHellmanPublicKey>() != null);
                return ECDiffieHellmanCngPublicKey.FromKey(Key);
            }
        }
 
        /// <summary>
        ///     Use the secret agreement as the HMAC key rather than supplying a seperate one
        /// </summary>
        public bool UseSecretAgreementAsHmacKey {
            get { return HmacKey == null; }
        }
 
        /// <summary>
        ///     Given a second party's public key, derive shared key material
        /// </summary>
        public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result<byte[]>() != null);
            Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
                            m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
 
            if (otherPartyPublicKey == null) {
                throw new ArgumentNullException("otherPartyPublicKey");
            }
 
            // We can only work with ECDiffieHellmanCngPublicKeys
            ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
            if (otherKey == null) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
            }
 
            using (CngKey import = otherKey.Import()) {
                return DeriveKeyMaterial(import);
            }
        }
 
        /// <summary>
        ///     Given a second party's public key, derive shared key material
        /// </summary>
        [SecuritySafeCritical]
        public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result<byte[]>() != null);
            Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
                            m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
 
            if (otherPartyPublicKey == null) {
                throw new ArgumentNullException("otherPartyPublicKey");
            }
            if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
            }
            if (otherPartyPublicKey.KeySize != KeySize) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
            }
 
            NCryptNative.SecretAgreementFlags flags =
                UseSecretAgreementAsHmacKey ? NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey : NCryptNative.SecretAgreementFlags.None;
 
            // We require access to the handles for generating key material. This is safe since we will never
            // expose these handles to user code
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
 
            // This looks horribly wrong - but accessing the handle property actually returns a duplicate handle, which
            // we need to dispose of - otherwise, we're stuck keepign the resource alive until the GC runs.  This explicitly
            // is not disposing of the handle underlying the key dispite what the syntax looks like.
            using (SafeNCryptKeyHandle localKey = Key.Handle)
            using (SafeNCryptKeyHandle otherKey = otherPartyPublicKey.Handle) {
                CodeAccessPermission.RevertAssert();
 
                //
                // Generating key material is a two phase process.
                //   1. Generate the secret agreement
                //   2. Pass the secret agreement through a KDF to get key material
                //
 
                using (SafeNCryptSecretHandle secretAgreement = NCryptNative.DeriveSecretAgreement(localKey, otherKey)) {
                    if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hash) {
                        byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
                        byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
 
                        return NCryptNative.DeriveKeyMaterialHash(secretAgreement,
                                                                  HashAlgorithm.Algorithm,
                                                                  secretPrepend,
                                                                  secretAppend,
                                                                  flags);
                    }
                    else if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hmac) {
                        byte[] hmacKey = HmacKey == null ? null : HmacKey.Clone() as byte[];
                        byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
                        byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
 
                        return NCryptNative.DeriveKeyMaterialHmac(secretAgreement,
                                                                  HashAlgorithm.Algorithm,
                                                                  hmacKey,
                                                                  secretPrepend,
                                                                  secretAppend,
                                                                  flags);
                    }
                    else {
                        Debug.Assert(KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Tls, "Unknown KDF");
 
                        byte[] label = Label == null ? null : Label.Clone() as byte[];
                        byte[] seed = Seed == null ? null : Seed.Clone() as byte[];
 
                        if (label == null || seed == null) {
                            throw new InvalidOperationException(SR.GetString(SR.Cryptography_TlsRequiresLabelAndSeed));
                        }
 
                        return NCryptNative.DeriveKeyMaterialTls(secretAgreement, label, seed, flags);
                    }
                }
            }
        }
 
        [SecuritySafeCritical]
        public override byte[] DeriveKeyFromHash(
            ECDiffieHellmanPublicKey otherPartyPublicKey,
            HashAlgorithmName hashAlgorithm,
            byte[] secretPrepend,
            byte[] secretAppend)
        {
            Contract.Ensures(Contract.Result<byte[]>() != null);
 
            if (otherPartyPublicKey == null)
                throw new ArgumentNullException("otherPartyPublicKey");
            if (string.IsNullOrEmpty(hashAlgorithm.Name))
                throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
 
            using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey))
            {
                return NCryptNative.DeriveKeyMaterialHash(
                    secretAgreement,
                    hashAlgorithm.Name, 
                    secretPrepend,
                    secretAppend,
                    NCryptNative.SecretAgreementFlags.None);
            }
        }
 
        [SecuritySafeCritical]
        public override byte[] DeriveKeyFromHmac(
            ECDiffieHellmanPublicKey otherPartyPublicKey,
            HashAlgorithmName hashAlgorithm,
            byte[] hmacKey,
            byte[] secretPrepend,
            byte[] secretAppend)
        {
            Contract.Ensures(Contract.Result<byte[]>() != null);
 
            if (otherPartyPublicKey == null)
                throw new ArgumentNullException("otherPartyPublicKey");
            if (string.IsNullOrEmpty(hashAlgorithm.Name))
                throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
 
            using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey))
            {
                NCryptNative.SecretAgreementFlags flags = hmacKey == null ?
                    NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey :
                    NCryptNative.SecretAgreementFlags.None;
 
                return NCryptNative.DeriveKeyMaterialHmac(
                    secretAgreement,
                    hashAlgorithm.Name,
                    hmacKey,
                    secretPrepend,
                    secretAppend,
                    flags);
            }
        }
 
        [SecuritySafeCritical]
        public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed)
        {
            Contract.Ensures(Contract.Result<byte[]>() != null);
 
            if (otherPartyPublicKey == null)
                throw new ArgumentNullException("otherPartyPublicKey");
            if (prfLabel == null)
                throw new ArgumentNullException("prfLabel");
            if (prfSeed == null)
                throw new ArgumentNullException("prfSeed");
 
            using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey))
            {
                return NCryptNative.DeriveKeyMaterialTls(
                    secretAgreement,
                    prfLabel,
                    prfSeed,
                    NCryptNative.SecretAgreementFlags.None);
            }
        }
 
        /// <summary>
        ///     Get a handle to the secret agreement generated between two parties
        /// </summary>
        public SafeNCryptSecretHandle DeriveSecretAgreementHandle(ECDiffieHellmanPublicKey otherPartyPublicKey) {
            if (otherPartyPublicKey == null) {
                throw new ArgumentNullException("otherPartyPublicKey");
            }
            
            // We can only work with ECDiffieHellmanCngPublicKeys
            ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
            if (otherPartyPublicKey == null) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
            }
 
            using (CngKey importedKey = otherKey.Import()) {
                return DeriveSecretAgreementHandle(importedKey);
            }
        }
 
        /// <summary>
        ///     Get a handle to the secret agreement between two parties
        /// </summary>
        [System.Security.SecurityCritical]
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public SafeNCryptSecretHandle DeriveSecretAgreementHandle(CngKey otherPartyPublicKey) {
            if (otherPartyPublicKey == null) {
                throw new ArgumentNullException("otherPartyPublicKey");
            }
            if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
            }
            if (otherPartyPublicKey.KeySize != KeySize) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
            }
 
            // This looks strange, but the Handle property returns a duplicate so we need to dispose of it when we're done
            using (SafeNCryptKeyHandle localHandle = Key.Handle)
            using (SafeNCryptKeyHandle otherPartyHandle = otherPartyPublicKey.Handle) {
                return NCryptNative.DeriveSecretAgreement(localHandle, otherPartyHandle);
            }
        }
 
        /// <summary>
        ///     Clean up the algorithm
        /// </summary>
        protected override void Dispose(bool disposing) {
            try {
                if (disposing) {
                    if (m_key != null) {
                        m_key.Dispose();
                    }
                }
            }
            finally {
                base.Dispose(disposing);
            }
        }
 
        public override void GenerateKey(ECCurve curve) {
            curve.Validate();
 
            if (m_key != null) {
                m_key.Dispose();
                m_key = null;
            }
 
            CngKey newKey = CngKey.Create(curve, name => CngKey.EcdhCurveNameToAlgorithm(name));
            m_key = newKey;
            KeySizeValue = newKey.KeySize;
        }
 
        //
        // XML Import
        //
        // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
        // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
        // elliptic curve XML formats.
        //
 
        public override void FromXmlString(string xmlString) {
            throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
        }
 
        public void FromXmlString(string xml, ECKeyXmlFormat format) {
            if (xml == null) {
                throw new ArgumentNullException("xml");
            }
            if (format != ECKeyXmlFormat.Rfc4050) {
                throw new ArgumentOutOfRangeException("format");
            }
 
            bool isEcdh;
            ECParameters ecParams = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
 
            if (!isEcdh) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
            }
 
            ImportParameters(ecParams);
        }
 
        //
        // XML Export
        //
        // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
        // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
        // elliptic curve XML formats.
        //
 
        public override string ToXmlString(bool includePrivateParameters) {
            throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
        }
 
        public string ToXmlString(ECKeyXmlFormat format) {
            Contract.Ensures(Contract.Result<string>() != null);
 
            if (format != ECKeyXmlFormat.Rfc4050) {
                throw new ArgumentOutOfRangeException("format");
            }
 
            ECParameters ecParams = ExportParameters(false);
            return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: true);
        }
 
        /// <summary>
        ///  ImportParameters will replace the existing key that this object is working with by creating a
        ///  new CngKey. If the parameters contains only Q, then only a public key will be imported.
        ///  If the parameters also contains D, then a full key pair will be imported. 
        ///  The parameters Curve value specifies the type of the curve to import.
        /// </summary>
        /// <exception cref="CryptographicException">
        ///  if <paramref name="parameters" /> does not contain valid values.
        /// </exception>
        /// <exception cref="NotSupportedException">
        ///  if <paramref name="parameters" /> references a curve that cannot be imported.
        /// </exception>
        /// <exception cref="PlatformNotSupportedException">
        ///  if <paramref name="parameters" /> references a curve that is not supported by this platform.
        /// </exception>
        public override void ImportParameters(ECParameters parameters) {
            Key = ECCng.ImportEcdhParameters(ref parameters);
        }
 
        /// <summary>
        ///  Exports the key and explicit curve parameters used by the ECC object into an <see cref="ECParameters"/> object.
        /// </summary>
        /// <exception cref="CryptographicException">
        ///  if there was an issue obtaining the curve values.
        /// </exception>
        /// <exception cref="PlatformNotSupportedException">
        ///  if explicit export is not supported by this platform. Windows 10 or higher is required.
        /// </exception>
        /// <returns>The key and explicit curve parameters used by the ECC object.</returns>
        public override ECParameters ExportExplicitParameters(bool includePrivateParameters) {
            return ECCng.ExportExplicitParameters(Key, includePrivateParameters);
        }
 
        /// <summary>
        ///  Exports the key used by the ECC object into an <see cref="ECParameters"/> object.
        ///  If the key was created as a named curve, the Curve property will contain named curve parameters
        ///  otherwise it will contain explicit parameters.
        /// </summary>
        /// <exception cref="CryptographicException">
        ///  if there was an issue obtaining the curve values.
        /// </exception>
        /// <returns>The key and named curve parameters used by the ECC object.</returns>
        public override ECParameters ExportParameters(bool includePrivateParameters) {
            return ECCng.ExportParameters(Key, includePrivateParameters);
        }
    }
}