File: System\Security\Cryptography\CngKey.Import.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 Internal.Cryptography;
using Microsoft.Win32.SafeHandles;
using ErrorCode = Interop.NCrypt.ErrorCode;

namespace System.Security.Cryptography
{
    /// <summary>
    ///     Managed representation of an NCrypt key
    /// </summary>
    public sealed partial class CngKey : IDisposable
    {
        //
        // Import factory methods
        //
        internal static CngKey Import(ReadOnlySpan<byte> keyBlob, CngKeyBlobFormat format)
        {
            return Import(keyBlob, null, format, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }

        [SupportedOSPlatform("windows")]
        public static CngKey Import(byte[] keyBlob, CngKeyBlobFormat format)
        {
            return Import(keyBlob, format, provider: CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }

        internal static CngKey Import(byte[] keyBlob, string? curveName, CngKeyBlobFormat format)
        {
            return Import(keyBlob, curveName, format, provider: CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }

        [SupportedOSPlatform("windows")]
        public static CngKey Import(byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider)
        {
            return Import(keyBlob, null, format, provider);
        }

        internal static CngKey ImportEncryptedPkcs8(
            ReadOnlySpan<byte> keyBlob,
            ReadOnlySpan<char> password)
        {
            return ImportEncryptedPkcs8(keyBlob, password, CngProvider.MicrosoftSoftwareKeyStorageProvider);
        }

        internal static unsafe CngKey ImportEncryptedPkcs8(
            ReadOnlySpan<byte> keyBlob,
            ReadOnlySpan<char> password,
            CngProvider provider)
        {
            SafeNCryptProviderHandle providerHandle = provider.OpenStorageProvider();
            SafeNCryptKeyHandle keyHandle;

            using (SafeUnicodeStringHandle passwordHandle = new SafeUnicodeStringHandle(password))
            {
                Interop.NCrypt.NCryptBuffer* buffers = stackalloc Interop.NCrypt.NCryptBuffer[1];

                buffers[0] = new Interop.NCrypt.NCryptBuffer
                {
                    BufferType = Interop.NCrypt.BufferType.PkcsSecret,
                    cbBuffer = checked(2 * (password.Length + 1)),
                    pvBuffer = passwordHandle.DangerousGetHandle(),
                };

                if (buffers[0].pvBuffer == IntPtr.Zero)
                {
                    buffers[0].cbBuffer = 0;
                }

                Interop.NCrypt.NCryptBufferDesc desc = new Interop.NCrypt.NCryptBufferDesc
                {
                    cBuffers = 1,
                    pBuffers = (IntPtr)buffers,
                    ulVersion = 0,
                };

                ErrorCode errorCode = Interop.NCrypt.NCryptImportKey(
                    providerHandle,
                    IntPtr.Zero,
                    Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
                    ref desc,
                    out keyHandle,
                    ref MemoryMarshal.GetReference(keyBlob),
                    keyBlob.Length,
                    0);

                if (errorCode != ErrorCode.ERROR_SUCCESS)
                {
                    keyHandle.Dispose();
                    providerHandle.Dispose();
                    throw errorCode.ToCryptographicException();
                }
            }

            CngKey key = new CngKey(providerHandle, keyHandle);
            key.IsEphemeral = true;
            return key;
        }

        internal static CngKey Import(
            byte[] keyBlob,
            string? curveName,
            CngKeyBlobFormat format,
            CngProvider provider)
        {
            ArgumentNullException.ThrowIfNull(keyBlob);

            return Import(new ReadOnlySpan<byte>(keyBlob), curveName, format, provider);
        }

        internal static CngKey Import(
            ReadOnlySpan<byte> keyBlob,
            string? curveName,
            CngKeyBlobFormat format,
            CngProvider provider)
        {
            ArgumentNullException.ThrowIfNull(format);
            ArgumentNullException.ThrowIfNull(provider);

            SafeNCryptProviderHandle providerHandle = provider.OpenStorageProvider();
            SafeNCryptKeyHandle? keyHandle = null;
            try
            {
                ErrorCode errorCode;

                if (curveName == null)
                {
                    errorCode = Interop.NCrypt.NCryptImportKey(
                        providerHandle,
                        IntPtr.Zero,
                        format.Format,
                        IntPtr.Zero,
                        out keyHandle,
                        ref MemoryMarshal.GetReference(keyBlob),
                        keyBlob.Length,
                        0);

                    if (errorCode != ErrorCode.ERROR_SUCCESS)
                    {
                        providerHandle.Dispose();
                        keyHandle.Dispose();
                        throw errorCode.ToCryptographicException();
                    }
                }
                else
                {
                    keyHandle = ECCng.ImportKeyBlob(format.Format, keyBlob, curveName, providerHandle);
                }

                CngKey key = new CngKey(providerHandle, keyHandle);

                // We can't tell directly if an OpaqueTransport blob imported as an ephemeral key or not
                key.IsEphemeral = format != CngKeyBlobFormat.OpaqueTransportBlob;

                return key;
            }
            catch
            {
                keyHandle?.Dispose();
                providerHandle.Dispose();
                throw;
            }
        }
    }
}