Hi, welcome back,
I will talk today about a very common issue we face when we try to use .NET's RSACryptoServiceProvider (http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx) class with mandatory profiles (i.e. Citrix environment).
When we try to create a new RSACryptoServiceProvider object in this scenario, we get the following exception:
"System.Security.Cryptography.CryptographicException: Cryptographic Service Provider (CSP) for this implementation could not be acquired".
This behavior is by design for security protection.
RSACryptoServiceProvider calls CryptAcquireContext API (http://msdn2.microsoft.com/en-us/library/aa379886.aspx) behind the scenes to get a handle to a key container within a CSP (Cryptographic Service Provider). CryptAcquireContext will fail with NTE_TEMPORARY_PROFILE error when called from a mandatory profile.
Mandatory profiles are read-only user profiles. Since changes to the mandatory profile cannot be saved, PKI design doesn't allow this operation, and CryptAcquireContext prevents this scenario by failing.
Private keys are protected by DPAPI (Data Protection API) component of Windows. DPAPI component regularly creates new Master Keys in the profile to protect confidential information such as private keys. For security reasons, Master Keys will expire, which means that after a period of time, the hard-coded value being three months, a new Master Key is generated and protected in the same manner. This expiration prevents an attacker from compromising a single Master Key and accessing all of a user's protected data.
The Master Key regeneration is explained in the DPAPI white-paper at:
Windows Data Protection
So, higher level components that depend on DPAPI to protect confidential information cannot be protected securely with read-only profiles.
More information about all this can be found here:
Cryptographic Keys and Certificates Cannot Be Used with Mandatory Profiles
How to troubleshoot the Data Protection API (DPAPI)
"DPAPI and Mandatory Profiles"
Cannot Gain Access to Microsoft Encrypted File Systems
There are three possible solutions to this issue:
1) If you need to access some user’s private keys (i.e. when signing data) and you want to prevent other users from using its keys, the only way is configuring the profile as non-mandatory.
2) If you need a persistent RSA private key, then the only solution is to use CryptAcquireContext with the CRYPT_MACHINE_KEYSET flag (or RSACryptoServiceProvider with the CspProviderFlags.UseMachineKeyStore flag) for private key storage in this configuration. But this is not protected based on the user's password hash, so any user with access to the machine may have access to those keys.
3) If you don’t need persistent RSA private keys (i.e. when verifying signatures), you could use CryptAcquireContext with the CRYPT_VERIFYCONTEXT flag. This flag tells CryptoAPI to create the key container in memory and not in the user’s profile. And you won't need to worry about deleting the key container; you just release the handle with CryptReleaseContext (http://msdn2.microsoft.com/en-us/library/aa380268.aspx).
Additional info about CryptAcquireContext & CRYPT_VERIFYCONTEXT can be found here:
238187 CryptAcquireContext() use and troubleshooting
The problem is that, at the moment of this writing, creating a temporary key container with RSACryptoServiceProvider is not possible. We may think of using CryptAcquireContext with CRYPT_VERIFYCONTEXT flag to get a handle to the key container within the CSP before using RSACryptoServiceProvider, but RSACryptoServiceProvider maintains its own cryptographic handle and it's not possible to give RSACryptoServiceProvider a handle to use. We have already requested .NET Framework developers to expose CRYPT_VERIFYCONTEXT for us, but I can't tell when we will get such a feature.
The way to i.e. verify signatures using temporary keysets in .NET would be to use CryptoAPI directly through P/Invoke (Platform Invoke), instead of using RSACryptoServiceProvider.
I hope this helps.
Alex (Alejandro Campos Magencio)
PS: In a near future I will post how we can easily see which CryptoAPI calls .NET is making behind the scenes. When a CryptoAPI call fails, .NET captures the error and it returns its own exception, which is not always self-explanatory as we've seen today. We will be able to see the failing CryptoAPI, and which error is actually returning. This way, if we see a "Cryptographic Service Provider (CSP) for this implementation could not be acquired" error, we will be able to see for instance that CryptAcquireContext is failing with NTE_TEMPORARY_PROFILE and that will give us some clues about what's going on there.