Last week (ok, really two weeks ago ....), I wrote about using DPAPI with Whidbey. (You can find that post here: Managed DPAPI Part I: ProtectedData). In addition to the ProtectedData class, Whidbey will also expose DPAPI through the ProtectedMemory class. In this post I'll show some samples of using ProtectedMemory and compare it to ProtectedData to help in making decisions as to which one to use in your managed application.
ProtectedMemory is a wrapper around CryptProtectMemory and CryptUnprotectMemory. The main difference between ProtectedMemory and ProtectedData is that if the computer is rebooted between calls to Protect and Unprotect, you won't be able to decrypt the data. This means that if you need to encrypt some data and save it to a file or other persistent storage, you'll need to use ProtectedData instead. However if the data you're encrypting is for use just while the application is running, ProtectedMemory is slightly easier to use, and is a good choice.
Like the ProtectedData class, ProtectedMemory contains two static methods Protect and Unprotect. Since there's no MSDN docs for Whidbey yet, I'll provide their signatures here:
The corresponding Unprotect method is:
As you can see, ProtectedMemory is even simpler than ProtectedData. The encrypted data overwrites the original data, so you don't have to worry about destroying your original array. Also, there's no entropy to deal with in these APIs. The scope parameter has one of the following three values:
Generally you'll want to use MemoryProtectionScope.SameProcess. When unprotecting your data, you'll need to provide the same MemoryProtectionScope, otherwise the decryption won't work. Also, if you're writing a server application that recieves some data from a user application (say encrypted with MemoryProtectionScope.SameLogon), you'll need to impersonate the user that the other process is running as before decrypting.
One slight gotchya that exists with ProtectedMemory is that it will only work on blocks of 16 bytes, so you may have to pad your array before using it. Here's some sample code:
The output from this code is:
There are some things to be aware of when using ProtectedMemory. Since your data needs to be decrypted for you to use it, there will be some window of time that it exists in plaintext in memory and can be found with a debugger. During the time that your array is unencrypted, you're also vulnerable to some other problems. Since managed arrays are under the control of the garbage collector, if they aren't pinned, they may be moved around memory. In this case, even though you've protected your copy of the array, other unencrypted “images” of the sensitive data may still exist. And of course, there's the possibility that Windows swaps your process out of memory into the swap file while your data is unencrypted. In this case, an attacker could read the swap file and possibly find your unencrypted data.
ProtectedMemory makes it very easy to significantly raise the bar for an attacker to spy on your process and find some sensitive data. Its use is very easy, and I recommend employing it in your Whidbey applications where you'll be holding information that you want hidden from prying eyes. It's not perfect, but when used properly, attackers will have a significantly harder time obtaining sensitive information from your application.
As for choosing between ProtectedMemory and ProtectedData:
http://msdn2.microsoft.com/en-us/library/aa302406.aspx http://blogs.msdn.com/shawnfa/archive/2004/05/17/133650.asp
I use WindowsIdentity.Impersonate() to log on as a different user and protect data.
And ProtectedData.Protect() raises CryptographicException "The system can not find the file specified". What can that mean?
Same protection code is used when crypting by current user, and that goes ok.
Hi Kurt,
DPAPI stores information in the user's profile, so in order to use it when you've impersonated another user you'll need to ensure that their entire profile is loaded. You can do this by calling LoadUserProfile after you've called LogonUser.
-Shawn
I get the same error as Kurt when I use Impersonate() on a web page (C#):
this.Context.Request.LogonUserIdentity.Impersonate();
return Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(unencrypted), null, DataProtectionScope.CurrentUser));
If this problem is caused by the same issue as Kurt, how can I load the user profile from the web page
Hi Kevin,
You can use the LoadUserProfile API to force the user's profile to be loaded.
Every time you need to store sensitive data your first thought use to be encryption. You probably gather