I suppose you are trying to encrypt & hash plaintext data in one pass?
If yes, then first you have to create a Hash object and pass it's handle to CryptEncrypt API. Then retrieve the hash using CryptGetHashParam.
This is a pseudo code (not tested, but gives you an idea how to proceed):
procedure doSomeEncryption()
var
HASHOBJ: HCRYPTHASH;
hProv: HCRYPTPROV;
bHash: tBytes;
dwHashBytes: DWORD;
begin
if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
raiseLastOsError;
if not CryptCreateHash(hProv, CALG_SHA, 0, 0, @HASHOBJ) then
raiseLastOsError;
// Your encrypt stuff here
CryptEncrypt(yourHKey, HASHOBJ, ...) //
setLength(bHash, 255); // Allocate the buffer
if CryptGetHashParam(HASHOBJ, HP_HASHVAL, @bHash[0], @dwHashBytes, 0) then
begin
setLength(bHash, dwHashBytes); // bHash now contains the hash bytes
end
else
setLength(bHash, 0);
// Release HASHOBJ
CryptDestroyHash(HASHOBJ);
// Release Provider Context
CryptReleaseContext(hProv, 0);
end;
- In my pseudo code I rely on Jedi API project (JWA) because it contains translation of almost all Windows APIs and types (including Crypt API). You can include it in your projects.
- The pseudo code needs some improvements of API error handling.
- bHash contains the hash of the plaintext version of your data. Be aware that Hashing (as well as Encryption) is byte oriented operation. That is it does not "understand" string encodings.
Visually the same string value encoded in UTF16, UTF8 and ASCII will have different byte representation, so it will have different Hash value. Keep in mind Encodings when Hashing/Encrypting.
BTW this behavior is documented for CryptEncrypt API in MSDN:
hHash [in] A handle to a hash object. If data is to be hashed and
encrypted simultaneously, a handle to a hash object can be passed in
the hHash parameter. The hash value is updated with the plaintext
passed in. This option is useful when generating signed and encrypted
text. Before calling CryptEncrypt, the application must obtain a
handle to the hash object by calling the CryptCreateHash function.
After the encryption is complete, the hash value can be obtained by
using the CryptGetHashParam function, or the hash can be signed by
using the CryptSignHash function. If no hash is to be done, this
parameter must be NULL.
UPDATE
After Encryption H1 will be the hash of the data before encryption i.e. H1=HASH('aaa')
After Decryption H2 will be the hash of the decrypted data (plaintext value).
So in your case if the decryption was successful then H2 will be equal to HASH('aaa') i.e. H1 = H2.
The purpose of H1 & H2 is to check data integrity. Usually Decrypt functions will not tell you if the decryption was successful. If you try to decrypt the data with wrong password then you receive garbage bytes. So there is the problem - how to know if the decryption was successful? One way to do it is to use the hash of the data on the input and on the output. If they match - your Decryption was successful. If hashes differ then probably decryption has failed silently due to wrong password (for example).
CryptEncrypt/CryptDecrypt provide a convenient way to get those hashes in one operation, instead to hash it separately.