search
Twitter Rss Feeds
MicrosoftArticlesForumsGroups
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml/Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsGroups
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsGroups
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsGroups
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsGroups
Microsoft Excel
Microsoft Word
Microsoft Powerpoint
Publisher
Money

Operating SystemsArticlesForumsGroups
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsGroups
Share Point
BizTalk
Site Server
Exhange Server
IIS
Transaction Server

Graphic DesignArticlesForumsGroups
Macromedia Flash
Adobe PhotoShop
Microsoft Expression

OtherArticlesForumsGroups
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Reviews
Search Engines
Resumes

 
Encrypting Sensitive Data in .NET with DPAPI
By Peter A. Bromberg, Ph.D.
Printer - Friendly Version
Peter Bromberg

Beginning with Windows 2000, the operating system began to provide a data protection application-programming interface called the Data Protection API (DPAPI). DPAPI is a very simple API consisting of only a pair of function calls that provide OS-level data protection services to user and system processes. Since the data protection is part of the OS, every application can secure data without needing any specific cryptographic code other than the necessary function calls to DPAPI. These calls are two simple functions with various options to modify DPAPI behavior. Overall, DPAPI is a very easy-to-use service that will benefit developers that must provide protection for sensitive application data, such as passwords and private keys.

DPAPI is a password-based data protection service: it requires a password to provide protection. The drawback, of course, is that all protection provided by DPAPI rests on the password provided. This is offset by DPAPI using proven cryptographic routines, specifically the strong Triple-DES algorithm, and strong keys. Since DPAPI is focused on providing protection for users and requires a password to provide this protection, it logically uses the user's logon password for protection.

DPAPI Architecture

The public DPAPI interfaces are part of crypt32.dll and are available for any user process that has loaded it. This DLL is part of CryptoAPI; application developers can assume that all Windows systems have this DLL available.

Applications either pass plaintext data to DPAPI and receive an opaque protected data blob back, or pass the protected data blob to DPAPI and receive the plaintext data back.

The protected data blob is an opaque structure, because in addition to the encrypted data, it also contains data to allow DPAPI to unprotect it. Since it is opaque, developers do not need to parse or understand the format at all. An important point to remember is that DPAPI only applies cryptographic protection to the data. It does not store any of the protected data; applications calling DPAPI must implement their own storage. In this example I'll show how the Isolated Storage classes can be combined with the calls to DPAPI to provide an easy - to - use, robust encrypted local data store.

When an application calls one of the DPAPI functions, the functions make a local RPC call to the Local Security Authority (LSA). The LSA is a system process that starts on boot and runs until the computer is shut down. These local RPC calls never traverse the network, so all data remains on the local machine. The endpoints of these RPC calls then call DPAPI private functions to protect or unprotect the data. These functions then call back into CryptoAPI, via crypt32.dll, for the actual encryption or decryption of the data in the security context of the LSA. The functions run in the security context of the LSA so that security audits can be generated.

The LSA also contains system-level functions for using DPAPI. These functions are available to any thread running inside the LSA and only to those threads. This is because the functions provide additional options for LSA threads, specifically to allow LSA threads to protect user data that cannot be unprotected by non-LSA threads using DPAPI such as user applications.

Keys and Passwords in DPAPI

DPAPI is focused on providing data protection for users. Since it requires a password to provide protection, the logical step is for DPAPI to use a user's logon password. Actually, DPAPI uses the user's logon credential. In a typical system, in which the user logs on with a password, the logon credential is simply a hash of the user's password. In a system in which the user logs on with a smart card, however, the credential would be different.

A small drawback to using the logon password is that all applications running under the same user can access any protected data that they know about.   Since applications must store their own protected data, gaining access to the data could be somewhat difficult for other applications, but certainly not impossible. To counteract this, DPAPI allows an application to use an additional secret when protecting data. This additional secret is then required to unprotect the data.

Technically, this "secret" should be called secondary entropy. It is secondary, because, while it doesn't strengthen the key used to encrypt the data, it does increase the difficulty of one application, running under the same user, to compromise another application's encryption key. Applications should be careful about how they use and store this entropy. If it is simply saved to a file, unprotected, then adversaries could access the entropy and use it to unprotect an application's data.

Additionally the application can pass in a data structure that will be used by DPAPI to prompt the user. This "prompt structure" allows the user to specify an additional password for this particular data. I won't go into the details of using the prompt structure here, as we want to develop a simple class library that can be used with ASP.NET to encrypt passwords, database connection strings and similar information that can then be safely stored in web.config. We don't want any "prompts" popping up on our web server!

DPAPI initially generates a strong key called a MasterKey, which is protected by the user's password. DPAPI uses a standard cryptographic process called Password-Based Key Derivation, described in PKCS #5, to generate a key from the password. This password-derived key is then used with Triple-DES to encrypt the MasterKey, which is finally stored in the user's profile directory.

Using DPAPI

There are two sets of interfaces to DPAPI, the user interfaces and the system interfaces. There are also two data structures, the CRYPTPROTECT_PROMPTSTRUCT, which is the "prompt structure" mentioned earlier, and the protected data blob that holds the protected data.

