【问题标题】:Translate Windows CryptoAPI Salt and Hash to PHP将 Windows CryptoAPI Salt and Hash 转换为 PHP
【发布时间】:2025-12-08 13:55:02
【问题描述】:

我们有一个旧应用程序,它使用 Windows CryptoAPI 的 CryptoHashData 函数创建 Salt 和 Hash。我们取一个随机字节字符串,应用散列,然后再次使用 CryptoHashData 将该散列应用于第二个数据字符串。这是代码。随机密钥被散列,然后与秘密数据再次散列。

BYTE baKeyRandom[10] = {87,253, ...}; 
pszSecret = 'ABCDEF-G...';
::CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hSaveHash); 
::CryptHashData(hSaveHash, baKeyRandom, (DWORD)sizeof(baKeyRandom), 0);
::CryptHashData(hSaveHash, (LPBYTE)T2CW(pszSecret), (DWORD)_tcslen(pszSecret) * sizeof(WCHAR), 0);

我们已经能够通过使用简单的 md5() 函数对随机字节字符串 baKeyRandom 进行散列来重新创建 PHP 的第一步。此哈希返回的值与 ::CryptoHashData 上的第一个哈希完全匹配。

$random = md5($random); 

完全匹配

::CryptHashData(hSaveHash, baKeyRandom, (DWORD)sizeof(baKeyRandom), 0);

我们无法弄清楚 CryptoAPI 如何将缓冲区中的第一个哈希值与第二个数据字符串结合起来进行哈希处理。
即我们如何重新创建第二步

::CryptHashData(hSaveHash, (LPBYTE)T2CW(pszSecret), (DWORD)_tcslen(pszSecret) * sizeof(WCHAR), 0);

我们尝试了以下最明显的方法:

$random = md5($random); followed by any of the below
$key = md5($random.$secret); 
$key = md5($secret.$random); 
$key = md5($random.md5($secret)); 
$key = md5(md5($secret).$random);

这些方法都不匹配 CryptoHashData(random) + CryptoHashData(secret) 的值

有谁知道 CryptoHashData 如何将缓冲区中的两个值组合起来,然后进行哈希处理?有没有人在其他平台上重新创建了这个 CryptoAPI hash+salt 方法,可以用 PHP 复制?是的,我们知道这不安全,而且它不用于密码或 PII。我们希望摆脱这种陈旧且不安全的方法,但我们需要先将原始加密转换为 PHP 的临时步骤,以便与现有部署的系统进行互操作。任何帮助或指导将不胜感激。

这是基于下面接受的答案的最终工作代码。

<?php
function main(){
    $random = pack('c*', 22,194,253,.......); 
    $secret = pack('c*', 22,194,253,.......);

    $hash = hash_init("md5");
    hash_update($hash, $random);
    hash_update($hash, $secret);
    print(hash_final($hash));
}

main();
?>

【问题讨论】:

  • 首先,this 将帮助您了解 CryptHashData 的运作方式。另请参阅 API cryptgethashparam
  • $key = md5($random.$secret); 这必须有效,但是,您必须处理字符串转换。在处理字符串转换之前,您还可以使用两个数组进行测试。
  • @kelalaka,感谢您的回复。我已经阅读了您提到的上一篇文章,并且您可以看到我已经尝试了那里描述的方法组合。不清楚当第二个散列与包含第一个散列的缓冲区结合时会发生什么。它说它是附加的,但我认为还有更多。我还在使用您的文档链接中提到的 Crypto API 的 CryptoGetHashParam 函数,这就是我如何判断第二个哈希的结果与 PHP 结果不匹配的方法。 PHP 似乎以与 CryptoAPI 不同的方式处理盐。
  • 除非我对你所说的感到困惑,否则你描述的测试正是我正在做的,结果不匹配。请注意,我无法更改 Windows 端的任何方法,因为尽管我创建了测试版本,但无法更改该代码。 md5 random + md5($random.$secret) 不匹配 CryptoHashData random + (CryptoHashData random + secret) 数组和秘密值是固定的,所以不同之处在于它们在每一侧的处理方式,这是我试图弄清楚的.
  • 嗨@kelalaka,我现在尝试了您描述的简单测试,并使用 CryptoGetHashParam 分两个阶段监控哈希结果。 CryptoHashData random1 的第一个哈希与 md5($random1) 完全匹配。第二个 CryptoHashData random1 + random2 不匹配 $key = md5($random.$secret); 的任何组合; $key = md5($secret.$random); $key = md5($random.md5($secret)); $key = md5(md5($secret).$random);双方对第一个散列使用相同的字节数组,我只是将它复制为第二个散列的输入以保持它非常简单。哈希 1 匹配,哈希 2 不匹配。

标签: php hash cryptography salt cryptoapi


【解决方案1】:

您不能在 PHP 中使用 md5() 函数来进行哈希的渐进式更新。 哈希算法的链接是基于算法的内部状态,而不是输入。 当我们使用md5() 方法时,md5 的内部状态无法转移,因此无法进行渐进式更新。

您需要使用hash_init()hash_update()hash_final() 进行流散列。这是一个例子:

<?php
$hash = hash_init("md5");
$data1="ABCD";
hash_update($hash, $data1);
$data2="ABCD";
hash_update($hash, $data2);
print(hash_final($hash));
?>

此代码结果将类似于您的 Windows API 调用。

【讨论】:

  • 嗨@Afshin 你太棒了!这正是所需要的。现在,第二个哈希的结果在两个系统上完全匹配。 Windows CryptoHashData + CryptoHashData + CryptoHashGetParam 现在匹配 hash_update($hash,$data1) + hash_update($hash, data2) + print(hash_final($hash));。谢谢你的圣诞礼物!