我想您正在尝试一次性加密和散列明文数据?如果是,那么首先您必须创建一个 Hash 对象并将其句柄传递给 CryptEncrypt API。然后使用 CryptGetHashParam 检索哈希。
这是一个伪代码(未经测试,但可以让您了解如何进行):
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;
- 在我的伪代码中,我依赖Jedi API 项目 (JWA),因为它包含几乎所有 Windows API 和类型(包括 Crypt API)的翻译。您可以将其包含在您的项目中。
- 伪代码需要对 API 错误处理进行一些改进。
- bHash 包含数据明文版本的哈希值。请注意,散列(以及加密)是面向字节的操作。那就是它不“理解”字符串编码。视觉上用 UTF16、UTF8 和 ASCII 编码的相同字符串值会有不同的字节表示,因此会有不同的 Hash 值。在散列/加密时记住编码。
顺便说一句,MSDN 中的 CryptEncrypt API 记录了这种行为:
hHash [in] 哈希对象的句柄。如果要同时对数据进行散列和加密,则可以在 hHash 参数中传递散列对象的句柄。哈希值使用传入的明文进行更新。此选项在生成签名和加密文本时很有用。在调用 CryptEncrypt 之前,应用程序必须通过调用 CryptCreateHash 函数获得散列对象的句柄。加密完成后,可以使用 CryptGetHashParam 函数获取哈希值,也可以使用 CryptSignHash 函数对哈希进行签名。如果不进行散列,则此参数必须为 NULL。
更新
加密后 H1 将是加密前数据的哈希值,即 H1=HASH('aaa')
解密后 H2 将是解密数据的哈希值(明文值)。
因此,在您的情况下,如果解密成功,则 H2 将等于 HASH('aaa') 即 H1 = H2。
H1 & H2 的目的是检查数据完整性。通常解密函数不会告诉你解密是否成功。如果您尝试使用错误的密码解密数据,则会收到垃圾字节。那么问题来了——如何知道解密是否成功?一种方法是在输入和输出上使用数据的散列。如果它们匹配 - 您的解密成功。如果哈希值不同,则可能由于密码错误(例如)而导致解密失败。CryptEncrypt/CryptDecrypt 提供了一种在一次操作中获取这些哈希值的便捷方法,而不是单独对其进行哈希处理。