How to implement NCryptEncrypt and NCryptDecrypt methods in C# using Microsoft Key Storage Provider?

SUBRAMANIAN Prabhakaran 1 Reputation point
2021-12-06T08:54:32.367+00:00

Hi Team,

I want to implement NCryptEncrypt and NCryptDecrypt methods using Microsoft Key Storage Provider.
Please find the below link.

https://learn.microsoft.com/en-us/windows/win32/api/ncrypt/nf-ncrypt-ncryptencrypt
https://learn.microsoft.com/en-us/windows/win32/api/ncrypt/nf-ncrypt-ncryptdecrypt

I have tried following code but in NCryptEncrypt method I don't know what are the parameter values needs to be passed?
Please help me on this.

using static PInvoke.NCrypt;

public static void Encrypt(string plainText)
{
SafeProviderHandle ProviderHandle;
SafeKeyHandle KeyHandle;
string KeyName = "SampleStrongKey";
byte[] plainText_bytes = Encoding.ASCII.GetBytes(plainText);
SECURITY_STATUS secStatus = NCryptOpenStorageProvider(out ProviderHandle, "Microsoft Software Key Storage Provider", 0);
secStatus = NCryptCreatePersistedKey(ProviderHandle, out KeyHandle, "AES", KeyName, LegacyKeySpec.AT_SIGNATURE, NCryptCreatePersistedKeyFlags.NCRYPT_OVERWRITE_KEY_FLAG);
secStatus = NCryptEncrypt(?????????);
}

