Click here to Skip to main content
Click here to Skip to main content

Data encryption with DPAPI

By , 21 May 2002
 

Windows Data Protection

DPAPI - Data Protection Application Programming Interface; most probably the smallest API available under Win32. It contains just two functions.

Beginning with Windows 2000, Microsoft introduced the DPAPI. It wasn't well known or documented until Windows XP came out.

The DPAPI is a pretty well thought-out mechanism to allow any application to do simple and yet powerful encryption for its data. It has good recovery methods - in case the password gets lost, fully supports enterprise or home use and is based on the Cryptographic Services available under Win32.

So, what does it actually do?? Simple - it encrypts or decrypts a block of data.

And it does it without asking much for settings, cryptographic keys, algorithms and other hocus-pocus. Sounds like a ideal function for securing sensitive data? Definitely. There are some options that you can set but it goes even without them.

Here are some highlights:

  • user bound encryption (using users credentials)
  • machine bound encryption (using the machines credentials)
  • application supplied pass phrase
  • optional user supplied password
  • transparent mode (no user-interface at all)
  • optional security auditing
  • operating on standards - PKCS#5v2, RFC-2404, RSA etc.

MSDN contains a very well written article explaining the guts of DPAPI and is for sure worth reading if you are into security.

While DPAPI isn't generally difficult to use it has some pitfalls which you can avoid with the CProtectedData class

The class makes all necessary conversions if needed (DPAPI takes only UNICODE strings) and handles filling the data structures, allocating/freeing memory and so on. But at the end its just a wrapper class.

It provides one function in many flavors to perform the encryption and another one for decryption. Sanity checks on parameters are performed.

User interface

As you can see on the pictures, the CryptoAPI provides some dialog boxes with information and options to the user. It allows the user to modify encryption strength, choose a custom password and view information about the data to be encrypted.

Its important to remember when the user is allowed to choose a password, he most probably will forget it. In this case there is no way to retrieve this lost password and the data is not recoverable.

The main dialog when the UI is enabled.

The "Set security level..." dialog.

Changing to High Security mode enables the password. If you don't supply a description, the user can enter a description on his own.

The "Details..." dialog.

How to use?

Create an instance of CProtectedData, call its SetData() member function, if needed set some options and finally call one of the ProtectData() or UnprotectData() functions. Both return a pointer to a DATA_BLOB structure. The DATA_BLOB structure contains the size of the resulting data and a pointer to the data. If the cbData member is 0 the encryption/decryption failed and you can call ::GetLastError() to retrieve the reason.

Be aware that this functions may fail when you enable the user interface because the user can cancel the operation while normally the functions succeed.

CProtectedData pd;
pd.SetData(pPlainData, dwPlainDataSize);

const DATA_BLOB* pData = pd.ProtectData();
if(pData->cbData)
    // well, use the data in pData->pbData

For convenience there are two derived classes CUserProtectedData and CMachineProtectedData. While the first is equal to the CProtectedData class, the second class just sets the appropriate option to encrypt with machine credentials. They are just provided for code cleanness.

The DPAPI uses optional entropy data which is supplied by the application. If you supply entropy data, it will - simply expressed - be taken as a part of the password and included in key generation. In other words, it increases security. Data encrypted without entropy data can be decrypted by any other application too.

One last thing is up to you in order to avoid memory leaks: when you retrieve the description string from the encrypted data by supplying UnprotectData() with a pointer to a LPTSTR variable it will place the description into this variable and you must call ::LocalFree() when you don't need it anymore.

Sample Application

The sample application shows how to use any and all of the available options and performs multiple full cycles of encryption/decryption with and without user intervention. Anyway, it does not save the encrypted data to disk.

Compatibility

