1

I've created a machine wide CngKey (MachineKey=true), but my applications aren't able to access it.

How to I assign permissions to so that my App Pool can access the key? Preferably pragmatically so that I can build it into the installer.

Powershell create script:

[System.Security.Cryptography.CngKeyCreationParameters] $cngKeyParameter =  [System.Security.Cryptography.CngKeyCreationParameters]::new()
    $cngKeyParameter.KeyUsage = [System.Security.Cryptography.CngKeyUsages]::AllUsages
    $cngKeyParameter.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowPlaintextExport

    $cngKeyParameter.Provider = [System.Security.Cryptography.CngProvider]::MicrosoftSoftwareKeyStorageProvider
    $cngKeyParameter.UIPolicy = [System.Security.Cryptography.CngUIPolicy]::new([System.Security.Cryptography.CngUIProtectionLevels]::None)
    $cngKeyParameter.KeyCreationOptions = [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey

    #Create Cng Property for Length, set its value and add it to Cng Key Parameter
    [System.Security.Cryptography.CngProperty] $cngProperty = [System.Security.Cryptography.CngProperty]::new($cngPropertyName, [System.BitConverter]::GetBytes(2048), [System.Security.Cryptography.CngPropertyOptions]::None)
    $cngKeyParameter.Parameters.Add($cngProperty)

    #Create Cng Key for given $keyName using Rsa Algorithm
    [System.Security.Cryptography.CngKey] $key = [System.Security.Cryptography.CngKey]::Create([System.Security.Cryptography.CngAlgorithm]::Rsa, "MyKey", $cngKeyParameter)
Swifty
  • 1,422
  • 2
  • 18
  • 38

1 Answers1

7

The permissions for a CNG key are a bit indirect.

If you know the full set of permissions you want to apply you can do it at creation (you'll have to translate the C# to PowerShell, sorry):

CryptoKeySecurity sec = new CryptoKeySecurity();

sec.AddAccessRule(
    new CryptoKeyAccessRule(
        new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null),
        CryptoKeyRights.FullControl,
        AccessControlType.Allow));

sec.AddAccessRule(
    new CryptoKeyAccessRule(
        new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null),
        CryptoKeyRights.GenericRead,
        AccessControlType.Allow));

const string NCRYPT_SECURITY_DESCR_PROPERTY = "Security Descr";
const CngPropertyOptions DACL_SECURITY_INFORMATION = (CngPropertyOptions)4;

CngProperty permissions = new CngProperty(
    NCRYPT_SECURITY_DESCR_PROPERTY,
    sec.GetSecurityDescriptorBinaryForm(),
    CngPropertyOptions.Persist | DACL_SECURITY_INFORMATION);

cngKeyParameter.Parameters.Add(permissions);

If you want to append a rule later (such as after creating it with the default permissions):

CngProperty prop = key.GetProperty(NCRYPT_SECURITY_DESCR_PROPERTY, DACL_SECURITY_INFORMATION);
CryptoKeySecurity sec = new CryptoKeySecurity();
sec.SetSecurityDescriptorBinaryForm(prop.GetValue());

sec.AddAccessRule(
    new CryptoKeyAccessRule(
        new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null),
        CryptoKeyRights.GenericRead,
        AccessControlType.Allow));

CngProperty newProp = new CngProperty(
    prop.Name,
    sec.GetSecurityDescriptorBinaryForm(),
    CngPropertyOptions.Persist | DACL_SECURITY_INFORMATION);

key.SetProperty(newProp);
bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Thanks bartonjs. I added the permissions as you suggested. It executed without error. But I was still unable to access it. I'm using the key to encrypt a db with always encrypted. The master encryption key creates without error, but when I try to create the Column encryption key, I get the following error: "An error occurred while opening the Microsoft Cryptography API: Next Generation (CNG) key: 'Microsoft Software Key Storage Provider/CNG_Test'. Verify that the CNG provider name 'Microsoft Software Key Storage Provider' is valid, installed on the machine, and the key 'CNG_Test' exists." – Swifty Jun 26 '18 at 07:22
  • @Swifty sounds like it’s looking for a user key. But if the permissions change worked now you’re into the realm of a SqlServer-specific problem that I can’t help with - and would probably be a different question. – bartonjs Jun 26 '18 at 15:31
  • Yes, I believe you are right. I was able to use the code you supplied to add in a work around. It allows me to open the machine key manually with the network service and then import a copy of it into the network service's key store. It's not the best solution, but the only one until SQL allows the use of machine keys. Thanks for the help. – Swifty Jun 27 '18 at 06:40
  • @bartonjs - When using similar access rules as above, the access for the admin account could not delete the key, even with FullControl specified. If I instead generated the key and then used the second approach, the key retained its ability to be deleted by the admin account. Do you know of any obvious reasons why that might be? – Rich B Dec 14 '19 at 07:32
  • 2
    For anyone trying to use that code in .Net5. You can replace CryptoKeySecurity/CryptoKeyAccessRule with FileSecurity/FileSystemAccessRule and it will work just fine, provided that you use the Microsoft Software Key Storage Provider (which is the default). – Angel Yordanov Jun 04 '21 at 07:58