.Net CryptographicException "Object already exists" on Azure

1.5k Views Asked by At

We have a website deployed on two web apps on Azure : a production and a pre-production version.

The website at certain time creates a container to host RSA keys, using the below code :

// -----------------------------
// Part 1 : Initialize csp params
// -----------------------------
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "OurKeyContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";

// --------------------------------------------------
// Part 2 : A try to set folder access rights to "everyone"
// --------------------------------------------------
// http://whowish-programming.blogspot.fr/2010/10/systemsecuritycryptographycryptographic.html
// http://stackoverflow.com/questions/5013881/c-sharp-how-do-i-get-the-everybody-user
var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var rule = new CryptoKeyAccessRule(sid, CryptoKeyRights.FullControl, AccessControlType.Allow);
cspParams.CryptoKeySecurity = new CryptoKeySecurity();
cspParams.CryptoKeySecurity.SetAccessRule(rule);

return new RSACryptoServiceProvider(cspParams);

The issue is that this code works only for one of the website, the one that was first launched actually. The second one threw a CryptographicException "Object already exists".

After googling , the issue seems to be caused by users executing the website that do not have right to access the key container, but unfortunately the recommended fix (implemented in Part 2 on our code above) does not work ..

Any idea or advice ?

Thanks

Riana

1

There are 1 best solutions below

2
On BEST ANSWER

I think the problem is that you are basically trying to create a Machine wide container with the same name two times. I am assuming that the pre-prod and prod environments are on the same machine, perhaps as deployment slots in your App Service? I could replicate your problem with this setup at least. There are some other setups that could produce the same situation I guess, but this is the most likely.

There are three things you can change in this setup that will produce different results:

  • Identity to grant access
  • Container store
  • Container name

For identity we can use either Everyone (as you have tried) or Current which we can get from WindowsIdentity

    private IdentityReference GetWindowsIdentity()
    {
        return System.Security.Principal.WindowsIdentity.GetCurrent().User;
    }

    private IdentityReference GetEveryoneIdentity()
    {
        return new SecurityIdentifier(WellKnownSidType.WorldSid, null);
    }

The container store can be either machine wide or just for the current user

// machine wide store
var cspParams = new CspParameters(PROVIDER_RSA_FULL)
{
    ...
    Flags = CspProviderFlags.UseMachineKeyStore;
    ...
};

// default store
var cspParams = new CspParameters(PROVIDER_RSA_FULL)
{
    ...
    Flags = CspProviderFlags.UseDefaultKeyContainer;
    ...
};

And for the store name we can choose something, in your case you choose the same but we can set something that is unique for the identity as well

    KeyContainerName = "OurKeyContainer",

    KeyContainerName = $"OurKeyContainer-{identity}",

The different combinations will produce different results:

Everyone identity, machine wide store, same container name Fails on one of the slots with System.Security.Cryptography.CryptographicException: Object already exists.

Everyone identity, machine wide store, container per identity OK

Everyone identity, user store, same container name Fails on both of the slots with System.Security.Cryptography.CryptographicException: The system cannot find the file specified.

Everyone identity, user store, container per identity OK

Current identity, machine wide store, same container name Fails on one of the slots with System.Security.Cryptography.CryptographicException: Object already exists.

Current identity, machine wide store, container per identity OK

Current identity, user store, same container name Fails on both of the slots with System.Security.Cryptography.CryptographicException: The system cannot find the file specified.

Current identity, user store, container per identity Fails on both of the slots with System.Security.Cryptography.CryptographicException: The system cannot find the file specified.

So, to summarize, changing the name of the container to include something that is unique for the environment will fix the problem. It doesn't have to be the identity (but you will get one identity per App Service you are running on a machine so it is fairly safe), it could be the name of the environment if you set it as an ENVIRONMENT VARIABLE in your App Service and make sure it is set to pre-prod and prod respectively.

Here is the playaround code to test it