File: System\Security\Cryptography\CngKey.OpenHandle.cs
Web Access
Project: src\src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;

namespace System.Security.Cryptography
{
    /// <summary>
    ///     Managed representation of an NCrypt key
    /// </summary>
    public sealed partial class CngKey : IDisposable
    {
        /// <summary>
        ///     Wrap an existing key handle with a CngKey object
        /// </summary>
        [SupportedOSPlatform("windows")]
        public static CngKey Open(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions)
        {
            ArgumentNullException.ThrowIfNull(keyHandle);

            if (keyHandle.IsClosed || keyHandle.IsInvalid)
                throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(keyHandle));

            return OpenNoDuplicate(keyHandle.Duplicate(), keyHandleOpenOptions);
        }

        [SupportedOSPlatform("windows")]
        internal static CngKey OpenNoDuplicate(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions)
        {
            SafeNCryptProviderHandle? providerHandle = null;
            try
            {
                // Get a handle to the key's provider.
                providerHandle = new SafeNCryptProviderHandle();
                IntPtr rawProviderHandle = keyHandle.GetPropertyAsIntPtr(KeyPropertyName.ProviderHandle, CngPropertyOptions.None);
                Marshal.InitHandle(providerHandle, rawProviderHandle);

                // If we're wrapping a handle to an ephemeral key, we need to make sure that IsEphemeral is
                // set up 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.

                var key = new CngKey(providerHandle, keyHandle);

                bool openingEphemeralKey = (keyHandleOpenOptions & CngKeyHandleOpenOptions.EphemeralKey) == CngKeyHandleOpenOptions.EphemeralKey;
                if (!key.IsEphemeral)
                {
                    if (openingEphemeralKey)
                    {
                        key.IsEphemeral = true;
                    }
                }
                else if (!openingEphemeralKey)
                {
                    throw new ArgumentException(SR.Cryptography_OpenEphemeralKeyHandleWithoutEphemeralFlag, nameof(keyHandleOpenOptions));
                }

                return key;
            }
            catch
            {
                // Make sure that we don't leak the handles
                providerHandle?.Dispose();
                keyHandle.Dispose();
                throw;
            }
        }
    }
}