Sunday, September 11, 2005

Data Protection API in .NET 2.0

Protecting sensitive Data through Managed DPAPI

While developing a web or windows application, developers often make use of the web.config file to store sensitive data like passwords and connection strings. In most cases, this data is protected from unauthorized access for e.g. IIS forbidding the download of web.config file placed in the virtual directory where the application resides.

The data in the config files should nevertheless be in encrypted format for the safe storage of username/passwords and connection strings. Most developers including me were quite intent to use the Cryptography classes provided in .NET to encrypt and decrypt the strings, but all or most of them require the management of public/ private key pairs which doesn't make life easier anyway.
Using DPAPI (Data protection API) is thus a better option which provides automatic key management. This key management can be either based on the user's credentials or the machine.

User Level Encryption

When using user level encryption, DPAPI generates a session key which is derived from the user's credentials and the optional entropy that is discussed later. This session key is then used to do the actual encryption. With user level encryption, the encryption and decryption should be performed by the same user.

Machine Level Encryption

Machine level encryption (although not much secure) allows the encrypted data to be decrypted by any application running on the same machine. This can be used in a server scenario where there are no unauthorized logins.

Support in Previous Versions

.NET version 1.1 did not provide any wrappers to use the DPAPI hence we had to make use of DLLImport to import Crypt32.dll and use the APIs CryptProtectData and CryptUnprotectData for encryption and decryption respectively.

ProtectedData class in Whidbey

Starting from version 2.0, Whidbey provides a class called ProtectedData having two static members Protect and UnProtect. The signatures of these methods are:

1. byte[] Protect(byte[] dataToEncrypt, byte[] optionalEntropy,
DataProtectionScope scope)

2. byte[] UnProtect(byte[] dataToDecrypt, byte[] optionalEntropy,
DataProtectionScope scope)


  • dataToEncrypt is the data to encrypt
  • optionalEntropy is an additional entropy added to the key generation step. Without knowing this byte array no one can decrypt the data.
  • Setting the scope to DataProtectionScope.CurrentUser allows user level encryption while DataProtectionScope.LocalMachine allows machine level encryption as discussed earlier.

DataProtectionPermission


Whidbey also provides with DataProtectionPermission that can be used to grant/deny permission to the code on the call stack for ProtectedData to work. This can be used to limit permission to only encrypting or decrypting the data or both.


Code sample

static void Main()
{

byte[] entropyBytes = new byte[] {0x00,0x01,0x23, 0x34,0x0A};
byte[] myDataBytes;
string myData = "My sensitive data";
myDataBytes= System.Text.Encoding.Unicode.GetBytes(myData);
byte[] encryptedData = ProtectedData.Protect(
myDataBytes,entropyBytes,DataProtectionScope.CurrentUser);

//print the encrypted bytes
////////////////////////////

//decrypt the data
byte[] decryptedData = ProtectedData.UnProtect(
encryptedData,entropyBytes,DataProtectionScope.CurrentUser);

//print the decrypted bytes which should be the same as myData
////////////////////////////////////////////////

Console.ReadLine();

}