About the author

J Sawyer is a developer based in Houston, TX who absolutely loves to write code. After spending 9 years at Microsoft, he moved on to other things and is currently the Lead Developer for the RealTime Data Management team at Logica US. He spends his days building Really Cool Things around StreamInsight and having a blast doing it.

He has been involved with HDNUG, one of the oldest and largest .NET-focused user groups in the US, since its inception in 2001 and has watched it grow from 5-10 technologists meeting around a conference table to a thriving community of over 5000 with regular meeting attendance averaging 100 attendees. He currently serves as the Vice President. You can join him at HDNUG on the second Thursday of every month at the Houston Microsoft office.

He also loves to ride his Yamaha FZ1. And sometimes his Ninja 650. And also his Honday XR-400 dirt bike. But he doesn't code and ride at the same time. That would be bad.

Protecting Crypto Keys

January 2, 2008 1:32 AM

In my last post, I discussed how to work with symmetric encryption. One thing that I mentioned, but didn't go in to, was how to protect the keys for symmetric encryption. Here's the deal: you're using 256-bit Rijndael; you're doing everything right. But what do you do with the key? This is, after all, the key to your encrypted data (pun intended). If a bad guy gets the key, they'll be at your data in no time flat. This, but the way, is the excuse (and it is a poor excuse) that I've most commonly heard to defend a foray into craptology. But let's face it, it is a problem. What, oh what is a security conscious developer to do? Encrypt it with another symmetric algorithm? But then you have the same problem. How do we get ourselves out of this seemingly bottomless pit?
Fear not, dear developer. No reason to worry yourself about all of this mess. Since Windows 2000, the Data Protection API (DPAPI) has shipped with Windows, providing a clean solution to this problem. DPAPI is based on TripleDES (see the previous entry) ... but here's the deal. The TripleDES key is based on the Windows profile, is automatically rotated and the key itself exists in memory for only a brief period of time. But honestly, there's no need to worry about the details. It works, it works well and it's also been reviewed by external security experts and is generally considered to be an excellent implementation to solve this difficult problem.
Now, before we get to the code on how to use DPAPI, let's talk a little more about the details. First, DPAPI can be associated with a single user account or with the machine account. The user account mode is, in general, more secure; that's because when the machine account is used, anyone with access to the machine can get the data decrypted. But that doesn't mean you should jump right into using the user account mode. When you use the user account mode, you will need to load the user's profile (and desktop) in order to encrypt and decrypt. Now, you can technically do that in a web application (by way of some Win32 API calls via PInvoke), but that is a Very Bad Idea™. So ... user account mode is not good for web applications. It is, however, very good for desktop applications - especially in scenarios where there may be multiple users for the system. It's also good to use with Windows services. In both of these situations, the user profile and desktop is loaded and ready for you. One little thorn that you might run in to is this: you need to access the same encrypted data from multiple machines, but using the same user account. If you read the documentation, you'll see what appears to be a silver bullet to solve this problem ... roaming profiles. However, there be Dragons there. Big, nasty, fire-breathing dragons. Does it work? Yes ... in a perfect world. The problem is this: if the profile is unavailable, for whatever reason, Windows will quite happily create a temporary local profile. Which puts everything out of whack. Completely. (Don't ask how I know this ... I still have the scars.) For both modes, you can add an extra layer of security by adding entropy to the mix. It's just an extra bit of (again) gobbledygook added to the algorithm to ensure greater randomness. You'll see this in the code sample.
So, how to use it? In .Net 2.0 and higher, it's actually very easy. In .Net 1.x, you had to directly call the CryptoAPI via PInvoke. There was an implementation on MSDN that you could download and use, which was quite a relief. If you looked at the code, you'll be glad that you never had to write it yourself and your appreciation for crypto in .Net will increase 10 fold. The .Net 2.0 implementation is in (of course) the System.Security.Cryptography namespace, but is not in mscorlib.  It's in System.Security.dll, so if you don't see it, make sure you add it as a reference and all will be well. You have 2 classes in there related to DPAPI: ProtectedData and ProtectedMemory. Their names tell you the difference between them.
Here's a code sample of using DPAPI:
private string ProtectData(string clearText, string password)
{
	//convert our clear text into a byte array.
	byte[] clearTextBytes = System.Text.Encoding.UTF8.GetBytes(clearText);
	//We're going to add some entropy to this.
	//In this case, we're deriving random bytes from the password.
	//This is a good way to use passwords in a more secure manner.
	System.Security.Cryptography.PasswordDeriveBytes pwd =
		new System.Security.Cryptography.PasswordDeriveBytes(password, null);
	byte[] entropy = pwd.GetBytes(16);
	//Do the encryption
	//Notice that it is a static method.
	byte[] cipherText =
		System.Security.Cryptography.ProtectedData.Protect(
		clearText, entropy, System.Security.Cryptography.DataProtectionScope.CurrentUser);
	//write to the label.
	return Convert.ToBase64String(cipherText);
}
So ... not to hard, is it?  Now, before you go off encrypting your keys for your web.config files, I must mention one more little thing: ASP.Net 2.0 will actually encrypt sections of the web.config file for you as well as handle the encryption invisibly - you just continue to use the configuration API's like you always have. One way to do this is to use the aspnet_regiis command-line tool. You can read the docs on that on MSDN. More interesting to me, however, is the ability to do this in code. And, while the aspnet_regiis utility only works on web applications, doing this in code will work with every application. And so, without further ado, here's the code:
static public void EncryptConnectionStrings()
{
	// Get the current configuration file.
	System.Configuration.Configuration config =
		ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
	// Get the section.
	UrlsSection section =
		(UrlsSection)config.GetSection("connectionStrings");
	// Protect (encrypt) the section.
	section.SectionInformation.ProtectSection("DpapiProtectedConfigurationProvider");
	// Save the encrypted section.
	section.SectionInformation.ForceSave = true;
	//And then save the config file.
	config.Save(ConfigurationSaveMode.Full);
}

Tags: , ,

.NET Stuff | Security