File: System\Security\Cryptography\CngKey.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
 
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
 
namespace System.Security.Cryptography {
    /// <summary>
    ///     Flags providing information about a raw key handle being opened
    /// </summary>
    [Flags]
    public enum CngKeyHandleOpenOptions {
        None = 0x00000000,
 
        /// <summary>
        ///     The key handle being opened represents an ephemeral key
        /// </summary>
        EphemeralKey = 0x00000001
    }
 
    /// <summary>
    ///     Managed representation of an NCrypt key
    /// </summary>
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public sealed partial class CngKey : IDisposable {
        private SafeNCryptKeyHandle m_keyHandle;
        private SafeNCryptProviderHandle m_kspHandle;
 
        [System.Security.SecurityCritical]
        private CngKey(SafeNCryptProviderHandle kspHandle, SafeNCryptKeyHandle keyHandle) {
            Contract.Requires(keyHandle != null && !keyHandle.IsInvalid && !keyHandle.IsClosed);
            Contract.Requires(kspHandle != null && !kspHandle.IsInvalid && !kspHandle.IsClosed);
            Contract.Ensures(m_keyHandle != null && !m_keyHandle.IsInvalid && !m_keyHandle.IsClosed);
            Contract.Ensures(kspHandle != null && !kspHandle.IsInvalid && !kspHandle.IsClosed);
 
            m_keyHandle = keyHandle;
            m_kspHandle = kspHandle;
        }
 
        //
        // Key properties
        //
 
        /// <summary>
        ///     Algorithm group this key can be used with
        /// </summary>
        public CngAlgorithmGroup AlgorithmGroup {
            [SecuritySafeCritical]
            [Pure]
            get {
                Contract.Assert(m_keyHandle != null);
                string group = NCryptNative.GetPropertyAsString(m_keyHandle,
                                                                NCryptNative.KeyPropertyName.AlgorithmGroup,
                                                                CngPropertyOptions.None);
 
                if (group == null) {
                    return null;
                }
                else {
                    return new CngAlgorithmGroup(group);
                }
            }
        }
 
        /// <summary>
        ///     Name of the algorithm this key can be used with
        /// </summary>
        public CngAlgorithm Algorithm {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
                string algorithm = NCryptNative.GetPropertyAsString(m_keyHandle,
                                                                    NCryptNative.KeyPropertyName.Algorithm,
                                                                    CngPropertyOptions.None);
                return new CngAlgorithm(algorithm);
            }
        }
 
        /// <summary>
        ///     Export restrictions on the key
        /// </summary>
        public CngExportPolicies ExportPolicy {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
                int policy = NCryptNative.GetPropertyAsDWord(m_keyHandle,
                                                             NCryptNative.KeyPropertyName.ExportPolicy,
                                                             CngPropertyOptions.None);
 
                return (CngExportPolicies)policy;
            }
 
