Introduction
This article shows how to store encrypted values into a registry entry and later retrieve them. The sample uses two classes CEncrypt
(provides a wrapper for the encryption APIs) and CRegister
(provides a wrapper for the registry operations). The registry key value encryption is useful when you want to store some sensitive data like database server username and password into the registry.The demo shows how to use these classes to write encrypted values into the registry ,as well as later retrieve these values and decrypt them to their original form.
Encryption
The encryption operations are declared in <wincrypt.h>
If you get problems including wincrypt.h,try the following:
#define _WIN32_WINNT 0x0400
#include "wincrypt.h"
If you don�t define _WIN32_WINNT
then the content of wincrypt.h won�t get included
Actually, it can be any value greater than 0x0400. A peep into wincrypt.h will probably help.
CryptGenKey
generates keys randomly so its of no use in our case,since the encryption and decryption should be performed by the same key.We need to generate a key which is derived from a base data e.g. a string of our choice.We should use CryptDeriveKey
for this purpose.But there's some initialisation required before calling this particular function which can be dome in the done in the constructor for CEncrypt
class.
The constructor code calls the following functions in the given order:
CryptAcquireContext(&cspContext,NULL,NULL,PROV_RSA_FULL ,0);
acquires an handle to the Cryptographic Service Provider (CSP )with the characteristics specified as the 4th parameter.The 3rd parameter is the name of service provider.If it is NULL
then a handle to the default provider (�Microsoft Base Cryptographic Provider� ) is provided.
CryptCreateHash(cspContext,CALG_MD5,0,0,&hashData);
The above function creates a hash object. The valid values for the 2nd parameter depends on the CSP and the characteristics specified while acquiring the context.If the value of the parameter is not compatible with CSP characteristics it will give strange results via execution.
CryptHashData(hashData,(const unsigned
char*)encryptKey.GetBuffer(256),encryptKey.GetLength(),0);
produces a hash data based on the string passed to the constructor and the hash object created in the previous step.
CryptDeriveKey(cspContext,CALG_RC2,hashData,0,&keyEncryption);
This function generates the key based on the hashed data available. Again the 2nd parameter value of "CALG_RC2" is dependent on the characteristics of the CSP.
All the crypt APIs discussed above return 1 on success and 0 on error.Besides,the above code guarantees that the key generated from same string will give equivalent keys. Now let us look into the functions Encrypt()
and DeCrypt()
int CEncrypt::Encrypt(char *msg,long orginalSize);
accepts the string to be encrypted and the orginal size of the buffer holding the string.It just calls CryptEncrypt
API.The size of the encrypted text can be larger than the lenght of "msg
" i.e., why we pass the original size of the buffer. If the buffer is not large enough to hold the encrypted text then a �NTE_NO_MEMORY
� will be generated.After the encryption the �actualSize� param contains the actual size of the encrypted text.
The CEncrypt::DeCrypt
just wraps up a call to CryptDecrypt
API.
Registry Operations
The registry is organized into keys and their values.A key in turn can have subkeys. Before retrieving a value of the key or writing a new value into it,we need to open a key.This is what is being done in the CRegister
class constructor.It accepts the name of the key which is supposed to be a descendent of the HKEY_CURRENT_USER
.It obtains a handle to the key via a call to RegCreateKeyEx
. The RegCreateKeyEx
function creates the specified key. If the key already exists in the registry, the function opens it.The first param is the handle of an already open key or any of the predefined reserved HANDLE values like HKEY_CURRENT_USER.
Reading a Registry Value:
char *CRegister::getRegValue(char *key,DWORD *aSize);
accepts the keyName
whose value is to be read.It returns a string with the key value and the size of the read data is returned in aSize
. The code for this function looks as follows:
if(RegQueryValueEx(hKey, key, 0, &dwType,
(PBYTE)NULL, &dwDataSize)==ERROR_SUCCESS && hKey!=NULL)
{
dest=(char *)malloc(sizeof(char)*256);
RegQueryValueEx(hKey, key, 0,&dwType,(PBYTE)(LPTSTR)dest,&dwDataSize);
}
RegQueryValueEx
is used to retrieve the value from registry. The first call with the buffer set to NULL is for knowing the size of the key value,so that we can allocate adequate memory space for the registry value during the second call.
Setting a Registry Value:
void CRegister::setRegValue(char *key,char * newValue,long actualSize);
setRegValue
simply sets the value of a key to a given data using the RegSetValueEx
API.
Points to Note
dwType
in RegQueryValueEx
and RegSetValueEx
which specifies type of value�s data is set to REG_BINARY
, i.e., we read and writevalues in binary form.This is done to avoid unnecessary translation of special characters while reading and writing to registry. For example suppose after encryption we get the following string �99 63 AF 00 1F F5 5E 3C� where the values in quotes represent the ascii values of characters in hexadecimal format. Notice the NULL character represented by �00�.Now if you set the dwType as REG_SZ
the string �99 63 AF 00� will only be written to registry. So information is lost and you cannot decrypt this incomplete string.For the same reason avoid using standard C-string functions on the value fetched from the registry.For example in the above case "strlen
" will give length as 3 whereas the actual size is 8.