将 Windows RC4 CryptDeriveKey 翻译成 PHP for openssl
Translate Windows RC4 CryptDeriveKey to PHP for openssl
这是我们一直在尝试做的遗留系统翻译的第二个组成部分。我们已经成功匹配 Windows ::CryptHashData 生成的初始二进制文件 password/key。
将 password/key 传递给 ::CryptDeriveKey,它会执行多个步骤来创建最终密钥,供 ::CryptEncrypt 使用。我的研究使我找到了 CryptDeriveKey 文档,其中清楚地描述了为 ::CryptEncrypt 派生密钥所需的步骤,但到目前为止,我还无法使用它来解密 PHP 端的文件。
https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptderivekey
根据 ::CryptDeriveKey 文档,对于我们特定的遗留密钥大小,可能还有一些未记录的步骤可能不太好理解。当前 Windows ::CryptDeriveKey 默认设置为零盐,这在某种程度上明显不同于 NO_SALT。在此处查看盐值功能:
https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/salt-value-functionality
我们遗留系统的 CryptAPI 参数如下:
提供商类型:PROV_RSA_FULL
提供商名称:MS_DEF_PROV
算法 ID CALG_RC4
描述RC4流加密算法
密钥长度:40 位。
盐长度:88 位。 ZERO_SALT
特别注意:但是,具有零值盐的 40 位对称密钥不等同于没有盐的 40 位对称密钥。为了互操作性,必须在不加盐的情况下创建密钥。此问题是由于默认情况导致的,该情况仅发生在恰好 40 位的密钥上。
我不打算导出密钥,而是重现创建传递给 ::CryptEncrypt 的 RC4 加密算法的最终加密密钥的过程,并使其与 openssl_decrypt 一起使用。
这是当前的 windows 加密代码。
try {
BOOL bSuccess;
bSuccess = ::CryptAcquireContextA(&hCryptProv,
CE_CRYPTCONTEXT,
MS_DEF_PROV_A,
PROV_RSA_FULL,
CRYPT_MACHINE_KEYSET);
::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);
::CryptDeriveKey(hCryptProv,
CALG_RC4,
hSaveHash,
0,
&hCryptKey);
// Now Encrypt the value
BYTE * pData = NULL;
DWORD dwSize = (DWORD)_tcslen(pszToEncrypt) * sizeof(WCHAR);
// will be a wide str
DWORD dwReqdSize = dwSize;
::CryptEncrypt(hCryptKey,
NULL,
TRUE,
0,
(LPBYTE)NULL,
&dwReqdSize, 0);
dwReqdSize = max(dwReqdSize, dwSize);
pData = new BYTE[dwReqdSize];
memcpy(pData, T2CW(pszToEncrypt), dwSize);
if (!::CryptEncrypt(hCryptKey,
NULL,
TRUE,
0,
pData,
&dwSize,
dwReqdSize)) {
printf("%l\n", hCryptKey);
printf("error during CryptEncrypt\n");
}
if (*pbstrEncrypted)
::SysFreeString(*pbstrEncrypted);
*pbstrEncrypted = ::SysAllocStringByteLen((LPCSTR)pData, dwSize);
delete[] pData;
hr = S_OK;
}
这里是 PHP 代码,它试图按照文档中的描述复制 ::CryptDeriveKey 函数。
令 n 为所需的派生密钥长度,以字节为单位。派生密钥是 CryptDeriveKey 完成哈希计算后哈希值的前 n 个字节。如果散列不是 SHA-2 家族的成员并且所需的密钥用于 3DES 或 AES,则密钥派生如下:
通过重复常量0x36 64 次形成一个64 字节的缓冲区。设 k 是由输入参数 hBaseData 表示的散列值的长度。将缓冲区的前 k 个字节设置为缓冲区的前 k 个字节与输入参数 hBaseData 表示的哈希值进行异或运算的结果。
通过重复常量0x5C 64 次形成一个64 字节的缓冲区。将缓冲区的前 k 个字节设置为缓冲区的前 k 个字节与输入参数 hBaseData 表示的哈希值进行异或运算的结果。
使用与用于计算 hBaseData 参数表示的哈希值的哈希算法相同的哈希算法对步骤 1 的结果进行哈希处理。
使用与用于计算 hBaseData 参数表示的哈希值的哈希算法相同的哈希算法对步骤 2 的结果进行哈希处理。
将第 3 步的结果与第 4 步的结果连接起来。
- 使用步骤 5 结果的前 n 个字节作为派生密钥。
PHP ::CryptDeriveKey 的版本。
function cryptoDeriveKey($key){
//Put the hash key into an array
$hashKey1 = str_split($key,2);
$count = count($hashKey1);
$hashKeyInt = array();
for ($i=0; $i<$count; $i++){
$hashKeyInt[$i] = hexdec($hashKey1[$i]);
}
$hashKey = $hashKeyInt;
//Let n be the required derived key length, in bytes. CALG_RC4 = 40 bits key or 88 salt bytes
$n = 40/8;
//Let k be the length of the hash value that is represented by the input parameter hBaseData
$k = 16;
//Step 1 Form a 64-byte buffer by repeating the constant 0x36 64 times
$arraya = array_fill(0, 64, 0x36);
//Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value
for ($i=0; $i<$k; $i++){
$arraya[$i] = $arraya[$i] ^ $hashKey[$i];
}
//Hash the result of step 1 by using the same hash algorithm as hBaseData
$arrayPacka = pack('c*', ...$arraya);
$hashArraya = md5($arrayPacka);
//Put the hash string back into the array
$hashKeyArraya = str_split($hashArraya,2);
$count = count($hashKeyArraya);
$hashKeyInta = array();
for ($i=0; $i<$count; $i++){
$hashKeyInta[$i] = hexdec($hashKeyArraya[$i]);
}
//Step 2 Form a 64-byte buffer by repeating the constant 0x5C 64 times.
$arrayb = array_fill(0, 64, 0x5C);
//Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value
for ($i=0; $i<$k; $i++){
$arrayb[$i] = $arrayb[$i] ^ $hashKey[$i];
}
//Hash the result of step 2 by using the same hash algorithm as hBaseData
$arrayPackb = pack('c*', ...$arrayb);
$hashArrayb = md5($arrayPackb);
//Put the hash string back into the array
$hashKeyArrayb = str_split($hashArrayb,2);
$count = count($hashKeyArrayb);
$hashKeyIntb = array();
for ($i=0; $i<$count; $i++){
$hashKeyIntb[$i] = hexdec($hashKeyArrayb[$i]);
}
//Concatenate the result of step 3 with the result of step 4.
$combined = array_merge($hashKeyInta, $hashKeyIntb);
//Use the first n bytes of the result of step 5 as the derived key.
$finalKey = array();
for ($i=0; $i <$n; $i++){
$finalKey[$i] = $combined[$i];
}
$key = $finalKey;
return $key;
}
PHP解密函数
function decryptRC4($encrypted, $key){
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
$cypher = ‘rc4-40’;
$decrypted = openssl_decrypt($encrypted, $cypher, $key, $opts);
return $decrypted;
}
下面是大问题:
有没有人能够在另一个系统上用 RC4 成功复制 ::CryptDeriveKey?
有谁知道我们创建的 PHP 脚本缺少什么来阻止它创建相同的密钥并使用 openssl_decrypt 解密 Windows CryptoAPI 加密文件?
我们在哪里以及如何创建 40 位密钥所需的 88 位零盐?
接受此密钥并解密由 ::CryptDeriveKey 生成的内容的正确 openssl_decrypt 参数是什么?
是的,我们知道这不安全,也不会用于密码或 PII。我们想摆脱这种陈旧且不安全的方法,但我们需要采取这个临时步骤,首先将原始加密转换为 PHP,以便与现有部署的系统进行互操作。任何帮助或指导将不胜感激。
以防万一其他人沿着这条路走下去这里是上述所有问题的答案。
您可以使用 openssl 在 PHP 上复制 ::CryptDeriveKey,但必须先在 windows 端满足一些先决条件。
CryptDeriveKey 必须设置为 CRYPT_NO_SALT,如下所示:
::CrypeDeriveKey(hCryptProv, CALG_RC4, hSaveHash, CRYPT_NO_SALT, &hCryptKey)
这将允许您从哈希创建一个密钥,并在 PHP 中生成一个匹配的密钥,该密钥将在 openssl 上运行。如果您不设置任何盐参数,您将获得一个密钥,该密钥是使用未知的专有盐算法创建的,该算法无法在其他系统上匹配。
您必须设置 CRYPT_NO_SALT 的原因是因为 CryptAPI 和 openssl 都有专有的 salt 算法并且无法使它们匹配。所以你应该分开腌制。这里有关于此盐值功能的更多详细信息:https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/salt-value-functionality
下面是 PHP 脚本创建等效密钥以供 openssl 使用所需的内容。
<?php
$random = pack('c*', 87,194,...........);
$origSecret = 'ASCII STRING OF CHARACTERS AS PASSWORD';
//Need conversion to match format of Windows CString or wchar_t*
//Windows will probably be UTF-16LE and LAMP will be UTF-8
$secret = iconv('UTF-8','UTF-16LE', $origSecret);
//Create hash key from Random and Secret
//This is basically a hash and salt process.
$hash = hash_init("md5");
hash_update($hash, $random);
hash_update($hash, $secret);
$key = hash_final($hash);
$key = cryptoDeriveKey($key);
//Convert the key hex array to a hex string for openssl_decrypt
$count = count($key);
$maxchars = 2;
for ($i=0; $i<$count; $i++){
$key .= str_pad(dechex($key[$i]), $maxchars, "0", STR_PAD_LEFT);
}
重要提示:OpenSSL 期望密钥是从散列派生的原始十六进制值,不幸的是 openssl_decrypt() 需要与字符串或密码相同的值。因此,此时您必须进行十六进制到字符串的转换。关于为什么必须这样做,这里有一篇很棒的文章。
http://php.net/manual/en/function.openssl-encrypt.php
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
//Convert key hex string to a string for openssl_decrypt
//Leave it as it is for openssl command line.
$key = hexToStr($key);
$cipher = 'rc4-40';
$encrypted = “the data you want to encrypt or decrypt”;
$decrypted = openssl_decrypt($encrypted, $cipher, $key, $opts);
echo $decrypted; //This is the final information you’re looking for
function cryptoDeriveKey($key){
//convert the key into hex byte array as int
$hashKey1 = str_split($key,2);
$count = count($hashKey1);
$hashKeyInt = array();
for ($i=0; $i<$count; $i++){
$hashKeyInt[$i] = hexdec($hashKey1[$i]);
}
$hashKey = $hashKeyInt;
//Let n be the required derived key length, in bytes. CALG_RC4 = 40 bits key with 88 salt bits
$n = 40/8;
//Chop the key down to the first 40 bits or 5 bytes.
$finalKey = array();
for ($i=0; $i <$n; $i++){
$finalKey[$i] = $hashKey[$i];
}
return $finalKey;
}
function hexToStr($hex){
$string='';
for ($i=0; $i < strlen($hex)-1; $i+=2){
$string .= chr(hexdec($hex[$i].$hex[$i+1]));
}
return $string;
}
?>
如果您在使用上述代码后无法获得正确的值,您可以尝试从 CryptoAPI 导出您的密钥值并使用 openssl 命令行对其进行测试。
首先,您必须设置 CryptDeriveKey 以允许使用 CRYPT_EXPORTABLE 和 CRYPT_NO_SALT
导出密钥
::CrypeDeriveKey(hCryptProv, CALG_RC4, hSaveHash, CRYPT_EXPORTABLE | CRYPT_NO_SALT, &hCryptKey)
如果您想知道如何从导出的密钥中显示 PLAINTEXTKEYBLOB,请遵循此 link。
https://docs.microsoft.com/en-us/windows/desktop/seccrypto/example-c-program--importing-a-plaintext-key
这是一个导出密钥 blob 的示例
0x08 0x02 0x00 0x00 0x01 0x68 0x00 0x00 0x05 0x00 0x00 0x00 0xAA 0xBB 0xCC 0xDD 0xEE
0x08 0x02 0x00 0x00 0x01 0x68 0x00 0x00 //BLOB头几乎完全匹配
0x05 0x00 0x00 0x00 //以字节为单位的密钥长度是正确的 5 个字节
0xAA 0xBB 0xCC 0xDD 0xEE //我们创建的哈希键的前 5 个字节!!
在下面的 openssl enc 命令中使用从 BLOB 导出的键值作为十六进制键值。
openssl enc -d -rc4-40 -in testFile-NO_SALT-enc.txt -out testFile-NO_SALT-dec.txt -K "Hex Key Value" -nosalt -nopad
这将解密在 Windows 机器上使用 CryptEncrypt 加密的文件。
如您所见,当您将 CryptDeriveKey 设置为 CRYPT_NO_SALT 时,您需要的 openssl 密码或密钥是 CryptHashData 密码的第一个“keylength”位。说起来很简单,但真正做到却很痛苦。祝你好运,希望这能帮助其他人解决遗留 Windows 翻译问题。
这是我们一直在尝试做的遗留系统翻译的第二个组成部分。我们已经成功匹配 Windows ::CryptHashData 生成的初始二进制文件 password/key。
将 password/key 传递给 ::CryptDeriveKey,它会执行多个步骤来创建最终密钥,供 ::CryptEncrypt 使用。我的研究使我找到了 CryptDeriveKey 文档,其中清楚地描述了为 ::CryptEncrypt 派生密钥所需的步骤,但到目前为止,我还无法使用它来解密 PHP 端的文件。 https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/nf-wincrypt-cryptderivekey
根据 ::CryptDeriveKey 文档,对于我们特定的遗留密钥大小,可能还有一些未记录的步骤可能不太好理解。当前 Windows ::CryptDeriveKey 默认设置为零盐,这在某种程度上明显不同于 NO_SALT。在此处查看盐值功能: https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/salt-value-functionality
我们遗留系统的 CryptAPI 参数如下:
提供商类型:PROV_RSA_FULL
提供商名称:MS_DEF_PROV
算法 ID CALG_RC4
描述RC4流加密算法
密钥长度:40 位。
盐长度:88 位。 ZERO_SALT
特别注意:但是,具有零值盐的 40 位对称密钥不等同于没有盐的 40 位对称密钥。为了互操作性,必须在不加盐的情况下创建密钥。此问题是由于默认情况导致的,该情况仅发生在恰好 40 位的密钥上。
我不打算导出密钥,而是重现创建传递给 ::CryptEncrypt 的 RC4 加密算法的最终加密密钥的过程,并使其与 openssl_decrypt 一起使用。
这是当前的 windows 加密代码。
try {
BOOL bSuccess;
bSuccess = ::CryptAcquireContextA(&hCryptProv,
CE_CRYPTCONTEXT,
MS_DEF_PROV_A,
PROV_RSA_FULL,
CRYPT_MACHINE_KEYSET);
::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);
::CryptDeriveKey(hCryptProv,
CALG_RC4,
hSaveHash,
0,
&hCryptKey);
// Now Encrypt the value
BYTE * pData = NULL;
DWORD dwSize = (DWORD)_tcslen(pszToEncrypt) * sizeof(WCHAR);
// will be a wide str
DWORD dwReqdSize = dwSize;
::CryptEncrypt(hCryptKey,
NULL,
TRUE,
0,
(LPBYTE)NULL,
&dwReqdSize, 0);
dwReqdSize = max(dwReqdSize, dwSize);
pData = new BYTE[dwReqdSize];
memcpy(pData, T2CW(pszToEncrypt), dwSize);
if (!::CryptEncrypt(hCryptKey,
NULL,
TRUE,
0,
pData,
&dwSize,
dwReqdSize)) {
printf("%l\n", hCryptKey);
printf("error during CryptEncrypt\n");
}
if (*pbstrEncrypted)
::SysFreeString(*pbstrEncrypted);
*pbstrEncrypted = ::SysAllocStringByteLen((LPCSTR)pData, dwSize);
delete[] pData;
hr = S_OK;
}
这里是 PHP 代码,它试图按照文档中的描述复制 ::CryptDeriveKey 函数。
令 n 为所需的派生密钥长度,以字节为单位。派生密钥是 CryptDeriveKey 完成哈希计算后哈希值的前 n 个字节。如果散列不是 SHA-2 家族的成员并且所需的密钥用于 3DES 或 AES,则密钥派生如下:
通过重复常量0x36 64 次形成一个64 字节的缓冲区。设 k 是由输入参数 hBaseData 表示的散列值的长度。将缓冲区的前 k 个字节设置为缓冲区的前 k 个字节与输入参数 hBaseData 表示的哈希值进行异或运算的结果。
通过重复常量0x5C 64 次形成一个64 字节的缓冲区。将缓冲区的前 k 个字节设置为缓冲区的前 k 个字节与输入参数 hBaseData 表示的哈希值进行异或运算的结果。
使用与用于计算 hBaseData 参数表示的哈希值的哈希算法相同的哈希算法对步骤 1 的结果进行哈希处理。
使用与用于计算 hBaseData 参数表示的哈希值的哈希算法相同的哈希算法对步骤 2 的结果进行哈希处理。
将第 3 步的结果与第 4 步的结果连接起来。
- 使用步骤 5 结果的前 n 个字节作为派生密钥。
PHP ::CryptDeriveKey 的版本。
function cryptoDeriveKey($key){
//Put the hash key into an array
$hashKey1 = str_split($key,2);
$count = count($hashKey1);
$hashKeyInt = array();
for ($i=0; $i<$count; $i++){
$hashKeyInt[$i] = hexdec($hashKey1[$i]);
}
$hashKey = $hashKeyInt;
//Let n be the required derived key length, in bytes. CALG_RC4 = 40 bits key or 88 salt bytes
$n = 40/8;
//Let k be the length of the hash value that is represented by the input parameter hBaseData
$k = 16;
//Step 1 Form a 64-byte buffer by repeating the constant 0x36 64 times
$arraya = array_fill(0, 64, 0x36);
//Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value
for ($i=0; $i<$k; $i++){
$arraya[$i] = $arraya[$i] ^ $hashKey[$i];
}
//Hash the result of step 1 by using the same hash algorithm as hBaseData
$arrayPacka = pack('c*', ...$arraya);
$hashArraya = md5($arrayPacka);
//Put the hash string back into the array
$hashKeyArraya = str_split($hashArraya,2);
$count = count($hashKeyArraya);
$hashKeyInta = array();
for ($i=0; $i<$count; $i++){
$hashKeyInta[$i] = hexdec($hashKeyArraya[$i]);
}
//Step 2 Form a 64-byte buffer by repeating the constant 0x5C 64 times.
$arrayb = array_fill(0, 64, 0x5C);
//Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value
for ($i=0; $i<$k; $i++){
$arrayb[$i] = $arrayb[$i] ^ $hashKey[$i];
}
//Hash the result of step 2 by using the same hash algorithm as hBaseData
$arrayPackb = pack('c*', ...$arrayb);
$hashArrayb = md5($arrayPackb);
//Put the hash string back into the array
$hashKeyArrayb = str_split($hashArrayb,2);
$count = count($hashKeyArrayb);
$hashKeyIntb = array();
for ($i=0; $i<$count; $i++){
$hashKeyIntb[$i] = hexdec($hashKeyArrayb[$i]);
}
//Concatenate the result of step 3 with the result of step 4.
$combined = array_merge($hashKeyInta, $hashKeyIntb);
//Use the first n bytes of the result of step 5 as the derived key.
$finalKey = array();
for ($i=0; $i <$n; $i++){
$finalKey[$i] = $combined[$i];
}
$key = $finalKey;
return $key;
}
PHP解密函数
function decryptRC4($encrypted, $key){
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
$cypher = ‘rc4-40’;
$decrypted = openssl_decrypt($encrypted, $cypher, $key, $opts);
return $decrypted;
}
下面是大问题:
有没有人能够在另一个系统上用 RC4 成功复制 ::CryptDeriveKey?
有谁知道我们创建的 PHP 脚本缺少什么来阻止它创建相同的密钥并使用 openssl_decrypt 解密 Windows CryptoAPI 加密文件?
我们在哪里以及如何创建 40 位密钥所需的 88 位零盐?
接受此密钥并解密由 ::CryptDeriveKey 生成的内容的正确 openssl_decrypt 参数是什么?
是的,我们知道这不安全,也不会用于密码或 PII。我们想摆脱这种陈旧且不安全的方法,但我们需要采取这个临时步骤,首先将原始加密转换为 PHP,以便与现有部署的系统进行互操作。任何帮助或指导将不胜感激。
以防万一其他人沿着这条路走下去这里是上述所有问题的答案。
您可以使用 openssl 在 PHP 上复制 ::CryptDeriveKey,但必须先在 windows 端满足一些先决条件。
CryptDeriveKey 必须设置为 CRYPT_NO_SALT,如下所示:
::CrypeDeriveKey(hCryptProv, CALG_RC4, hSaveHash, CRYPT_NO_SALT, &hCryptKey)
这将允许您从哈希创建一个密钥,并在 PHP 中生成一个匹配的密钥,该密钥将在 openssl 上运行。如果您不设置任何盐参数,您将获得一个密钥,该密钥是使用未知的专有盐算法创建的,该算法无法在其他系统上匹配。
您必须设置 CRYPT_NO_SALT 的原因是因为 CryptAPI 和 openssl 都有专有的 salt 算法并且无法使它们匹配。所以你应该分开腌制。这里有关于此盐值功能的更多详细信息:https://docs.microsoft.com/en-us/windows/desktop/SecCrypto/salt-value-functionality
下面是 PHP 脚本创建等效密钥以供 openssl 使用所需的内容。
<?php
$random = pack('c*', 87,194,...........);
$origSecret = 'ASCII STRING OF CHARACTERS AS PASSWORD';
//Need conversion to match format of Windows CString or wchar_t*
//Windows will probably be UTF-16LE and LAMP will be UTF-8
$secret = iconv('UTF-8','UTF-16LE', $origSecret);
//Create hash key from Random and Secret
//This is basically a hash and salt process.
$hash = hash_init("md5");
hash_update($hash, $random);
hash_update($hash, $secret);
$key = hash_final($hash);
$key = cryptoDeriveKey($key);
//Convert the key hex array to a hex string for openssl_decrypt
$count = count($key);
$maxchars = 2;
for ($i=0; $i<$count; $i++){
$key .= str_pad(dechex($key[$i]), $maxchars, "0", STR_PAD_LEFT);
}
重要提示:OpenSSL 期望密钥是从散列派生的原始十六进制值,不幸的是 openssl_decrypt() 需要与字符串或密码相同的值。因此,此时您必须进行十六进制到字符串的转换。关于为什么必须这样做,这里有一篇很棒的文章。 http://php.net/manual/en/function.openssl-encrypt.php
$opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
//Convert key hex string to a string for openssl_decrypt
//Leave it as it is for openssl command line.
$key = hexToStr($key);
$cipher = 'rc4-40';
$encrypted = “the data you want to encrypt or decrypt”;
$decrypted = openssl_decrypt($encrypted, $cipher, $key, $opts);
echo $decrypted; //This is the final information you’re looking for
function cryptoDeriveKey($key){
//convert the key into hex byte array as int
$hashKey1 = str_split($key,2);
$count = count($hashKey1);
$hashKeyInt = array();
for ($i=0; $i<$count; $i++){
$hashKeyInt[$i] = hexdec($hashKey1[$i]);
}
$hashKey = $hashKeyInt;
//Let n be the required derived key length, in bytes. CALG_RC4 = 40 bits key with 88 salt bits
$n = 40/8;
//Chop the key down to the first 40 bits or 5 bytes.
$finalKey = array();
for ($i=0; $i <$n; $i++){
$finalKey[$i] = $hashKey[$i];
}
return $finalKey;
}
function hexToStr($hex){
$string='';
for ($i=0; $i < strlen($hex)-1; $i+=2){
$string .= chr(hexdec($hex[$i].$hex[$i+1]));
}
return $string;
}
?>
如果您在使用上述代码后无法获得正确的值,您可以尝试从 CryptoAPI 导出您的密钥值并使用 openssl 命令行对其进行测试。
首先,您必须设置 CryptDeriveKey 以允许使用 CRYPT_EXPORTABLE 和 CRYPT_NO_SALT
导出密钥::CrypeDeriveKey(hCryptProv, CALG_RC4, hSaveHash, CRYPT_EXPORTABLE | CRYPT_NO_SALT, &hCryptKey)
如果您想知道如何从导出的密钥中显示 PLAINTEXTKEYBLOB,请遵循此 link。 https://docs.microsoft.com/en-us/windows/desktop/seccrypto/example-c-program--importing-a-plaintext-key
这是一个导出密钥 blob 的示例 0x08 0x02 0x00 0x00 0x01 0x68 0x00 0x00 0x05 0x00 0x00 0x00 0xAA 0xBB 0xCC 0xDD 0xEE
0x08 0x02 0x00 0x00 0x01 0x68 0x00 0x00 //BLOB头几乎完全匹配 0x05 0x00 0x00 0x00 //以字节为单位的密钥长度是正确的 5 个字节 0xAA 0xBB 0xCC 0xDD 0xEE //我们创建的哈希键的前 5 个字节!!
在下面的 openssl enc 命令中使用从 BLOB 导出的键值作为十六进制键值。
openssl enc -d -rc4-40 -in testFile-NO_SALT-enc.txt -out testFile-NO_SALT-dec.txt -K "Hex Key Value" -nosalt -nopad
这将解密在 Windows 机器上使用 CryptEncrypt 加密的文件。
如您所见,当您将 CryptDeriveKey 设置为 CRYPT_NO_SALT 时,您需要的 openssl 密码或密钥是 CryptHashData 密码的第一个“keylength”位。说起来很简单,但真正做到却很痛苦。祝你好运,希望这能帮助其他人解决遗留 Windows 翻译问题。