Tested with Microsoft Visual Studio 2005 8.0.50727.42; .NET Framework 2.0.50727
Microsoft has added a new way for us to implement simple string encryption and decryption:
the ProtectedData class.
The ProtectedData class is a System.Security.Cryptography namespace class. This class makes use of the crytography
functionality of the Windows 2000 (and later) Data Protection API (DPAPI).
A Great Way to Quickly Encrypt and Decrypt Database Connection Strings
By using the ProtectedData class, we now have a quick and easy way of encrypting things such as
password-containing database strings.
/**************** Encrypt/Protect Database String ****************/
// The DB string that we want to encrypt/decrypt:
String strConnectionString = "Data Source=localhost; User ID=sa; Password=somepassword;";
// Convert the string to a byte array:
byte[] arrDBString = System.Text.Encoding.Unicode.GetBytes(strConnectionString);
// Provide additional protection via entropy with another byte array:
byte[] arrEntropy = { 4, 5, 7, 9, 4, 5, 7, 9 }; //Save this for unprotecting later
// Encrypt/protect the DB string:
byte[] arrEncryptedDBString = ProtectedData.Protect(arrDBString, arrEntropy,
DataProtectionScope.CurrentUser);
// Display the encrypted value (my not be visible due to encoding):
String strEncryptedDBString = System.Text.Encoding.Unicode.GetString(arrEncryptedDBString);
MessageBox.Show(strEncryptedDBString);
/**************** Decrypt/Unprotect Database String ****************/
// Decrypt/unprotect the DB string:
byte[] arrUnencryptedDBString = ProtectedData.Unprotect(arrEncryptedDBString,
arrEntropy, DataProtectionScope.CurrentUser);
// Display the unencrypted/original DB string:
String strUnencryptedDBString = System.Text.Encoding.Unicode.GetString(arrUnencryptedDBString);
MessageBox.Show(strUnencryptedDBString);
The preceding example encrypts and decrypts a database string in one block of code. In the real world, you'll
probably want to store an encrypted database string at one time and then read it back for unencryption at a later
time. The following code illustrates how this can be accomplished.
// This is a button click event - to encrypt the DB string to disk:
private void btnEncrypt_Click(object sender, EventArgs e)
{
// The DB string that we want to encrypt/decrypt:
String strConnectionString = "Data Source=localhost; User ID=sa; Password=somepassword;";
// Call the custom method for encrypting the string:
this.EncryptDBString(strConnectionString);
}
// This is a button click event - to unencrypt the saved DB string:
private void btnDecrypt_Click(object sender, EventArgs e)
{
String strConnectionString = this.DecryptDBString();
// Do something with the unencrypted DB string ...
}
// This method will encrypt the provided DB string to disk
private void EncryptDBString(String DBString)
{
/**************** Encrypt/Protect Database String ****************/
// Convert the string to a byte array:
byte[] arrDBString = System.Text.Encoding.Unicode.GetBytes(DBString);
// Provide additional protection via entropy with another byte array:
byte[] arrEntropy = { 4, 5, 7, 9, 4, 5, 7, 9 }; // Save this for unprotecting later
// Encrypt/protect the DB string:
byte[] arrEncryptedDBString = ProtectedData.Protect(arrDBString, arrEntropy,
DataProtectionScope.CurrentUser);
// Write the encrypted DB string to disk:
using (FileStream fs = new FileStream("ENCDBSTR.bin", FileMode.OpenOrCreate))
{
fs.Write(arrEncryptedDBString, 0, arrEncryptedDBString.Length);
}
}
// This method will decrypt the saved encrypted DB string
private String DecryptDBString()
{
/**************** Decrypt/Unprotect Database String ****************/
// Setup the unencrypted DB string to return:
String strUnencryptedDBString = null;
// Provide additional protection via entropy with another byte array:
byte[] arrEntropy = { 4, 5, 7, 9, 4, 5, 7, 9 }; // Save this for protecting later
// Setup the byte array that will hold the encrypted DB string:
byte[] arrEncryptedDBString = new byte[0];
// Read the encrypted DB string from disk:
if (File.Exists("ENCDBSTR.bin"))
{
using (FileStream fs = new FileStream("ENCDBSTR.bin", FileMode.Open))
{
// Reset the byte array's length based on read length:
arrEncryptedDBString = new byte[fs.Length];
// Read the encrypted file into the byte array:
fs.Read(arrEncryptedDBString, 0, (int) fs.Length);
}
}
if (arrEncryptedDBString.Length > 0)
{
// Decrypt/unprotect the DB string:
byte[] arrUnencryptedDBString = ProtectedData.Unprotect(arrEncryptedDBString,
arrEntropy, DataProtectionScope.CurrentUser);
// Convert the byte array to a string:
strUnencryptedDBString = System.Text.Encoding.Unicode.GetString(arrUnencryptedDBString);
}
// Return the unencrypted DB string:
return strUnencryptedDBString;
}
Let me go through the steps involved so that you understand them more thoroughly.
Steps Involved in Encrypting a Database String (based on the preceding code)
Convert the database string to a byte array.
byte[] arrDBString = System.Text.Encoding.Unicode.GetBytes(DBString);
Create an entropy byte array for added protection. The exact same byte array must be used to unencrypt the database string later.
byte[] arrEntropy = { 4, 5, 7, 9, 4, 5, 7, 9 };
Use the ProtectedData class' Protect method to encrypt the database string. The Protect method will return a byte array consisting of the encrypted database string.
byte[] arrEncryptedDBString = ProtectedData.Protect(arrDBString, arrEntropy,
DataProtectionScope.CurrentUser);
Write the encrypted database string to a file for retrieval later using the Protect method of the ProtectedData class. You can always choose a different file name and path.
using (FileStream fs = new FileStream("ENCDBSTR.bin", FileMode.OpenOrCreate))
{
fs.Write(arrEncryptedDBString, 0, arrEncryptedDBString.Length);
}The encrypted file will look something like this:
Steps Involved in Decrypting an Encrypted Database String (based on the preceding code)
Create the entropy byte array that was used to encrypt the database string previously.
byte[] arrEntropy = { 4, 5, 7, 9, 4, 5, 7, 9 };
Open the file containing the encrypted database string and store it into a byte array.
using (FileStream fs = new FileStream("ENCDBSTR.bin", FileMode.Open))
{
// Reset the byte array's length based on read length:
arrEncryptedDBString = new byte[fs.Length];
// Read the encrypted file into the byte array:
fs.Read(arrEncryptedDBString, 0, (int) fs.Length);
}
Decrypt the encrypted database string byte array using the Unprotect method of the ProtectedData class.
The Unprotect method will return a byte array consisting of the unencrypted database string.
byte[] arrUnencryptedDBString = ProtectedData.Unprotect(arrEncryptedDBString,
arrEntropy, DataProtectionScope.CurrentUser);
Convert the decrypted database string byte array back into a regular string.
strUnencryptedDBString = System.Text.Encoding.Unicode.GetString(arrUnencryptedDBString);
Understanding DataProtectionScope
You may have noticed that I used a DataProtectionScope enumerated value of DataProtectionScope.CurrentUser for the
preceding code samples. This is the most secure way of implementing such functionality.
Setting the method parameter's DataProtectionScope enumeration to DataProtectionScope.CurrentUser ensures that
only the user that encrypted the database string can decrypt it later. On the other hand, using
DataProtectionScope.LocalMachine ensures that ANY user logged on to the local machine can decrypt the database string.
For example, let's say that user mike@dotnetfun.com encrypted a database string on the local machine with the
DataProtectionScope.CurrentUser enumeration. User scott@dotnetfun.com will NOT be able to decrypt that database string.
Similarly, let's say that user mike@dotnetfun.com encrypted a database string on the local machine with the
DataProtectionScope.LocalMachine enumeration. User scott@dotnetfun.com WILL be able to decrypt that database string.
Limitations: Encrypted Strings are Not Portable
If you save an encrypted database string (using the ProtectedData class) to a file, don't expect to be able to
copy it to another computer and decrypt it there and have it work, because it won't.
The DPAPI encrypts and decrypts in the context of a single machine only.
It's for this reason that you probably wouldn't want to encrypt a database string into a file and deploy it with
Windows Forms applications. That's because each Windows application running on a client would need a copy of the
encrypted file. Just remember that once encrypted on a machine, it must remain on the machine in order for it
to be decryptable later.