The user interfaces contain the only two functions application developers need to know to use DPAPI. The protect function: CryptProtectData() and the unprotect function: CryptUnprotectData(). The only requirement for applications to use these functions is to either link with crypt32.lib or dynamically load crypt32.dll. The protect function takes a plaintext data as input and returns an opaque protected data blob. The unprotect function takes this opaque data blob and returns the plaintext data.

In order to use the DPAPI in .NET code, we need to wrap the correct P/Invoke calls as follows:

// CryptProtectData

[DllImport("crypt32", CharSet=System.Runtime.InteropServices.CharSet.Unicode, SetLastError=true, ExactSpelling=true)]
public static extern bool CryptProtectData
(
ref DATA_BLOB dataIn
, string szDataDescr
, ref DATA_BLOB optionalEntropy
, IntPtr pvReserved
, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct
, int dwFlags
, ref DATA_BLOB pDataOut
);


// CryptUnprotectData

[DllImport("crypt32", CharSet=System.Runtime.InteropServices.CharSet.Unicode, SetLastError=true, ExactSpelling=true)]
public static extern bool CryptUnprotectData
(

ref DATA_BLOB dataIn
, StringBuilder ppszDataDescr
, ref DATA_BLOB optionalEntropy
, IntPtr pvReserved
, ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct
, int dwFlags
, ref DATA_BLOB pDataOut
);

 

// DataBlob struct

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct DATA_BLOB
{
public int cbData; // count of bytes
public IntPtr pbData; // pointer to block of data bytes
}

// CRYPTPROTECT_PROMPTSTRUCT struct

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPTPROTECT_PROMPTSTRUCT
{
public int cbSize;
public int dwPromptFlags;
public IntPtr hwndApp;
public string szPrompt;
}

There are some constants and helper API calls that you'll find in the downloadable solution file at the bottom of this article. There is also a small collection of articles in MSDN online that go into more detail on DPAPI. What I've done here is to simplify the use of DPAPI by accepting the default store and setting up a utility class library that takes care of all the Base64 encoding and decoding internally. In this manner, you can, for example, use the library to handle encryption of sensitive database connection strings or even user passwords for placement in the web.config file for ASP.NET applications, and easily decode these with the optional secret entropy "password" parameter. In addition, since the DPAPI does not provide for storage of data, I've implemented a mechanism to optionally use an Isolated storage file to securely store the encrypted data and retrieve it on demand:

private void Encrypt_Click(object sender, System.EventArgs e)
{
string strResult=String.Empty;
try
{
strResult=PAB.Security.DPAPI.ProtectData(SecretText.Text,EntropyText.Text);
CipherText.Text=strResult;
if (chkIsoStore.Checked)
{
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
writeToIsoFile(isoStore,strResult);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

}


private void writeToIsoFile(IsolatedStorageFile isoStore,string strContent)
{

string[] fileNames = isoStore.GetFileNames("DPAPI.txt");
foreach (string file in fileNames)
{
if(file == "DPAPI.txt")
{
isoStore.DeleteFile("DPAPI.txt");
}

}

StreamWriter writer = null;
writer = new StreamWriter(new IsolatedStorageFileStream("DPAPI.txt", FileMode.CreateNew,isoStore));
writer.Write( strContent);
writer.Close();
}

private string readFromIsoFile(IsolatedStorageFile isoStore)
{
StreamReader reader = new StreamReader(new IsolatedStorageFileStream("DPAPI.txt", FileMode.Open,isoStore));
String sb = reader.ReadToEnd();
reader.Close();
return sb.ToString();
}


private void Decrypt_Click(object sender, System.EventArgs e)
{
string strResult=String.Empty;
try
{
if(chkIsoStore.Checked)
{
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
strResult=readFromIsoFile(isoStore);
strResult=PAB.Security.DPAPI.UnProtectData(strResult,EntropyText.Text);

}
else
{
strResult=PAB.Security.DPAPI.UnProtectData(CipherText.Text,EntropyText.Text);

}

DecryptResults.Text = strResult;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

}
}
}

The "front end" Winforms - based test application illistrates how data can be encrypted, decryopted and optionally stored in IsolatedStorage:

 

If you have run this one time and encrypted some data with the "Save / Retrieve" checkbox checked, then the next time you run it you can retrieve and unprotect your data with the checkbox checked again, loading it from the Isolated storage file by pressing the "Decrypt" button.

Summary

While it is relatively easy to duplicate the functionality of DPAPI in .NET with the use of the built - in .NET cryptographic classes, DPAPI is a simple and reliable mechanism that is built into the OS for secure encryption of sensitive data. By combining this with the use of the .NET IsolatedStorage class, developers have a simple and effective mechanism for data protection in the enterprise.

 

 

 

Download the code that accompanies this article


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform. Pete's samples at GotDotNet.com have been downloaded over 41,000 times. You can read Peter's UnBlog Here.  --><--NOTE: Post QUESTIONS on FORUMS!

Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.


Pete's Blog   |    Pete's Resume   |    Robbe's Blog   |    Robbe's Resume   |    Archive #2   |    Archive #3   |    Dotnetslackers   |    XmlPitStop   |    Advertise   |   Contact Us   |   Privacy   |   Copyright (c) 2000 - 2009 eggheadcafe.com  All rights reserved.