0

Delphi Xe4. Use functions Win CryptoAPI - CryptEncrypt & CryptDecrypt.

http://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx (Enc) http://msdn.microsoft.com/en-us/library/windows/desktop/aa379913(v=vs.85).aspx (DeCr)

Everything works fine, encrypts and decrypts a string. But all the examples I see that the option "HCRYPTHASH hHash" is not used and is 0. And I need to encrypt more than an encrypted string issued its hash (not calculated separately, get with CryptEncrypt(hProv, Hash, ...). And the decryption - get hash of the source string.

I do not know how to implement it.

I would be grateful if anyone will show an example in Delphi.

p.s. update deleted *

Gu.
  • 1,947
  • 4
  • 30
  • 49

1 Answers1

4

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.

iPath ツ
  • 2,468
  • 20
  • 31
  • Thank you. Yes, I talked about it. Prior to that, I also thought of, but I'm confused in the translation of strings and their lengths, so if possible, not further pseudo code (need normal) :) And see update* – Gu. May 06 '13 at 16:27
  • For a real code, please add your Encryption procedure to the question. – iPath ツ May 06 '13 at 16:39
  • In TMyHash.ppp() you are getting hash of an AnsiString which is Single Byte char. But in your Cr/Decr functions you are hashing Unicode (UTF16LE) strings which is two byte char - that's why hashes differ. At byte level the string '123' as AnsiString is not the same as '123' in Unicode(UTF16LE). – iPath ツ May 06 '13 at 17:05
  • Yes, it seems that the whole thing in the lengths and types of strings. Thanks again, the topic is closed. – Gu. May 06 '13 at 17:11
  • TEncoding class will help you to deal with strings in different encodings and their byte representations. For example: tb := TEncoding.UTF8.getBytes('123') where tb is of type TBYTES. Then encrypt these bytes. On decryption use s := TEncoding.UTF8.getString(tb) where s is UNICODESTRING and tb is TBYTES. – iPath ツ May 06 '13 at 17:14
  • So your goal is to have Enc/Dec functions for strings in Delphi, using CryptoApi? – iPath ツ May 06 '13 at 17:15
  • 1. Yes, thank you, I thought about that too 2. Yes, it was necessary to encrypt a string with a password and put it in the file hash. when checking the password hash is checked (a simple task, but it was interesting.) – Gu. May 06 '13 at 17:22
  • See [this](http://stackoverflow.com/questions/14411975/simple-code-to-encrypt-an-ini-file-string-using-a-password/14462455#14462455) for another example of a password-based encryption of a string using the Crypto API – Glenn1234 May 07 '13 at 01:40