Regards,
Prabhakaran

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,429 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,306 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Castorix31 81,831 Reputation points
    2021-12-06T09:58:05.947+00:00

    See the MS sample RSACapiAndCngInterop.cpp, mainly the EncryptWithCngDecryptWithCapi function


  2. Castorix31 81,831 Reputation points
    2021-12-06T12:15:06.84+00:00

    A quick conversion of MS code (Encrypt part) :

    (test in a click of a Button)

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    using System.Runtime.InteropServices;
    
    //https://github.com/Microsoft/Windows-classic-samples/blob/main/Samples/Security/RSACapiAndCngInterop/cpp/RSACapiAndCngInterop.cpp
    
    namespace CSharp_NCryptEncrypt
    {
        public partial class Form1 : Form
        {
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptOpenStorageProvider(out IntPtr phProvider, string pszProviderName, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptCreatePersistedKey(IntPtr hProvider, out IntPtr phKey, string pszAlgId, 
                string pszKeyName, int dwLegacyKeySpec, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptSetProperty(IntPtr hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptSetProperty(IntPtr hObject, string pszProperty, string pbInput, int cbInput, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptSetProperty(IntPtr hObject, string pszProperty, IntPtr pbInput, int cbInput, int dwFlags);
    
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptFinalizeKey(IntPtr hKey, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptEncrypt(IntPtr hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput,
                [In] IntPtr pvPadding, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput,
                [Out] out int pcbResult, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptExportKey(IntPtr hKey,  IntPtr hExportKey, string pszBlobType, IntPtr pParameterList,
                [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptDeleteKey(IntPtr hKey, int flags);
    
            [DllImport("Ncrypt.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern int NCryptFreeObject(IntPtr hObject);
    
            [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, bool Final, int dwFlags,
                [In, Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbData, [In, Out] int pdwDataLen);
    
    
            [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CryptImportKey(IntPtr hProv, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int dwDataLen, IntPtr hPubKey, int dwFlags, [Out] out IntPtr phKey);
    
            [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CryptDestroyKey(IntPtr hKey);
    
            [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CryptAcquireContextW(out IntPtr phProv, string szContainer, string szProvider, int dwProvType, int dwFlags);
    
            [DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CryptReleaseContext(IntPtr hProv,int dwFlags);
    
    
            public const string MS_KEY_STORAGE_PROVIDER   = "Microsoft Software Key Storage Provider";
            public const string MS_SMART_CARD_KEY_STORAGE_PROVIDER= "Microsoft Smart Card Key Storage Provider";
            public const string MS_PLATFORM_KEY_STORAGE_PROVIDER = "Microsoft Platform Crypto Provider";
            public const string MS_NGC_KEY_STORAGE_PROVIDER  = "Microsoft Passport Key Storage Provider";
    
            public const string NCRYPT_RSA_ALGORITHM = "RSA";
            public const string NCRYPT_AES_ALGORITHM = "AES";
    
            public const int NCRYPT_OVERWRITE_KEY_FLAG = 0x00000080;
    
            public const int NCRYPT_ALLOW_EXPORT_FLAG = 0x00000001;
            public const int NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG = 0x00000002;
            public const int NCRYPT_ALLOW_ARCHIVING_FLAG = 0x00000004;
            public const int NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG = 0x00000008;
    
            public const string NCRYPT_UI_POLICY_PROPERTY = "UI Policy";
            public const string NCRYPT_EXPORT_POLICY_PROPERTY = "Export Policy";
    
            public const int NCRYPT_PERSIST_FLAG = unchecked((int)0x80000000);
            public const int NCRYPT_PERSIST_ONLY_FLAG = 0x40000000;
    
            public const int NCRYPT_NO_PADDING_FLAG = 0x00000001;  // NCryptEncrypt/Decrypt
            public const int NCRYPT_PAD_PKCS1_FLAG = 0x00000002;  // NCryptEncrypt/Decrypt NCryptSignHash/VerifySignature
            public const int NCRYPT_PAD_OAEP_FLAG = 0x00000004;  // BCryptEncrypt/Decrypt
            public const int NCRYPT_PAD_PSS_FLAG = 0x00000008;  // BCryptSignHash/VerifySignature
            public const int NCRYPT_PAD_CIPHER_FLAG = 0x00000010;  // NCryptEncrypt/Decrypt
            public const int NCRYPT_ATTESTATION_FLAG = 0x00000020; // NCryptDecrypt for key attestation
            public const int NCRYPT_SEALING_FLAG = 0x00000100; // NCryptEncrypt/Decrypt for sealing
    
            public const string BCRYPT_RSAPUBLIC_BLOB = "RSAPUBLICBLOB";
            public const string BCRYPT_RSAPRIVATE_BLOB = "RSAPRIVATEBLOB";
            public const string LEGACY_RSAPUBLIC_BLOB = "CAPIPUBLICBLOB";
            public const string LEGACY_RSAPRIVATE_BLOB = "CAPIPRIVATEBLOB";
    
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private readonly byte[] Data = { 0x04, 0x87, 0xec, 0x66, 0xa8, 0xbf, 0x17, 0xa6, 0xe3, 0x62, 0x6f, 0x1a, 0x55, 0xe2, 0xaf, 0x5e, 0xbc, 0x54, 0xa4, 0xdc, 0x68, 0x19, 0x3e, 0x94 };
    
    
            private void button1_Click(object sender, EventArgs e)
            {
    
                // EncryptWithCngDecryptWithCapi 
    
                IntPtr CngProviderHandle = IntPtr.Zero;
                IntPtr CapiKeyHandle = IntPtr.Zero;
                int Policy = NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
                int ResultLength = 0;
                int BlobLength = 0;           
    
                int secStatus = NCryptOpenStorageProvider(out CngProviderHandle, MS_KEY_STORAGE_PROVIDER,0);
                if (secStatus == 0)
                {
                    secStatus = NCryptCreatePersistedKey(
                        CngProviderHandle,
                        out CapiKeyHandle,
                        NCRYPT_RSA_ALGORITHM,
                        "test",
                        0,
                        NCRYPT_OVERWRITE_KEY_FLAG);
    
                    byte[] bytesPolicy = BitConverter.GetBytes(Policy);  
                    secStatus = NCryptSetProperty(
                    CapiKeyHandle,
                    NCRYPT_EXPORT_POLICY_PROPERTY,
                    bytesPolicy,
                    bytesPolicy.Length,
                    NCRYPT_PERSIST_FLAG);
    
                    secStatus = NCryptFinalizeKey(CapiKeyHandle, 0);
    
                    int OutputLength = 0;
                    secStatus = NCryptEncrypt(
                        CapiKeyHandle,
                        Data,
                        Data.Length,
                        IntPtr.Zero,
                        null,
                        0,
                        out OutputLength,
                        NCRYPT_PAD_PKCS1_FLAG);
    
                    byte[] Output = new byte[OutputLength];
                    //IntPtr hGlobal = Marshal.AllocHGlobal(OutputLength);
                    //Output = (PBYTE)HeapAlloc(GetProcessHeap(), 0, OutputLength);
    
                    secStatus = NCryptEncrypt(
                    CapiKeyHandle,
                    Data,
                    Data.Length,
                    IntPtr.Zero,
                    Output,
                    OutputLength,
                    out ResultLength,
                    NCRYPT_PAD_PKCS1_FLAG);
    
                    secStatus = NCryptExportKey(
                        CapiKeyHandle,
                         IntPtr.Zero,
                        LEGACY_RSAPRIVATE_BLOB,
                        IntPtr.Zero,
                        null,
                        0,
                        out BlobLength,
                        0);
    
                    byte[] Blob = new byte[BlobLength];
                    secStatus = NCryptExportKey(
                        CapiKeyHandle,
                        IntPtr.Zero,
                        LEGACY_RSAPRIVATE_BLOB,
                        IntPtr.Zero,
                        Blob,
                        BlobLength,
                        out BlobLength,
                        0);
    
                    // Decrypt to be added...
    
                    // ReverseBytes(Output, OutputLength);
    
                    //if (CngTmpKeyHandle != IntPtr.Zero)
                    //{
                    //    CryptDestroyKey(CngTmpKeyHandle);
                    //}
                    //if (CapiLocProvHandle != IntPtr.Zero)
                    //{
                    //    CryptReleaseContext(CapiLocProvHandle, 0);
                    //}
    
                    if (CapiKeyHandle != IntPtr.Zero)
                    {
                        NCryptDeleteKey(CapiKeyHandle, 0);
                    }
    
                    if (CngProviderHandle != IntPtr.Zero)
                    {
                        NCryptFreeObject(CngProviderHandle);
                    }
                }
            }
        }
    }