CngKey.Import() throws an exception saying “An internal error occurred” when importing a key-blob under a non-admin user from a WCF web service

This was a typical error reported by a customer on Windows Server 2008 R2 with IIS 7.5.

I created a user named TestCngKey which is a non-admin user as shown in the screen shot below.

I tested twice. The first time I logged onto the machine as a domain user. I was not able to reproduce the error as shown in the screen shot below. It showed me success.

I also logged onto the same machine as TestCngKey and performed the steps below:

1. I opened a browser and opened the URL <localhost/TestCngKey.svc>. This worked.

2. I opened WebService Studio and used the link <localhost/TestCngKey.svc> and performed the same repro steps. I never got the error/ exception as stated by the customer. Please see the screen shot
below.

Here is a typical repro code:

using System;

using System.Security.Cryptography;

namespace CryptographyTester

{

    class TestCng

    {

        static void Main()

        {

            byte[] keyBlob = GetCngPrivateKey();

            try

            {

                //This line fails ("An internal error occured") for a key pair, until it is run as admin once on the machine. After that, works fine.

                CngKey cngKey = CngKey.Import(keyBlob, CngKeyBlobFormat.EccPrivateBlob);

                //If the machine restarts, needs to run as admin again. 

                Console.WriteLine("Successful");

            }

            catch (Exception ex)

            {                Console.WriteLine(ex.ToString()); 

            }

        }

 

        private static byte[] GetCngPrivateKey()

        {

            byte[] key = new byte[104];

            //Same key needs to be used on multiple machines. It is encrypted and stored in config file.

            // This app decrypts and constructs the key blob as below.

            byte[] privateKey = new byte[] { 0x11, 0xBB, 0xC3, 0x67, 0x22, 0x8F, 0x01, 0x58, 0xE8, 0x7B, 0xF8, 0xDE, 0x89, 0x5F, 0x12, 0xE9, 0xA8, 0x24, 0x2F, 0x22, 0x1D, 0x14, 0xD3, 0xDF, 0x80, 0x41, 0xA3, 0x26, 0x9C, 0xD8, 0x04, 0x1E };

            byte[] publicKey = new byte[] { 0x50, 0x79, 0xE2, 0x98, 0xB0, 0xBF, 0x93, 0xEF, 0x0F, 0x06, 0xA9, 0xF5, 0x7E, 0xB3, 0xF6, 0x2A, 0x36, 0xEF, 0x4D, 0x76, 0x91, 0xFB, 0x8D, 0x6E, 0xBA, 0x56, 0xAC, 0xEF, 0x99, 0xE4, 0xE4, 0xF8, 0x9C, 0xEE, 0xA6, 0x5F, 0x14, 0xB8, 0x87, 0x04, 0xD3, 0xBB, 0xA5, 0x42, 0x24, 0x34, 0x78, 0xEB, 0xCB, 0xEA, 0x39, 0xD7, 0xDC, 0xBB, 0x16, 0xA7, 0xAA, 0xAD, 0xD0, 0xC6, 0xA3, 0x10, 0xA4, 0x93 };

            byte[] preFix = new byte[] { 0x45, 0x43, 0x53, 0x32, 0x20, 0x00, 0x00, 0x00 };

             Array.Copy(preFix, key, preFix.Length);

            Array.Copy(publicKey, 0, key, preFix.Length, publicKey.Length);

            Array.Copy(privateKey, 0, key, preFix.Length + publicKey.Length, 32);

            return key;

        }

    }

}

Not being able to reproduce the issue, took a dump of the above process on the customers machine. Here is the failing call-stack snippet:

Child-SP          RetAddr           Call Site

00000000`0265d780 000007fe`fc631a0f ncrypt!StartKeyIsoService+0x38

<snip>

00000000`0265d800 000007fe`fc619a4c ncrypt!LoadProvider+0x3e98

00000000`0265d870 00000000`027dbc10 ncrypt!OpenStorageProvider+0x127

It shows that the process is trying to communicate with the “CNG Key Isolation Service” via RPC and is failing. Figured out that the CNG key isolation service was not running on the customer’s machine.

So here is how it works:

The CryptoAPI key was being translated to a CNG key. During that process, the application attempts to communicate with the CNG Key Isolation service. If the service is not running the MS code attempts to start the service. Since the user is not an administrator this failed. The customer's CNG Key Isolation service was configured to start manually so they modified the behavior to start the service automatically.

The windows service - "CNG Key Isolation" - needs to be running, to successfully execute the call to CngKey.Export() method.

If the service was not running, it is started if the app pool identity is an admin or a built-in account.

When the app pool identity is any other non-admin user, it fails to start the CNG Key Isolation service. This results in the exception “An internal error occurred”.

 

Thanks,

Shamik