|
|
//+---------------------------------------------------------------------------
//
// File: cryptfnc.cpp
//
// Contents: This file implements the cryptfnc class that provides
// easy to use interfaces on the CryptoAPI.
//
// History: AshishS Created 12/03/96
//
//----------------------------------------------------------------------------
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile
#include <windows.h>
#include <cryptfnc.h>
#include <wincrypt.h>
#include <dbgtrace.h>
#ifndef CRYPT_MACHINE_KEYSET
#define CRYPT_MACHINE_KEYSET 0x00000020
#endif
// this function Generates a SessionKey using the pszPassword
// parameter
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::GenerateSessionKeyFromPassword( HCRYPTKEY * phKey, // location to store the session key
TCHAR * pszPassword) // password to generate the session key from
{ DWORD dwLength; HCRYPTHASH hHash = 0;
TraceFunctEnter("GenerateSessionKeyFromPassword");
// Init should have been successfully called before
_ASSERT(m_hProv); // Create hash object.
if(!CryptCreateHash(m_hProv, // handle to CSP
CALG_SHA, // use SHA hash algorithm
0, // not keyed hash
0, // flags - always 0
&hHash)) // address where hash object should be created
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptCreateHash", GetLastError()); goto cleanup; }
// Hash password string.
dwLength = lstrlen(pszPassword) * sizeof(TCHAR); if(!CryptHashData(hHash, // handle to hash object
(BYTE *)pszPassword, // address of data to be hashed
dwLength, // length of data
0)) // flags
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptHashData", GetLastError()); goto cleanup; }
// Create block cipher session key based on hash of the password.
if(!CryptDeriveKey(m_hProv, //CSP provider
CALG_RC2, // use RC2 block cipher algorithm
hHash, //handle to hash object
0, // no flags - we do not need the key to be exportable
phKey)) //address the newly created key should be copied
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptDeriveKey", GetLastError()); goto cleanup; } // Destroy hash object.
_VERIFY(CryptDestroyHash(hHash)); TraceFunctLeave(); return TRUE; cleanup:
// Destroy hash object.
if(hHash != 0) _VERIFY(CryptDestroyHash(hHash)); TraceFunctLeave(); return FALSE; }
// This function generates a Hash using the SHA hashing
// algorithm. Four seperate buffers of data can be given to this
// function. Data of length 0 will not be used in calculation of HASH.
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::GenerateHash( BYTE * pbData, // data to hash
DWORD dwDataLength, // length of data to hash
BYTE * pbData1, // another data to hash
DWORD dwData1Length, // length of above data
BYTE * pbData2, // another data to hash
DWORD dwData2Length, // length of above data
BYTE * pbData3, // another data to hash
DWORD dwData3Length, // length of above data
BYTE * pbHashBuffer, // buffer to store hash
DWORD * pdwHashBufLen)//length of buffer to store Hash
{ DWORD dwLength, dwResult; BOOL fResult; HCRYPTHASH hHash = 0;
TraceFunctEnter("GenerateHash");
// Init should have been successfully called before
_ASSERT(m_hProv);
dwResult = WaitForSingleObject( m_hSemaphore,// handle of object to wait for
INFINITE); // no time-out
_ASSERT(WAIT_OBJECT_0 == dwResult); // At least one of the Data Pairs should be valid
if (! (( pbData && dwDataLength ) || ( pbData1 && dwData1Length ) || ( pbData2 && dwData2Length ) || ( pbData3 && dwData3Length ) ) ) { ErrorTrace(CRYPT_FNC_ID, "No Data to hash"); goto cleanup; } // now ask the user for a password and generate a session key based
// on the password. This session key will be used to encrypt the secret
// key.
// Create hash object.
if(!CryptCreateHash(m_hProv, // handle to CSP
CALG_SHA, // use SHA hash algorithm
0, // not keyed hash
0, // flags - always 0
&hHash)) // address where hash object should be created
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptCreateHash", GetLastError()); goto cleanup; }
if ( pbData && dwDataLength ) { if(!CryptHashData(hHash, // handle to hash object
pbData, // address of data to be hashed
dwDataLength, // length of data
0)) // flags
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptHashData", GetLastError()); goto cleanup; } }
if ( pbData1 && dwData1Length ) { if(!CryptHashData(hHash, // handle to hash object
pbData1, // address of data to be hashed
dwData1Length, // length of data
0)) // flags
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptHashData", GetLastError()); goto cleanup; } }
if (pbData2) { if(!CryptHashData(hHash, // handle to hash object
pbData2, // address of data to be hashed
dwData2Length, // length of data
0)) // flags
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptHashData", GetLastError()); goto cleanup; } }
if (pbData3) { if(!CryptHashData(hHash, // handle to hash object
pbData3, // address of data to be hashed
dwData3Length, // length of data
0)) // flags
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptHashData", GetLastError()); goto cleanup; } } if (! CryptGetHashParam( hHash,// handle to hash object
HP_HASHVAL,// get the hash value
pbHashBuffer, // hash buffer
pdwHashBufLen, // hash buffer length
0 )) // flags
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptGetHashParam", GetLastError()); goto cleanup; } // Destroy hash object.
_VERIFY(CryptDestroyHash(hHash));
_VERIFY(fResult = ReleaseSemaphore(m_hSemaphore, 1, NULL)); TraceFunctLeave(); return TRUE; cleanup:
// Destroy hash object.
if(hHash != 0) _VERIFY(CryptDestroyHash(hHash));
_VERIFY(fResult = ReleaseSemaphore(m_hSemaphore, 1, NULL)); TraceFunctLeave(); return FALSE; }
// This function must be called before any member functions of the
// class are used.
// Returns FALSE if a Fatal error occured, TRUE otherwise
// this always gets the machine keyset
BOOL CCryptFunctions::InitCrypt() { TraceFunctEnter("InitCrypt"); TCHAR szSemaphoreName[100]; SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd; BOOL fResult; if (m_hProv) { DebugTrace(CRYPT_FNC_ID,"Already been inited before"); TraceFunctLeave(); return TRUE; }
// create a Unique semaphore name for each process
wsprintf(szSemaphoreName, TEXT("%s%d"), CRYPTFNC_SEMAPHORE_NAME, GetCurrentProcessId());
// also create a security descriptor so that everyone has access
// to the semaphore. Otherwise if the semaphore is created in a
// service running in system context, then only system can use
// this semaphore.
fResult = InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
_ASSERT(fResult); // if the security descriptor has a NULL DACL, then this gives
// everyone access to this semaphore.
fResult = SetSecurityDescriptorDacl(&sd, TRUE, NULL, // NULL ACL
FALSE);
_ASSERT(fResult); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = & sd;
m_hSemaphore = CreateSemaphore(&sa, // pointer to security attributes
1, // initial count
1, // maximum count
szSemaphoreName);// pointer to Semaphore-object name
if ( NULL == m_hSemaphore) { DWORD dwError;
dwError = GetLastError(); ErrorTrace(CRYPT_FNC_ID, "CreateSemaphore failed 0x%x", dwError); _ASSERT(0); goto cleanup; }
if(!CryptAcquireContext(&m_hProv, // address to get the handle to CSP
NULL, // contianer name - use default container
NULL, // provider
PROV_RSA_FULL, // type of provider
CRYPT_VERIFYCONTEXT)) { ErrorTrace(CRYPT_FNC_ID, "Fatal Error 0x%x during first" "call to CryptAcquireContext", GetLastError()); goto cleanup; } #if 0 // This code is being commented out since there are problems
// getting the machine keyset from an ASP app running in the IIS
// anonymous user context. This means that we will not be able to
// do any signing or in some cases encryption. This is fine since
// we do not want to do this currently.
// Get handle to machine default provider.
if(!CryptAcquireContext(&m_hProv, // address to get the handle to CSP
NULL, // contianer name - use default container
MS_DEF_PROV, // provider
PROV_RSA_FULL, // type of provider
CRYPT_MACHINE_KEYSET)) { DWORD dwError; dwError = GetLastError(); ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptAcquireContext", dwError); DebugTrace(CRYPT_FNC_ID, "Calling CryptAcquireContext again" "to create keyset"); if (! CryptAcquireContext(&m_hProv,// handle to CSP
NULL,// contianer name - use default
MS_DEF_PROV, // provider
PROV_RSA_FULL, // type of provider
CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET) ) // create the keyset
{ ErrorTrace(CRYPT_FNC_ID, "Fatal Error 0x%x during second" "call to CryptAcquireContext", GetLastError()); goto cleanup; } } #endif
TraceFunctLeave(); return TRUE; cleanup: // Release provider handle.
if(m_hProv != 0) { _VERIFY(CryptReleaseContext(m_hProv, 0)); m_hProv =0 ; } TraceFunctLeave(); return FALSE; }
CCryptFunctions::~CCryptFunctions() { TraceFunctEnter("~CCryptFunctions"); // Release provider handle.
if(m_hProv != 0) { _VERIFY(CryptReleaseContext(m_hProv, 0)); }
if (NULL != m_hSemaphore) { _VERIFY(CloseHandle(m_hSemaphore)); } TraceFunctLeave(); }
CCryptFunctions::CCryptFunctions() { m_hProv = 0; m_hSemaphore = NULL; }
// This function generates random data of length dwLength bytes. This
// data is guaranteed by CryptoAPI to be truly random.
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::GenerateSecretKey( BYTE * pbData,// Buffer to store secret key
//buffer must be long enough for dwLength bits
DWORD dwLength ) // length of secret key in bytes
{ DWORD dwResult; BOOL fResult; // Init should have been successfully called before
_ASSERT(m_hProv); TraceFunctEnter("GenerateSecretKey");
dwResult = WaitForSingleObject( m_hSemaphore,//handle of object to wait for
INFINITE); // no time-out
_ASSERT(WAIT_OBJECT_0 == dwResult); // Create a random dwLength byte number for a secret.
if(!CryptGenRandom(m_hProv, // handle to CSP
dwLength , // number of bytes of
// random data to be generated
pbData )) // buffer - uninitialized
{ _VERIFY(fResult = ReleaseSemaphore(m_hSemaphore, 1, NULL)); ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptGenRandom", GetLastError()); TraceFunctLeave(); return FALSE; }
_VERIFY(fResult = ReleaseSemaphore(m_hSemaphore, 1, NULL)); TraceFunctLeave(); return TRUE; }
// Given a password, and data to encrypt this function generates a
// session key from the password. This session key is then used to
// encrypt the data.
// Returns FALSE if a Fatal error occured, TRUE otherwise
BOOL CCryptFunctions::EncryptDataWithPassword( TCHAR * pszPassword, // password
BYTE * pbData, // Data to be encrypted
DWORD dwDataLength, // Length of data in bytes
BYTE * pbEncryptedData, // Encrypted secret key will be stored here
DWORD * pdwEncrytedBufferLen // Length of this buffer
) { DWORD dwBufferLength; HCRYPTKEY hKey = 0; TraceFunctEnter("EncryptDataWithPassword"); // Init should have been successfully called before
_ASSERT(m_hProv);
if (!GenerateSessionKeyFromPassword(&hKey, pszPassword)) goto cleanup;
if (dwDataLength > * pdwEncrytedBufferLen ) { ErrorTrace(CRYPT_FNC_ID, "Buffer not large enough"); goto cleanup; } // copy the data into another buffer to encrypt it
memcpy (pbEncryptedData, pbData, dwDataLength); dwBufferLength = *pdwEncrytedBufferLen; *pdwEncrytedBufferLen = dwDataLength; // now encrypt the secret key using the key generated
if ( ! CryptEncrypt(hKey, 0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
pbEncryptedData, // data buffer
pdwEncrytedBufferLen, // length of data
dwBufferLength ) ) // size of buffer
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptEncrypt", GetLastError()); goto cleanup; }
// destroy session key
_VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return TRUE; cleanup: // destroy session key
if (hKey != 0) _VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return FALSE; }
// Given a password, and encrypted data using EncryptDataWithPassword,
// this function generates a session key from the password. This
// session key is then used to decrypt the data.
// returns
// CRYPT_FNC_NO_ERROR no error
// CRYPT_FNC_BAD_PASSWORD password bad try again
// CRYPT_FNC_INSUFFICIENT_BUFFER larger buffer is required
// *pdwEncrytedBufferLen is set to required length
// CRYPT_FNC_INIT_NOT_CALLED InitCrypt not successfully called
// CRYPT_FNC_INTERNAL_ERROR
DWORD CCryptFunctions::DecryptDataWithPassword( TCHAR * pszPassword, // password
BYTE * pbData, // Decrypted Data will be stored here
DWORD *pdwDataBufferLength, // Length of the above buffer in bytes
BYTE * pbEncryptedData, // Encrypted data
DWORD dwEncrytedDataLen // Length of encrypted data
) { DWORD dwBufferLength; HCRYPTKEY hKey = 0; TraceFunctEnter("DecryptDataWithPassword"); DWORD dwError; // Init should have been successfully called before
if (m_hProv== 0) { dwError = CRYPT_FNC_INIT_NOT_CALLED; goto cleanup; } if (!GenerateSessionKeyFromPassword(&hKey, pszPassword)) { dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; }
// check if buffer is large enough
if ( dwEncrytedDataLen > *pdwDataBufferLength ) { dwError = CRYPT_FNC_INSUFFICIENT_BUFFER; *pdwDataBufferLength = dwEncrytedDataLen; goto cleanup; } // copy the data into another buffer to encrypt it
memcpy (pbData, pbEncryptedData, dwEncrytedDataLen); *pdwDataBufferLength = dwEncrytedDataLen;
// now decrypt the secret key using the key generated
if ( ! CryptDecrypt(hKey, 0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
pbData, // data buffer
pdwDataBufferLength )) // length of data
{ DWORD dwCryptError = GetLastError(); DebugTrace(CRYPT_FNC_ID, "Error 0x%x during CryptDecrypt", dwCryptError); // CryptDecrypt fails with error NTE_BAD_DATA if the password
// is incorrect. Hence we should check for this error and prompt the
// user again for the password
// Issue: if the data is garbled in transit, then the secret key
// will still be decrypted into a wrong value and the user will not
// know about it.
if ( dwCryptError == NTE_BAD_DATA ) { dwError = CRYPT_FNC_BAD_PASSWORD; } else { dwError = CRYPT_FNC_INTERNAL_ERROR; } goto cleanup; }
// destroy session key
_VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return CRYPT_FNC_NO_ERROR; cleanup: // destroy session key
if (hKey != 0) _VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return dwError; }
/*
This function: 1. Generates a session key to encrypt the secret data 2. Encrypts the secret data using this session key - return this value in the pbEncryptedData parameter 3. Encrypts the session key using the public key in the CSP - only the private key can decrypt this value. Return the encrypted session key in the pbEncryptedSessionKey parameter. returns CRYPT_FNC_NO_ERROR no error CRYPT_FNC_INSUFFICIENT_BUFFER larger buffer is required *pdwEncrytedBufferLen and are set to required length CRYPT_FNC_INIT_NOT_CALLED InitCrypt not successfully called CRYPT_FNC_INTERNAL_ERROR */ DWORD CCryptFunctions::EncryptDataAndExportSessionKey( BYTE * pbData, // Secret Data
DWORD dwDataLen, // Secret Data Length
BYTE * pbEncryptedData, // Buffer to store Encrypted Data
DWORD * pdwEncrytedBufferLen, // Length of above buffer
BYTE * pbEncryptedSessionKey, // Buffer to store encrypted session key
DWORD * pdwEncrytedSessionKeyLength) // Length of above buffer
{ HCRYPTKEY hXchgKey = 0; HCRYPTKEY hKey = 0; DWORD dwBufferLen, dwError; TraceFunctEnter("EncryptDataAndExportSessionKey"); // Init should have been successfully called before
if (m_hProv== 0) { dwError = CRYPT_FNC_INIT_NOT_CALLED; goto cleanup; } // now get the public key to encrypt Secret key for storage
// Get handle to exahange key.
if(!CryptGetUserKey(m_hProv, // CSP provider
AT_KEYEXCHANGE, // we need the exchange public key
&hXchgKey)) { DWORD dwCryptError; dwCryptError = GetLastError(); if ( dwCryptError == NTE_NO_KEY ) { DebugTrace(CRYPT_FNC_ID, "Error NTE_NO_KEY during" "CryptGetUserKey"); DebugTrace(CRYPT_FNC_ID, "Calling CryptGenKey to generate key"); if (!CryptGenKey( m_hProv,// CSP provider
AT_KEYEXCHANGE, // generate the exchange
//public key
0, //no flags
&hXchgKey ) ) { ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptGenKey", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; } } else { ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptGetUserKey", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; }
}
// now generate a random session key to encrypt the secret key
// Create block cipher session key.
if (!CryptGenKey(m_hProv, // CSP provider
CALG_RC2, // use RC2 block cipher algorithm
CRYPT_EXPORTABLE, // flags
&hKey)) // address of key
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptGenKey", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; }
// Export key into a simple key blob.
if(!CryptExportKey(hKey, // key to export
hXchgKey, // our exchange public key
SIMPLEBLOB, // type of blob
0, // flags (always 0)
pbEncryptedSessionKey, // buffer to store blob
pdwEncrytedSessionKeyLength)) // length of above buffer
{ // BUGBUG check here if insufficient buffer error
ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptExportKey", GetLastError()); goto cleanup; }
// check if buffer is large enough
if ( dwDataLen > *pdwEncrytedBufferLen ) { dwError = CRYPT_FNC_INSUFFICIENT_BUFFER; *pdwEncrytedBufferLen = dwDataLen; goto cleanup; } // copy the data into another buffer to encrypt it
memcpy (pbEncryptedData, pbData, dwDataLen);
dwBufferLen = *pdwEncrytedBufferLen; *pdwEncrytedBufferLen = dwDataLen; // now encrypt the secret key using the key generated
if ( ! CryptEncrypt(hKey, 0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
pbEncryptedData, // data buffer
pdwEncrytedBufferLen, // length of data
dwBufferLen ) ) // size of buffer
{ ErrorTrace(CRYPT_FNC_ID, "Error 0x%x during CryptEncrypt", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; }
_VERIFY(CryptDestroyKey(hKey)); _VERIFY(CryptDestroyKey(hXchgKey));
TraceFunctLeave(); return CRYPT_FNC_NO_ERROR; cleanup: // Destroy key exchange key handle.
if(hXchgKey != 0) _VERIFY(CryptDestroyKey(hXchgKey));
// destroy session key
if (hKey != 0) _VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return dwError; }
/*
This function performs the reverse of EncryptDataAndExportSessionKey: 1. It imports the session key using the private key in the CSP that was used to encrypt the secret data. 2. It decrypts the secret data using this session key - return this value in the pbData parameter
returns CRYPT_FNC_NO_ERROR no error CRYPT_FNC_INSUFFICIENT_BUFFER larger buffer is required *pdwDataLen is set to required length CRYPT_FNC_INIT_NOT_CALLED InitCrypt not successfully called CRYPT_FNC_INTERNAL_ERROR */ DWORD CCryptFunctions::ImportSessionKeyAndDecryptData( BYTE * pbData, // Buffer to store secret Data
DWORD * pdwDataLen, // Length of Above buffer
BYTE * pbEncryptedData, // Buffer that stores Encrypted Data
DWORD dwEncrytedBufferLen, // Length of above data
BYTE * pbEncryptedSessionKey, // Buffer that stores encrypted session key
DWORD dwEncrytedSessionKeyLength) // Length of above data
{ HCRYPTKEY hKey = 0; DWORD dwError; TraceFunctEnter("ImportSessionKeyAndDecryptData"); // Init should have been successfully called before
if (m_hProv== 0) { dwError = CRYPT_FNC_INIT_NOT_CALLED; goto cleanup; }
// now import the key from the registry
// Import key blob into CSP.
if(!CryptImportKey( m_hProv, // CSP provider
pbEncryptedSessionKey,// buffer that stores encrypted
// session key
dwEncrytedSessionKeyLength,// length of data in
// above buffer
0, // since we have a SIMPLEBLOB and the key blob
// is encrypted with the key exchange key pair, this
// parameter is zero
0, // flags
&hKey)) // key to export to
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptImportKey", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; }
// check if buffer is large enough
if ( dwEncrytedBufferLen > *pdwDataLen ) { dwError = CRYPT_FNC_INSUFFICIENT_BUFFER; *pdwDataLen = dwEncrytedBufferLen; goto cleanup; } // copy the data into another buffer to encrypt it
memcpy (pbData, pbEncryptedData, dwEncrytedBufferLen);
*pdwDataLen = dwEncrytedBufferLen; // now decrypt the secret key using the key generated
if ( ! CryptDecrypt(hKey, 0, // no hash required
TRUE, // Final packet
0, // Flags - always 0
pbData, // data buffer
pdwDataLen )) // length of data
{ ErrorTrace(CRYPT_FNC_ID,"Error 0x%x during CryptDecrypt", GetLastError()); dwError = CRYPT_FNC_INTERNAL_ERROR; goto cleanup; } _VERIFY(CryptDestroyKey(hKey));
TraceFunctLeave(); return CRYPT_FNC_NO_ERROR; cleanup:
// destroy session key
if (hKey != 0) _VERIFY(CryptDestroyKey(hKey)); TraceFunctLeave(); return dwError; }
|