All classes are fully MBCS/Unicode enabled. The sample contains all configurations. Written, compiled and tested under VC7 but should also compile with VC6 and below. The header file automatically add linking with crypt32.lib. Remember that you need Windows 2000 or Windows XP to use DPAPI.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Andreas Saurwein Franci Gonçalves
CEO Uniwares Ltda.
Brazil Brazil
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCompiling in MBCS, not UNICODE, gives error [modified]memberehaerim11 Jul '11 - 19:32 
CProtectedData::~CProtectedData()
{
#ifndef _UNICODE
	if(m_Prompt.szPrompt)	// only non-unicode builds allocate memory for the conversion
		delete[] m_Prompt.szPrompt;
#endif
 
	// call FreeProtectedData() before dtor
	ASSERT(m_ProtectedData.cbData==0 && m_ProtectedData.pbData == NULL);
}
 
error C2665: 'delete' : none of the 4 overloads can convert parameter 1 from type 'const unsigned short *'
 
This seems because szPrompt is defined as LPCWSTR in _CRYPTPROTECT_PROMPTSTRUCT.
 
typedef struct  _CRYPTPROTECT_PROMPTSTRUCT
{
    DWORD cbSize;
    DWORD dwPromptFlags;
    HWND  hwndApp;
    LPCWSTR szPrompt;
} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT;
 
Does anyone know how to fix this?
 
thx
 
Solved myself as follows:
 
#if _MSC_VER >= 1400
		delete[] m_Prompt.szPrompt;
#elif _MSC_VER == 1200
		delete[] const_cast<LPWSTR>(m_Prompt.szPrompt);
#endif // _MSC_VER

modified on Tuesday, July 12, 2011 2:15 AM