            internal set {
                var property = new CngProperty(
                    NCryptNative.KeyPropertyName.ExportPolicy,
                    BitConverter.GetBytes((int)value),
                    CngPropertyOptions.Persist);
 
                SetProperty(property);
            }
        }
 
        /// <summary>
        ///     Native handle for the key
        /// </summary>
        public SafeNCryptKeyHandle Handle {
            [System.Security.SecurityCritical]
            [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
            get {
                Contract.Assert(m_keyHandle != null);
                return m_keyHandle.Duplicate();
            }
        }
 
        /// <summary>
        ///     Is this key ephemeral or persisted
        /// </summary>
        /// <remarks>
        ///     Any ephemeral key created by the CLR will have a property 'CLR IsEphemeral' which consists
        ///     of a single byte containing the value 1. We cannot detect ephemeral keys created by other
        ///     APIs and imported via handle.
        /// </remarks>
        public bool IsEphemeral {
            [SecuritySafeCritical]
            [Pure]
            get {
                Contract.Assert(m_keyHandle != null);
 
                bool foundProperty;
                byte[] ephemeralProperty = null;
                try {
                    ephemeralProperty = NCryptNative.GetProperty(m_keyHandle,
                                                                        NCryptNative.KeyPropertyName.ClrIsEphemeral,
                                                                        CngPropertyOptions.CustomProperty,
                                                                        out foundProperty);
                }
                catch (CryptographicException) { 
                    // Third party Key providers, and Windows PCP KSP won't recognize this property;
                    // and Win32 layer does not enforce error return contract.
                    // Therefore, they can return whatever error code they think appropriate.
                    return false;
                }
 
                return foundProperty &&
                       ephemeralProperty != null &&
                       ephemeralProperty.Length == 1 &&
                       ephemeralProperty[0] == 1;
            }
 
            [System.Security.SecurityCritical]
            private set {
                Contract.Assert(m_keyHandle != null);
 
                NCryptNative.SetProperty(m_keyHandle,
                                         NCryptNative.KeyPropertyName.ClrIsEphemeral,
                                         new byte[] { value ? (byte)1 : (byte)0 },
                                         CngPropertyOptions.CustomProperty);
            }
        }
 
        /// <summary>
        ///     Is this a machine key or a user key
        /// </summary>
        public bool IsMachineKey {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
                int type = NCryptNative.GetPropertyAsDWord(m_keyHandle,
                                                           NCryptNative.KeyPropertyName.KeyType,
                                                           CngPropertyOptions.None);
 
                return ((CngKeyTypes)type & CngKeyTypes.MachineKey) == CngKeyTypes.MachineKey;
            }
        }
 
        /// <summary>
        ///     The name of the key, null if it is ephemeral. We can only detect ephemeral keys created by
        ///     the CLR. Other ephemeral keys, such as those imported by handle, will get a CryptographicException
        ///     if they read this property.
        /// </summary>
        public string KeyName {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
 
                if (IsEphemeral) {
                    return null;
                }
                else {
                    return NCryptNative.GetPropertyAsString(m_keyHandle,
                                                            NCryptNative.KeyPropertyName.Name,
                                                            CngPropertyOptions.None);
                }
            }
        }
 
        /// <summary>
        ///     Size, in bits, of the key
        /// </summary>
        public int KeySize {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
 
                // First, try the Win10+ Public Key Length property, it matches the purpose
                // of this property better when it and Length disagree.
                int keySize = 0;
 
                NCryptNative.ErrorCode errorCode = NCryptNative.GetPropertyAsInt(
                    m_keyHandle,
                    NCryptNative.KeyPropertyName.PublicKeyLength,
                    CngPropertyOptions.None,
                    ref keySize);
 
                // If the new property reports it was successful, use it.
                // Otherwise, ask the old question.
                if (errorCode == NCryptNative.ErrorCode.Success) {
                    return keySize;
                }
 
                return NCryptNative.GetPropertyAsDWord(m_keyHandle,
                                                       NCryptNative.KeyPropertyName.Length,
                                                       CngPropertyOptions.None);
            }
        }
 
        /// <summary>
        ///     Usage restrictions on the key
        /// </summary>
        public CngKeyUsages KeyUsage {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
                int keyUsage = NCryptNative.GetPropertyAsDWord(m_keyHandle,
                                                               NCryptNative.KeyPropertyName.KeyUsage,
                                                               CngPropertyOptions.None);
                return (CngKeyUsages)keyUsage;
            }
        }
 
        /// <summary>
        ///     HWND of the window to use as a parent for any UI
        /// </summary>
        public IntPtr ParentWindowHandle {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
                return NCryptNative.GetPropertyAsIntPtr(m_keyHandle,
                                                        NCryptNative.KeyPropertyName.ParentWindowHandle,
                                                        CngPropertyOptions.None);
            }
 
            [SecuritySafeCritical]
            [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
            set {
                Contract.Assert(m_keyHandle != null);
                NCryptNative.SetProperty(m_keyHandle,
                                         NCryptNative.KeyPropertyName.ParentWindowHandle,
                                         value,
                                         CngPropertyOptions.None);
            }
        }
 
        /// <summary>
        ///     KSP which holds this key
        /// </summary>
        public CngProvider Provider {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_kspHandle != null);
                string provider = NCryptNative.GetPropertyAsString(m_kspHandle,
                                                                   NCryptNative.ProviderPropertyName.Name,
                                                                   CngPropertyOptions.None);
 
                if (provider == null) {
                    return null;
                }
                else {
                    return new CngProvider(provider);
                }
            }
        }
 
        /// <summary>
        ///     Native handle to the KSP associated with this key
        /// </summary>
        public SafeNCryptProviderHandle ProviderHandle {
            [System.Security.SecurityCritical]
            [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
            get {
                Contract.Assert(m_kspHandle != null);
                return m_kspHandle.Duplicate();
            }
        }
 
        /// <summary>
        ///     Unique name of the key, null if it is ephemeral. See the comments on the Name property for
        ///     details about names of ephemeral keys.
        /// </summary>
        public string UniqueName {
            [SecuritySafeCritical]
            get {
                Contract.Assert(m_keyHandle != null);
 
                if (IsEphemeral) {
                    return null;
                }
                else {
                    return NCryptNative.GetPropertyAsString(m_keyHandle,
                                                            NCryptNative.KeyPropertyName.UniqueName,
                                                            CngPropertyOptions.None);
                }
            }
        }
 
        /// <summary>
        ///     UI strings associated with a key
        /// </summary>
        public CngUIPolicy UIPolicy {
            [SecuritySafeCritical]
            get {
                Contract.Ensures(Contract.Result<CngUIPolicy>() != null);
                Contract.Assert(m_keyHandle != null);
 
                NCryptNative.NCRYPT_UI_POLICY uiPolicy =
                    NCryptNative.GetPropertyAsStruct<NCryptNative.NCRYPT_UI_POLICY>(m_keyHandle,
                                                                                    NCryptNative.KeyPropertyName.UIPolicy,
                                                                                    CngPropertyOptions.None);
 
                string useContext = NCryptNative.GetPropertyAsString(m_keyHandle,
                                                                     NCryptNative.KeyPropertyName.UseContext,
                                                                     CngPropertyOptions.None);
 
                return new CngUIPolicy(uiPolicy.dwFlags,
                                       uiPolicy.pszFriendlyName,
                                       uiPolicy.pszDescription,
                                       useContext,
                                       uiPolicy.pszCreationTitle);
            }
        }
 
        /// <summary>
        ///     Build a key container permission for the specified access to this key
        /// 
        ///     If the key is a known ephemeral key, return null, since we don't require permission to work with
        ///     those keys.  Otherwise return a permission scoped to the specific key and ksp if we can get those
        ///     values, defaulting back to a full KeyContainerPermission if we cannot.
        /// </summary>
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "The demand is based on a non-mutable value type")]
        internal KeyContainerPermission BuildKeyContainerPermission(KeyContainerPermissionFlags flags) {
            Contract.Ensures(Contract.Result<KeyContainerPermission>() != null || IsEphemeral);
            Contract.Assert(m_keyHandle != null);
            Contract.Assert(m_kspHandle != null);
 
            KeyContainerPermission permission = null;
 
            if (!IsEphemeral) {
 
                // Try to get the name of the key and ksp to demand for this specific instance
                string keyName = null;
                string kspName = null;
                try {
                    keyName = KeyName;
                    kspName = NCryptNative.GetPropertyAsString(m_kspHandle,
                                                               NCryptNative.ProviderPropertyName.Name,
                                                               CngPropertyOptions.None);
                }
                catch (CryptographicException) { /* This may have been an imported ephemeral key */ }
 
                if (keyName != null) {
                    KeyContainerPermissionAccessEntry access = new KeyContainerPermissionAccessEntry(keyName, flags);
                    access.ProviderName = kspName;
                    
                    permission = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
                    permission.AccessEntries.Add(access);
                }
                else {
                    permission = new KeyContainerPermission(flags);
                }
            }
 
            return permission;
        }
 
        //
        // Creation factory methods
        //
 
        public static CngKey Create(CngAlgorithm algorithm) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Create(algorithm, null);
        }
 
        public static CngKey Create(CngAlgorithm algorithm, string keyName) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Create(algorithm, keyName, null);
        }
 
        [SecuritySafeCritical]
        public static CngKey Create(CngAlgorithm algorithm, string keyName, CngKeyCreationParameters creationParameters) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
 
            if (algorithm == null) {
                throw new ArgumentNullException("algorithm");
            }
 
            if (creationParameters == null) {
                creationParameters = new CngKeyCreationParameters();
            }
 
            // Make sure that NCrypt is supported on this platform
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            // If we're not creating an ephemeral key, then we need to ensure the user has access to the key name
            if (keyName != null) {
                KeyContainerPermissionAccessEntry access = new KeyContainerPermissionAccessEntry(keyName, KeyContainerPermissionFlags.Create);
                access.ProviderName = creationParameters.Provider.Provider;
 
                KeyContainerPermission permission = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
                permission.AccessEntries.Add(access);
                permission.Demand();
            }
 
            //
            // Create the native handles representing the new key, setup the creation parameters on it, and
            // finalize it for use.
            //
 
            SafeNCryptProviderHandle kspHandle = NCryptNative.OpenStorageProvider(creationParameters.Provider.Provider);
            SafeNCryptKeyHandle keyHandle = NCryptNative.CreatePersistedKey(kspHandle,
                                                                            algorithm.Algorithm,
                                                                            keyName,
                                                                            creationParameters.KeyCreationOptions);
 
            SetKeyProperties(keyHandle, creationParameters);
            NCryptNative.FinalizeKey(keyHandle);
            
            CngKey key = new CngKey(kspHandle, keyHandle);
 
            // No name translates to an ephemeral key
            if (keyName == null) {
                key.IsEphemeral = true;
            }
 
            return key;
        }
 
        /// <summary>
        ///     Delete this key
        /// </summary>
        [SecuritySafeCritical]
        public void Delete() {
            Contract.Assert(m_keyHandle != null);
 
            // Make sure we have permission to delete this key
            KeyContainerPermission permission = BuildKeyContainerPermission(KeyContainerPermissionFlags.Delete);
            if (permission != null) {
                permission.Demand();
            }
 
            NCryptNative.DeleteKey(m_keyHandle);
 
            // Once the key is deleted, the handles are no longer valid so dispose of this instance
            Dispose();
        }
 
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
        public void Dispose() {
            if (m_kspHandle != null) {
                m_kspHandle.Dispose();
            }
 
            if (m_keyHandle != null) {
                m_keyHandle.Dispose();
            }
        }
 
        //
        // Check to see if a key already exists
        //
 
        public static bool Exists(string keyName) {
            return Exists(keyName, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }
 
        public static bool Exists(string keyName, CngProvider provider) {
            return Exists(keyName, provider, CngKeyOpenOptions.None);
        }
 
        [SecuritySafeCritical]
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
        public static bool Exists(string keyName, CngProvider provider, CngKeyOpenOptions options) {
            if (keyName == null) {
                throw new ArgumentNullException("keyName");
            }
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }
 
            // Make sure that NCrypt is supported on this platform
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            using (SafeNCryptProviderHandle kspHandle = NCryptNative.OpenStorageProvider(provider.Provider)) {
 
                SafeNCryptKeyHandle keyHandle = null;
 
                try {
                    NCryptNative.ErrorCode error = NCryptNative.UnsafeNativeMethods.NCryptOpenKey(kspHandle,
                                                                                                  out keyHandle,
                                                                                                  keyName,
                                                                                                  0,
                                                                                                  options);
 
                    // CNG will return either NTE_NOT_FOUND or NTE_BAD_KEYSET for the case where the key does
                    // not exist, so we need to check for both return codes.
                    bool keyNotFound = error == NCryptNative.ErrorCode.KeyDoesNotExist ||
                                       error == NCryptNative.ErrorCode.NotFound;
 
                    if (error != NCryptNative.ErrorCode.Success && !keyNotFound) {
                        throw new CryptographicException((int)error);
                    }
 
                    return error == NCryptNative.ErrorCode.Success;
                }
                finally {
                    if (keyHandle != null) {
                        keyHandle.Dispose();
                    }
                }
            }
        }
 
        //
        // Import factory methods
        //
 
        public static CngKey Import(byte[] keyBlob, CngKeyBlobFormat format) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Import(keyBlob, format, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }
 
        internal static CngKey Import(byte[] keyBlob, string curveName, CngKeyBlobFormat format) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Import(keyBlob, curveName, format, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }
 
        public static CngKey Import(byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Import(keyBlob, null, format, provider);
        }
 
        [SecuritySafeCritical]
        internal static CngKey Import(byte[] keyBlob, string curveName, CngKeyBlobFormat format, CngProvider provider)
        {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            if (keyBlob == null) {
                throw new ArgumentNullException("keyBlob"); 
            }
            if (format == null) {
                throw new ArgumentNullException("format");
            }
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }
 
            // Make sure that NCrypt is supported on this platform
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            // If we don't know for sure that the key will be ephemeral, then we need to demand Import
            // permission.  Since we won't know the name of the key until it's too late, we demand a full Import
            // rather than one scoped to the key.
            bool safeKeyImport = format == CngKeyBlobFormat.EccPublicBlob ||
                                 format == CngKeyBlobFormat.EccFullPublicBlob ||
                                 format == CngKeyBlobFormat.GenericPublicBlob;
 
            if (!safeKeyImport) {
                new KeyContainerPermission(KeyContainerPermissionFlags.Import).Demand();
            }
 
            // Import the key into the KSP
            SafeNCryptProviderHandle kspHandle = NCryptNative.OpenStorageProvider(provider.Provider);
            SafeNCryptKeyHandle keyHandle;
 
            if (curveName == null) {
                keyHandle = NCryptNative.ImportKey(kspHandle, keyBlob, format.Format);
            } else {
                keyHandle = ECCng.ImportKeyBlob(format.Format, keyBlob, curveName, kspHandle);
            }
 
            // Prepare the key for use
            CngKey key = new CngKey(kspHandle, keyHandle);
 
            // We can't tell directly if an OpaqueTransport blob imported as an ephemeral key or not
            key.IsEphemeral = format != CngKeyBlobFormat.OpaqueTransportBlob;
 
            return key;
        }
 
        /// <summary>
        ///     Export the key out of the KSP
        /// </summary>
        [SecuritySafeCritical]
        public byte[] Export(CngKeyBlobFormat format) {
            Contract.Assert(m_keyHandle != null);
 
            if (format == null) {
                throw new ArgumentNullException("format");
            }
 
            KeyContainerPermission permission = BuildKeyContainerPermission(KeyContainerPermissionFlags.Export);
            if (permission != null) {
                permission.Demand();
            }
 
            return NCryptNative.ExportKey(m_keyHandle, format.Format);
        }
 
        /// <summary>
        ///     Get the value of an arbitrary property
        /// </summary>
        [SecuritySafeCritical]
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public CngProperty GetProperty(string name, CngPropertyOptions options) {
            Contract.Assert(m_keyHandle != null);
 
            if (name == null) {
                throw new ArgumentNullException("name");
            }
 
            bool foundProperty;
            byte[] value = NCryptNative.GetProperty(m_keyHandle, name, options, out foundProperty);
 
            if (!foundProperty) {
                throw new CryptographicException((int)NCryptNative.ErrorCode.NotFound);
            }
 
            return new CngProperty(name, value, options);
        }
 
        /// <summary>
        ///     Determine if a property exists on the key
        /// </summary>
        [SecuritySafeCritical]
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public bool HasProperty(string name, CngPropertyOptions options) {
            Contract.Assert(m_keyHandle != null);
 
            if (name == null) {
                throw new ArgumentNullException("name");
            }
 
            bool foundProperty;
            NCryptNative.GetProperty(m_keyHandle, name, options, out foundProperty);
 
            return foundProperty;
        }
 
        //
        // Open factory methods
        //
 
        public static CngKey Open(string keyName) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Open(keyName, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }
 
        public static CngKey Open(string keyName, CngProvider provider) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
            return Open(keyName, provider, CngKeyOpenOptions.None);
        }
 
        [SecuritySafeCritical]
        public static CngKey Open(string keyName, CngProvider provider, CngKeyOpenOptions openOptions) {
            Contract.Ensures(Contract.Result<CngKey>() != null);
 
            if (keyName == null) {
                throw new ArgumentNullException("keyName");
            }
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }
 
            // Make sure that NCrypt is supported on this platform
            if (!NCryptNative.NCryptSupported) {
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            }
 
            // Ensure the user has access to the key name
            KeyContainerPermissionAccessEntry access = new KeyContainerPermissionAccessEntry(keyName, KeyContainerPermissionFlags.Open);
            access.ProviderName = provider.Provider;
 
            KeyContainerPermission permission = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
            permission.AccessEntries.Add(access);
            permission.Demand();
 
            // Open the key
            SafeNCryptProviderHandle kspHandle = NCryptNative.OpenStorageProvider(provider.Provider);
            SafeNCryptKeyHandle keyHandle = NCryptNative.OpenKey(kspHandle, keyName, openOptions);
 
            return new CngKey(kspHandle, keyHandle);
        }
 
        /// <summary>
        ///     Wrap an existing key handle with a CngKey object
        /// </summary>
        [System.Security.SecurityCritical]
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public static CngKey Open(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions) {
            if (keyHandle == null) {
                throw new ArgumentNullException("keyHandle");
            }
            if (keyHandle.IsClosed || keyHandle.IsInvalid) {
                throw new ArgumentException(SR.GetString(SR.Cryptography_OpenInvalidHandle), "keyHandle");
            }
 
            SafeNCryptKeyHandle keyHandleCopy = keyHandle.Duplicate();
 
            // Get a handle to the key's KSP
            SafeNCryptProviderHandle kspHandle = new SafeNCryptProviderHandle();
            RuntimeHelpers.PrepareConstrainedRegions();
            try { }
            finally {
                IntPtr rawHandle = NCryptNative.GetPropertyAsIntPtr(keyHandle,
                                                                    NCryptNative.KeyPropertyName.ProviderHandle,
                                                                    CngPropertyOptions.None);
                kspHandle.SetHandleValue (rawHandle);
            }
 
            // Setup a key object wrapping the handle
            CngKey key = null;
            bool keyFullySetup = false;
            try {
                key = new CngKey(kspHandle, keyHandleCopy);
 
                bool openingEphemeralKey = (keyHandleOpenOptions & CngKeyHandleOpenOptions.EphemeralKey) == CngKeyHandleOpenOptions.EphemeralKey;
 
                //
                // If we're wrapping a handle to an ephemeral key, we need to make sure that IsEphemeral is
                // setup to return true.  In the case that the handle is for an ephemeral key that was created
                // by the CLR, then we don't have anything to do as the IsEphemeral CLR property will already
                // be setup.  However, if the key was created outside of the CLR we will need to setup our
                // ephemeral detection property.
                // 
                // This enables consumers of CngKey objects to always be able to rely on the result of
                // calling IsEphemeral, and also allows them to safely access the Name property.
                // 
                // Finally, if we detect that this is an ephemeral key that the CLR created but we were not
                // told that it was an ephemeral key we'll throw an exception.  This prevents us from having
                // to decide who to believe -- the key property or the caller of the API.  Since other code
                // relies on the ephemeral flag being set properly to avoid tripping over bugs in CNG, we
                // need to reject the case that we suspect that the flag is incorrect.
                // 
 
                if (!key.IsEphemeral && openingEphemeralKey) {
                    key.IsEphemeral = true;
                }
                else if (key.IsEphemeral && !openingEphemeralKey) {
                    throw new ArgumentException(SR.GetString(SR.Cryptography_OpenEphemeralKeyHandleWithoutEphemeralFlag), "keyHandleOpenOptions");
                }
 
                keyFullySetup = true;
            }
            finally {
                // Make sure that we don't leak the handle the CngKey duplicated
                if (!keyFullySetup && key != null) {
                    key.Dispose();
                }
            }
 
            return key;
        }
 
        /// <summary>
        ///     Setup the key properties specified in the key creation parameters
        /// </summary>
        /// <param name="keyHandle"></param>
        /// <param name="creationParameters"></param>
        [System.Security.SecurityCritical]
        private static void SetKeyProperties(SafeNCryptKeyHandle keyHandle,
                                             CngKeyCreationParameters creationParameters) {
            Contract.Requires(keyHandle != null && !keyHandle.IsInvalid && !keyHandle.IsClosed);
            Contract.Requires(creationParameters != null);
 
            //
            // Setup the well-known properties.
            //
 
            if (creationParameters.ExportPolicy.HasValue) {
                NCryptNative.SetProperty(keyHandle,
                                         NCryptNative.KeyPropertyName.ExportPolicy,
                                         (int)creationParameters.ExportPolicy.Value,
                                         CngPropertyOptions.Persist);
            }
 
            if (creationParameters.KeyUsage.HasValue) {
                NCryptNative.SetProperty(keyHandle,
                                         NCryptNative.KeyPropertyName.KeyUsage,
                                         (int)creationParameters.KeyUsage.Value,
                                         CngPropertyOptions.Persist);
            }
 
            if (creationParameters.ParentWindowHandle != IntPtr.Zero) {
                NCryptNative.SetProperty(keyHandle,
                                         NCryptNative.KeyPropertyName.ParentWindowHandle,
                                         creationParameters.ParentWindowHandle,
                                         CngPropertyOptions.None);
            }
 
            if (creationParameters.UIPolicy != null) {
                NCryptNative.NCRYPT_UI_POLICY uiPolicy = new NCryptNative.NCRYPT_UI_POLICY();
                uiPolicy.dwVersion = 1;
                uiPolicy.dwFlags = creationParameters.UIPolicy.ProtectionLevel;
                uiPolicy.pszCreationTitle = creationParameters.UIPolicy.CreationTitle;
                uiPolicy.pszFriendlyName = creationParameters.UIPolicy.FriendlyName;
                uiPolicy.pszDescription = creationParameters.UIPolicy.Description;
 
                NCryptNative.SetProperty(keyHandle,
                                         NCryptNative.KeyPropertyName.UIPolicy,
                                         uiPolicy,
                                         CngPropertyOptions.Persist);
 
                // The use context is a seperate property from the standard UI context
                if (creationParameters.UIPolicy.UseContext != null) {
                    NCryptNative.SetProperty(keyHandle,
                                             NCryptNative.KeyPropertyName.UseContext,
                                             creationParameters.UIPolicy.UseContext,
                                             CngPropertyOptions.Persist);
                }
            }
 
            // Iterate over the custom properties, setting those as well.
            foreach (CngProperty property in creationParameters.ParametersNoDemand) {
                NCryptNative.SetProperty(keyHandle, property.Name, property.Value, property.Options);
            }
        }
 
        /// <summary>
        ///     Set an arbitrary property on the key
        /// </summary>
        [SecuritySafeCritical]
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public void SetProperty(CngProperty property) {
            Contract.Assert(m_keyHandle != null);
            NCryptNative.SetProperty(m_keyHandle, property.Name, property.Value, property.Options);
        }
    }
}