Wincrypt:无法解密用 C# 加密的文件。 CryptDecrypt 的 NTE_BAD_DATA
我正在尝试使用 wincrypt 解密文件的一部分,但似乎无法使该函数正确解密。这些字节使用 C# 中的 RC2 实现进行加密,并且我向加密和解密过程提供相同的密码和 IV(在 C# 中加密,在 C++ 中解密)。
我一路上的所有函数都返回 true,直到最终的“CryptDecrypt”函数。我不用再打字了,这里是函数:
static char* DecryptMyFile(char *input, char *password, int size)
{
HCRYPTPROV provider = NULL;
if(CryptAcquireContext(&provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{printf("Context acquired.");}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
{
if(CryptAcquireContext(&provider, 0, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{printf("new key made.");}
else
{
printf("Could not acquire context.");
}
}
else
{printf("Could not acquire context.");}
}
HCRYPTKEY key = NULL;
HCRYPTHASH hash = NULL;
if(CryptCreateHash(provider, CALG_MD5, 0, 0, &hash))
{printf("empty hash created.");}
else
{printf("could not create hash.");}
if(CryptHashData(hash, (BYTE *)password, strlen(password), 0))
{printf("data buffer is added to hash.");}
else
{printf("error. could not add data buffer to hash.");}
if(CryptDeriveKey(provider, CALG_RC2, hash, 0, &key))
{printf("key derived.");}
else
{printf("Could not derive key.");}
DWORD dwKeyLength = 128;
if(CryptSetKeyParam(key, KP_EFFECTIVE_KEYLEN, reinterpret_cast<BYTE*>(&dwKeyLength), 0))
{printf("success");}
else
{printf("failed.");}
BYTE IV[8] = {0,0,0,0,0,0,0,0};
if(CryptSetKeyParam(key, KP_IV, IV, 0))
{printf("worked");}
else
{printf("faileD");}
DWORD dwCount = size;
BYTE *decrypted = new BYTE[dwCount + 1];
memcpy(decrypted, input, dwCount);
decrypted[dwCount] = 0;
if(CryptDecrypt(key,0, true, 0, decrypted, &dwCount))
{printf("succeeded");}
else
{printf("failed");}
return (char *)decrypted;
}
输入是传递给函数的数据,经过加密。密码与 C# 中用于加密数据的密码相同。 size 是加密时数据的大小。
所有上述函数在 CryptDecrypt 之前都返回 true,我似乎无法弄清楚为什么。同时,我不确定 CryptDecrypt 函数如何编辑我的“解密”变量,因为我没有传递它的引用。
任何关于为什么这不起作用的帮助或建议将不胜感激。这是我第一次尝试使用 wincrypt,也是多年来第一次使用 C++。
如果还有更多帮助的话,这是我的加密(在 C# 中):
public static byte[] EncryptString(byte[] input, string password)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
//using an empty initialization vector for convenience.
byte[] IV = new byte[8];
ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
我已经确认我在 C++ 中的哈希值与我在 C# 中的密钥相同,由 PasswordDeriveBytes.CryptDeriveKey 创建
I am trying to decrypt a piece of a file with wincrypt and I cannot seem to make this function decrypt correctly. The bytes are encrypted with the RC2 implementation in C# and I am supplying the same password and IV to both the encryption and decryption process (encrypted in C#, decrypted in c++).
All of my functions along the way are returning true until the final "CryptDecrypt" function. Instead of me typing out any more, here is the function:
static char* DecryptMyFile(char *input, char *password, int size)
{
HCRYPTPROV provider = NULL;
if(CryptAcquireContext(&provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
{printf("Context acquired.");}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
{
if(CryptAcquireContext(&provider, 0, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{printf("new key made.");}
else
{
printf("Could not acquire context.");
}
}
else
{printf("Could not acquire context.");}
}
HCRYPTKEY key = NULL;
HCRYPTHASH hash = NULL;
if(CryptCreateHash(provider, CALG_MD5, 0, 0, &hash))
{printf("empty hash created.");}
else
{printf("could not create hash.");}
if(CryptHashData(hash, (BYTE *)password, strlen(password), 0))
{printf("data buffer is added to hash.");}
else
{printf("error. could not add data buffer to hash.");}
if(CryptDeriveKey(provider, CALG_RC2, hash, 0, &key))
{printf("key derived.");}
else
{printf("Could not derive key.");}
DWORD dwKeyLength = 128;
if(CryptSetKeyParam(key, KP_EFFECTIVE_KEYLEN, reinterpret_cast<BYTE*>(&dwKeyLength), 0))
{printf("success");}
else
{printf("failed.");}
BYTE IV[8] = {0,0,0,0,0,0,0,0};
if(CryptSetKeyParam(key, KP_IV, IV, 0))
{printf("worked");}
else
{printf("faileD");}
DWORD dwCount = size;
BYTE *decrypted = new BYTE[dwCount + 1];
memcpy(decrypted, input, dwCount);
decrypted[dwCount] = 0;
if(CryptDecrypt(key,0, true, 0, decrypted, &dwCount))
{printf("succeeded");}
else
{printf("failed");}
return (char *)decrypted;
}
input is the data passed to the function, encrypted. password is the same password used to encrypt the data in C#. size is the size of the data while encrypted.
All of the above functions return true until CryptDecrypt, which I cannot seem to figure out why. At the same time, I'm not sure how the CryptDecrypt function would possibly edit my "decrypted" variable, since I am not passing a reference of it.
Any help or advice onto why this is not working would be greatly appreciated. This is my first endeavour with wincrypt and first time using C++ in years.
If it is of any more help, as well, this is my encryption (in C#):
public static byte[] EncryptString(byte[] input, string password)
{
PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null);
byte[] ivZeros = new byte[8];
byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros);
RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider();
//using an empty initialization vector for convenience.
byte[] IV = new byte[8];
ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
csEncrypt.Write(input, 0, input.Length);
csEncrypt.FlushFinalBlock();
return msEncrypt.ToArray();
}
I have confirmed that my hash value in C++ is identical to my key in C#, created by PasswordDeriveBytes.CryptDeriveKey
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,正如我的评论中所述,使用 GetLastError() 以便您知道失败的原因。我假设您得到了 NTE_BAD_DATA,所有其他错误都更容易处理,因为它们基本上意味着您错过了 API 调用序列中的某些步骤。
CryptDecrypt 因 NTE_BAD_DATA 失败的典型原因是您正在解密块密码的最后一个块(就像您一样)并且解密的填充字节不正确。如果输入被截断(并非所有加密字节都保存到文件中)或者密钥不正确,则可能会发生这种情况。
我建议您有条不紊地采取这一点,因为有很多地方可能会失败,这些都只会在 CryptDecrypt 时显现出来:
First, as in my comment, use GetLastError() so you know what it failed. I'll assume that you get NTE_BAD_DATA, all the other errors are much more easier to deal with since they basically mean you missed some step int he API call sequence.
The typical reason why CryptDecrypt would fail with NTE_BAD_DATA would be that you're decrypting the last block of a block cypher (as you are) and the decrypted padding bytes are incorrect. This can happen if the input is truncated (not all encrypted bytes were saved to the file) or if the key is incorrect.
I would suggest you take this methodically since there are so many places where this can fail that will all manifest only at CryptDecrypt time:
有些人在操作系统之间移动时发现了问题。
CryptDeriveKey 调用使用基于所选操作系统和算法的“默认密钥长度”。对于 RC2,默认生成的密钥长度在 Windows 2000 上为 40 位,在 Windows 2003 上为 128 位。当在 CryptDecrypt 调用中使用生成的密钥时,这会导致“BAD DATA”返回代码。
据推测,这与尝试应用 128 位密钥解密 40 位加密流后最终缓冲区末尾出现的“垃圾”有关。错误代码通常表示填充字节错误 - 但根本原因可能是密钥生成问题。
要生成 40 位加密密钥,请在 CryptDeriveKey 调用的标志字段中使用 ( ( 40 << 16 ) )。
Some people have discovered issues when moving between operating systems.
The CryptDeriveKey call uses a "default key length" based on the operating system and algorithm chosen. For RC2, the default generated key length is 40 bits on Windows 2000 and 128 bits on Windows 2003. This results in a "BAD DATA" return code when the generated key is used in a CryptDecrypt call.
Presumably this is related to "garbage" appearing at the end of the final buffer after trying to apply a 128 bit key to decrypt a 40 bit encrypted stream. The error code typically indicates bad padding bytes - but the root cause may be a key generation issue.
To generate a 40 bit encryption key, use ( ( 40 <<16 ) ) in the flags field of the CryptDeriveKey call.