GeneralExtended DPAPImemberdaves_0513 Mar '11 - 4:22 
If you are interested in using the DPAPI you may also be interested in this project, XDP (http://code.google.com/p/xdp/[^]).
 
It is an extension of the DPAPI that offers the same simple 2 method API but allows you encrypt data so that another local or domain, user or group member can decrypt. The project gives full details of how it works and is open source so you can see for yourself.
 
Hopefully you'll find it useful.
GeneralFailed encryption And Corrupted decryption [modified]memberehaerim27 Feb '07 - 9:36 
I tried to run the demo using VC6++. The sln and vcproj files were converted using prjconverter.exe. Since it converts sln/vcproj files automatically, I am not sure it converts all settings correctly, but those converted dsw/dsp files worked fine at least.
 
[Problem 1] Non-Unicode compile gives compile error on both Win2000 and WinXP.
=> Please fix this.
=> Fixed myself. See below.
 
[Problme 2] Test1 on Win2000 Server SP4 failed. GetLastError returned 0x57 meaning "The parameter is incorrect".=> Please fix this.
 
[Problem 3] All Test1-4 returns corrupted decryption data on Windows XP SP2.
=> Please fix this.
=> Fixed myself. See below.
 
haerim
 

-- modified at 17:07 Tuesday 27th February, 2007
 
I solved myself for Problem 1 and Problem 3 as follows:
[Problem 1]
// delete[] (m_Prompt.szPrompt); //-[LHR20070228] -- Non-Unicode compile error
delete[] (LPBYTE)(m_Prompt.szPrompt);
[Problme 3]
// pd.SetData((LPBYTE)gTestData, (DWORD)_tcslen(gTestData)+1); //-[LHR20070228] -- commented out
//+[LHR20070228] -- bug fix
size_t sz = _tcslen(gTestData)+1;
#if defined(_UNICODE)
sz *= 2;
#endif
pd.SetData((LPBYTE)gTestData, (DWORD)sz);
//-[LHR20070228] -- bug fix

 

-- modified at 17:11 Tuesday 27th February, 2007
GeneralVS 2005 (VS8) Decrypted Data always returned brokenmemberAmit22034 Feb '07 - 23:39 
When i run sample tests from VS2003 it works fine.
 
When i run sample CProtectedData Class tests from VS2005 i got problem:
 
Decrypted Data for all sample tests of the CProtectedData Class is corrupted:
Decrypted Data: THIS IS A SAMPLE OF ??????0
 
The input was: LPCTSTR gTestData = _T("THIS IS A SAMPLE OF USING CPROTECTEDDATA");
 
Any ideas?
 

 
Thanks,Amit
GeneralRe: VS 2005 (VS8) Decrypted Data always returned brokenmemberehaerim27 Feb '07 - 9:41 
I got also corrupted decryption using VC++6 on both Win2000 and WinXP.
 
Have you figured out any reason of corrupted decryption? If so, please share your work.
 
Thanks.
 
haerim
 

GeneralRe: VS 2005 (VS8) Decrypted Data always returned brokenmemberAmit220328 Feb '07 - 10:01 
No.
I used MSDN sample and it works me fine.
 
I got problem in VS 2005 with this wrapper.
I believe i will look at this wrapper again soon.
 
Regards,
 

 
Amit
QuestionWhat about file size?memberesquijarosa29 Dec '04 - 15:44 
Hi:
I'm using the DPAPI Wrapper version proposed in the MSDN Article "How to: Create a DPAPI Library" but I'm having trouble "protecting" data from more than 300kb. There are a bug in the DPAPI implementation in WIN2000 or there are other parameters that is needed to set other than the proposed in the article to make it work properly?
 
Thanks in advance.
 
Alex.
QuestionHow to decrypt on a different machine?memberXavier John12 Nov '03 - 12:50 
The data encrypted by this code can only be decoded on this machine. How to decode on different machines if the logged in domain account is the same?
AnswerRe: How to decrypt on a different machine?memberSaurweinAndreas12 Nov '03 - 13:14 
As long as you use user credentials to encrypt, you can decrypt it on any machine on which the user is accredited.
If you use a local user or machine credentials, there is no way to decrypt on another machine.
 

Finally moved to Brazil
GeneralRe: How to decrypt on a different machine?memberXavier John12 Nov '03 - 15:00 
How do I get the user credentials? AcquireCredentialsHandle?
GeneralRe: How to decrypt on a different machine?memberSaurweinAndreas13 Nov '03 - 0:09 
Xavier John wrote:
How do I get the user credentials?
 
You probably should start with reading the article. Besides of that, please have a look at the mentioned documentation.
 

Finally moved to Brazil
GeneralRe: How to decrypt on a different machine?memberXavier John13 Nov '03 - 5:58 
My guess is you are thinking about CUserProtectedData. This class encrypts based on the user on the same machine. The article forgot to mention that. I have already tried out this code and it will not work on another machine. Based on MSDN it will work on another machine if you have roaming profiles enabled.
So I guess you are not familiar with this subject to help me.
I wonder what is the commonality between a domain user account between machines that I can use as a key.
GeneralRe: How to decrypt on a different machine?memberSaurweinAndreas13 Nov '03 - 6:08 
Xavier John wrote:
I have already tried out this code and it will not work on another machine. Based on MSDN it will work on another machine if you have roaming profiles enabled.
 
You got already the answer. And the secret is simply (as described in the MSDN article under "Keys and Passwords in DPAPI"):
They [the masterkeys] are kept forever in the user's profile directory, protected by the user's password. This also applies to the first, and active masterkey.
Althoug, dont ask me now where XP stores the local users masterkeys.
 

Finally moved to Brazil
GeneralBug with wrong entropy codememberGeorge Montemayor30 Sep '03 - 8:44 
There is a bug when you call UnprotectData with a different entropy string. I ended up with a DATA_BLOB whose cbData member is non-zero but pbData member is NULL.
 
As a result when you call FreeUnprotectData(), the cbdata member will remain non-zero and will fail the assertion at the destructor.
QuestionHow did you guys run this sample?memberBeer10 Dec '02 - 13:36 
where are data DATA_BLOB, and CRYPTPROTECT_PROMPT_ON_UNPROTECT supposed to be defined in the source?
 
These macros aren't defined anywhere that I could find.
 
hey
AnswerRe: How did you guys run this sample?memberAndreas Saurwein11 Dec '02 - 1:44 
These are defined in the wincrypt.h header file. Although, you might need to update your VC6 headers to something which is W2k compatible (any newer SDK).

 

I don't think this is a serious possesion, and the evil most likely comes from your hand. Colin J Davies, The Lounge
GeneralRe: How did you guys run this sample?memberBeer11 Dec '02 - 6:28 
I had that header included. I'm on winxp.
 
At any rate, I used another encryption to solve my problem.
 
hey
GeneralRe: How did you guys run this sample?memberAndreas Saurwein11 Dec '02 - 10:47 
"including the header" doesnt help if the header file is outdated. And wincrypt.h has undergone some changes since VC was released.
 

I don't think this is a serious possesion, and the evil most likely comes from your hand. Colin J Davies, The Lounge


GeneralRe: How did you guys run this sample?memberBeer11 Dec '02 - 11:56 
Don't you think the article should have or would have included this mandatory information had that been the case?
 
You would think so anyway?
 
At any rate, I went with something else, so this no longer has bearing for me.
Thanks anyway.
 
hey
GeneralRe: How did you guys run this sample?sussAnonymous8 Jul '03 - 5:42 
Did anyone try to use the DPAPI library example (C#) posted on MSDN:http://msdn.microsoft.com/security/default.aspx?pull=/library/en-us/dnnetsec/html/SecNetHT07.asp.
 
The decrytion fails with the 'The data is invalid' error message.

QuestionThanks and ideas on software protection?memberdswigger28 May '02 - 8:26 
Thanks for this great article - I had never heard of this before - looks like fun.
 
Thinking out loud - have you (or anyone reading this) tried using these methods for the basis of protecting a shareware app?
 
I am doing R&D on this subject and there is little info floating around on the best ways to do it (other than buying some 3rd parties shareware lock code and paying royalities).
 
If you (or anyone else) has any links,info, or ideas please share.
AnswerRe: Thanks and ideas on software protection?memberAndreas Saurwein28 May '02 - 8:54 
Glad that you liked the article.
 
First, DPAPI isnt useful for this purpose except that you could encrypt registration data with it. But where would you store it?
 
Second, shareware doesnt need protection. If its a good, useful tool then people will buy it anyway. And the few unhonest ones do not hurt.
 
cheers
Andreas
 
Vote against software patents in europe
GeneralRe: Thanks and ideas on software protection?memberdswigger28 May '02 - 9:45 
I tend to agree with you that a few dishonest ones do not hurt. The form of protection I am thinking about is basically to disuade casual copying.
 
I think that releasing a full featured demo product is essential for letting a user know what they are getting. I also think that putting in such strong protection that it could prevent a legal users software from functioning is bad. I am thinking time-trial - with a method to unlock (IE: one version thats a demo and can be unlocked to full potential). I have read quite a few little articles (even by some pirates) that talk about decent ways of doing it.
 
I am guessing you could use the DPAPI like you said - to crypt registration or time limitation info and put it somewhere. The main thing that I like about the DPAPI is that its already there.
 
Ultimately ANY protection measure comes down to an 'if statement'...Nothing is infallable and I am looking for a modest and maintainable approach without bothering the user much other than limiting the demo to 30 days.
 
Most protection API's (from what I have read) want a license for each sku - which is not something I want to get into - since I 'plan' on putting out more than one product.
 

GeneralRe: Thanks and ideas on software protection?memberAndreas Saurwein28 May '02 - 10:04 
I had right now a pretty silly idea but it might prove useful: why not use the DPAPI to encrypt a needed module (DLL) which will be decoded and loaded dynamically? This would overcome the 'if statement..' problem too Smile | :)
 
Have to think about it...

 
Vote against software patents in europe
GeneralRe: Thanks and ideas on software protection?memberdswigger28 May '02 - 10:20 
Wow thats a really cool idea....I will give it some thought as well. Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 22 May 2002
Article Copyright 2002 by Andreas Saurwein Franci Gonçalves
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid