Attempt to set permissions on a KeyContainer in C# is having no effect
Asked Answered
V

2

8

I'm using the following code in an attempt to programatically allow the NetworkService account to have access to a key:

var RSA = new RSACryptoServiceProvider(
   new CspParameters() { 
     KeyContainerName = "MyEncryptionKey", 
     Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore 
});

RSA.CspKeyContainerInfo.CryptoKeySecurity.AddAccessRule(
  new System.Security.AccessControl.CryptoKeyAccessRule(
    new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null),
    CryptoKeyRights.GenericAll,
    AccessControlType.Allow
  )
);

This code runs without error, but has no effect on the key container's permissions.

However, using the commandline tool aspnet_regiis to do the same thing, works perfectly:

aspnet_regiis -pa "MyEncryptionKey" "NetworkService"

I'm running with full admin rights - if I don't run with those rights, then an exception is thrown. I'm also running as the user that initially created the key.

The key container always has the following access rules:

S-1-5-18         -> LocalSystem
S-1-5-32-544     -> Administrators
S-1-5-5-0-135377 -> MyUser

With aspnet_regiis, the SID, S-1-5-20 gets added to this list. I can't affect it from code.

I've tried creating the security identifier from the sid in string format, as well as using SetAccessRule instead of AddAccessRule.

Any ideas how to actually affect this ACL list from code?

Velites answered 29/6, 2010 at 16:36 Comment(0)
F
10

You do not appear to be calling Persist. The changes you make to the CryptoKeySecurity do not actually get saved immediately. You need to use one of the Persist(...) methods to actually save the changes.

NativeObjectSecurity.Persist Method (String, AccessControlSections)

It seems these API's follow a rather convoluted approach to modification. You need to create a CspParameters first, apply the necessary changes, then construct the provider from those parameters. Construction invokes an update on the container.

var params = new CspParameters
{
     KeyContainerName = "MyEncryptionKey", 
     Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore    
};

params.CryptoKeySecurity.AddAccessRule(
  new System.Security.AccessControl.CryptoKeyAccessRule(
    new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null),
    CryptoKeyRights.GenericAll,
    AccessControlType.Allow
  )
);

var RSA = new RSACryptoServiceProvider(params);
Favin answered 29/6, 2010 at 16:46 Comment(4)
Sadly all the Persist methods are Protected.Velites
It looks like you need to modify the security before creating the provider using CspParameters. The CspParameters, when passed to the RSACryptoServiceProvider, invoke a persist. VERY odd way to build an API, but according to the documentation, that seems to be how it works. Answer updated to reflect.Favin
That got me on the right track. The CryptoKeySecurity property as used above is null, so throws. You can create a new one, but it blats out any permissions that already existed (this was the first actual effect it had, so now I'm locked out of my main keystore ;). But ... if you get the key from the keystore, then create a new CspParamters by copying the values from the RSA.CspKeyContainerInfo (including provider name & type, container name and CryptoKeySecurity), then you can modify it, create another RSA key object using the cspparameters, and voila, all done. simples ... ?Velites
It also helps to add the UseMachineKeyStore flag if you are indeed using a machine key.Velites
G
2

I just wanted to formalize what Jim T stated in the comments since it worked for me.

//Read the current settings
CspParameters csp = new CspParameters(PROVIDER_RSA_FULL)
{
    KeyContainerName = container,
    Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseExistingKey
};
//Retrieve Current Settings
using (var rsa = new RSACryptoServiceProvider(csp))
{
    var ci = rsa.CspKeyContainerInfo;

    //Create new settings and copy values over
    CspParameters csp2 = new CspParameters(PROVIDER_RSA_FULL)
    {
        KeyContainerName = container,
        Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseExistingKey,
        CryptoKeySecurity = ci.CryptoKeySecurity,
        ProviderName = ci.ProviderName,
        ProviderType = ci.ProviderType
    };
    //Add Permissions
    csp2.CryptoKeySecurity.AddAccessRule(new CryptoKeyAccessRule(securityIdentifier, CryptoKeyRights.FullControl, AccessControlType.Allow));

    //Save settings
    using (var rsa2 = new RSACryptoServiceProvider(csp2))
    {
    }
}
Gat answered 19/2, 2019 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.