none
Object already exists with RSACryptoServiceProvider RRS feed

  • Question

  •  

    After beating my head against the wall for half a day I finally figured out why I am getting an exception "Object already exists." when trying to instantiated an RSACryptoServiceProvider.

     

    Here was my C# code from .NET 1.1 which worked fine under my ASPNET windows user account:

    CspParameters csp = new CspParameters();

    csp.KeyContainerName = "MySecretKeyContainer";

    svrRSA = new RSACryptoServiceProvider(csp);

     

    When I ran this same code in .NET 2.0 also under my ASPNet windows user account.

    I got the following exception and stack trace:

     

    Object already exists.

     

    Stack Trace:
       at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
       at System.Security.Cryptography.Utils._CreateCSP(CspParameters param, Boolean randomKeyContainer, SafeProvHandle& hProv)
       at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
       at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
       at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
       at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
       at System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters)

     

     

    It turns out that I fixed the problem by changing the code to read as follows:

    CspParameters csp = new CspParameters();

    csp.Flags = CspProviderFlags.UseMachineKeyStore;

    csp.KeyContainerName = "ActivSvrV1";

    svrRSA = new RSACryptoServiceProvider(csp);

     

    Which is to say I added the line:

    csp.Flags = CspProviderFlags.UseMachineKeyStore;

     

    I would assume that between .NET 1.1 and .NET 2.0 Microsoft changed the default constructor for CspParameters from defaulting to the machine key store to not using the machine key store.

     

    My guess is that this was done to improve security.  After all we wouldn't want someone to use the default constructor assuming they are the only user with access to the key store when in fact they may not be.

     

    The confusing thing is that when I run the same code under a windows user with Administrator rights I get an exception:  Keyset does not exist

     

    So perhaps setting to machine key store doesn't really create a security issue.

     

    I'm thinking that perhaps the file or reg location that the key container is stored in isn't cryptographically protected on a user by user basis but may be protected with NT permissions.

     

    Could anyone from Microsoft provide an answer to this, or perhaps why the default behavior of the CspParameters contructor has changed?

     

     

    Tuesday, July 17, 2007 2:53 AM

