Search code examples
phphashcryptographysalt-cryptographycryptoapi

Translate Windows CryptoAPI Salt and Hash to PHP


We have a legacy application that creates a Salt and Hash using the CryptoHashData function of the Windows CryptoAPI. We take a random byte string, apply the hash and then apply that hash to a second string of data using CryptoHashData again. Here is the code. A random key is hashed and then hashed again with the secret data.

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);

We have been able to recreate the first step on PHP by using the simple md5() function to hash the random byte string baKeyRandom. The value returned by this hash matches exactly the first hash on the ::CryptoHashData.

$random = md5($random); 

matches exactly with

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

What we haven’t been able to figure out is how the CryptoAPI combines the first hash in the buffer with the second data string to being hashed with it.
i.e. How do we recreate the second step of

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

We’ve tried the most obvious methods of:

$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);

None of these methods match the values from CryptoHashData(random) + CryptoHashData(secret)

Does anyone know how the CryptoHashData will combine the two values in the buffer that will then be hashed? Has anyone recreated this CryptoAPI hash+salt method on other platforms that could be reproduced in PHP? Yes, we know this isn’t secure and its not being used for passwords or PII. We would like to move away from this old and insecure method, but we need take this interim step of translating the original encryption to PHP first for interoperability with the existing deployed systems. Any help or guidance would be appreciated.

Here is the final working code based on accepted answer below.

<?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();
?>

Solution

  • You cannot use md5() function in PHP for progressive updating of hash. Chaining of hash algorithms are based on internal state of algorithm, not inputs. The internal state of md5 cannot be transferred when we use md5() method, so progressive update is not possible.

    You need to use hash_init(), hash_update(), and hash_final() for stream hashing. Here is an example:

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

    This code result will be similar to your Windows API calls.