All replies

  • I had a similar problem, I tested a few files in the development server thing, then moved the files to the iis directory, tried to run and got the object already exists error. The file that handled the cryptography was exactly the same, all I had done was change directory so I don't think the problem is the flags. Try it with the Flags thing in 1.1, then try the same code in 2.0 and I think it will fail again.

    The other thing you changed, and I believe it's that that made it work, is the KeyContainerName, in effect you made another object.
    Tuesday, July 24, 2007 6:05 AM
  • The root cause of the “Object already exists” error when attempting to access a machine key store container, is caused by permissions on the Machine key store file

     

    When windows creates a Machine key store, it creates a file in “Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\ Machine Keys” Directory

     

    The file is created with permissions for the creating user account, and the system account only. This means that if any other user tries to use this key store, they will get a permission error, which manifests it’s self as a  “Object already exists” exception back to the calling code.

     

    I think the reason that this happened to you, is that the ASP.NET account name changed between a .net 1.1 and 2.0 ( I think? Or maybe it was from IIS 5.0 to 6.0… any way….)

    This means that you created the key container in .net 1.1, under one account, and then tried to access it in 2.0 as a different account, an account that wasen't on the key containers ACL.

     

    When you created a container with a new name, this container was created under the same new ASP.NET account, and hence could be created with this account also.

     

    I must say this I think this is quite badly documented by MS. Most of the references to the Machine store give you the impression it is available to any account on a machine. Here is an article have found that explains it well.

     

    http://msdn.microsoft.com/en-us/library/yxw286t2(VS.80).aspx

     

    See the section on “Granting authority to access a key container”

     

    Ed

     

    Monday, May 12, 2008 8:34 AM
  • Thank you!!!  I've been searching for a long time for this fix!
    Wednesday, March 4, 2009 7:20 PM
  • Thank you!!!  I've been searching for a long time for this fix!
    Wednesday, March 4, 2009 7:20 PM
  • Hello C.P.T,

    I wrote a method that attempts to correct the permissions issue.  Try it and improve upon it as you see fit.  The trick is that it finds the key file by searching for it's KeyContainerName.  Assuming your key container name is a UFT-8 string (and the current user context has Read|Change Permission writes to the file, it will always find the file and update the permissions.  It has not failed me yet when I use in in the back-end of my ASP.NET apps.

    internal static bool TryKeyContainerPermissionCheck(string secretKeyName) {
    
    	bool returnValue = false;
    
    	WindowsIdentity current = WindowsIdentity.GetCurrent();
    
    	WindowsPrincipal currentPrincipal = new WindowsPrincipal(current);
    
    	if (currentPrincipal.IsInRole(WindowsBuiltInRole.Administrator)) {
    		try {
    			foreach (string fileName in Directory.GetFiles(
    				@"C:\Documents and Settings\All Users\" +
    				@"Application Data\Microsoft\Crypto\RSA\MachineKeys")) {
    				FileInfo fi = new FileInfo(fileName);
    
    				if (fi.Length <= 1024 * 5) { // no key file should be greater then 5KB
    					using (StreamReader sr = fi.OpenText()) {
    						string fileData = sr.ReadToEnd();
    						if (fileData.Contains(secretKeyName)) { // this is our file
    
    							FileSecurity fileSecurity = fi.GetAccessControl();
    
    							bool currentIdentityFoundInACL = false;
    							foreach (FileSystemAccessRule rule in fileSecurity
    								.GetAccessRules(
    									true,
    									true,
    									typeof(NTAccount)
    								)
    							) {
    								if (rule.IdentityReference.Value.ToLower() ==
    									current.Name.ToLower()
    								) {
    									returnValue = true;
    									currentIdentityFoundInACL = true;
    									break;
    								}
    							}
    
    							if (!currentIdentityFoundInACL) {
    								fileSecurity.AddAccessRule(
    									new FileSystemAccessRule(
    										current.Name,
    										FileSystemRights.FullControl,
    										AccessControlType.Allow
    									)
    								);
    
    								fi.SetAccessControl(fileSecurity);
    
    								returnValue = true;
    							}
    
    							break;
    						}
    					}
    				}
    			}
    		} catch (UnauthorizedAccessException) {
    			throw;
    		} catch { }
    	}
    
    	return returnValue;
    }
    


    - Rashad Rivera www.omegusprime.com
    • Proposed as answer by Rashad Rivera Saturday, January 2, 2010 3:36 AM
    • Edited by Rashad Rivera Saturday, January 2, 2010 3:40 AM One last attempt to correct formatting.
    Saturday, January 2, 2010 3:34 AM
  • Outstanding, this is exactly what my problem is too. I tried to encrypt application config settings using the rsa provider. I ran it as one user then logged in as a different user on the same machine and ran it. Never even crossed my mind that this was the cause.

     

    Cheers,

     

    Ed (another Ed)

    Monday, October 11, 2010 4:37 PM
  • I solved the issue running Visual studio as Administrator
    Tuesday, February 14, 2012 5:34 AM
  • rashad rivera your code doesn't work. for anyways. i've tried it and it throws an error on following line.

    using (StreamReader sr = fi.OpenText())

    it throws the following exception:

    Access to the path 'C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys\046fad8bbb223e378d67e26b123ad750_082bae7b-731a-4414-8ac9-0a64851894ba' is denied.

    i'm running visual studio as administrator and my account is in the administrators group

    Sunday, September 30, 2012 11:55 PM
  • Paxa,

    You are correct Sir, besides Read and Change Permission writes, the executing context requires Read Permission writes as well.  Nevertheless, you are assuming that being an Administrator means you have Read, Read Permission and Change Permission writes.  Try checking you program and ensure the user context executing the function has these writes.

    Also, this is a code example.  If you have a better method, please share or improve on the above example. 


    - Rashad Rivera www.omegusprime.com


    • Edited by Rashad Rivera Monday, October 1, 2012 6:13 AM updated comment
    Monday, October 1, 2012 6:12 AM