1

我正在尝试使用 wincrypt 解密一个文件,但我似乎无法正确解密此函数。字节使用 C# 中的 RC2 实现进行加密,我为加密和解密过程提供相同的密码和 IV(在 C# 中加密,在 c++ 中解密)。

在最后的“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 创建

4

2 回答 2

3

首先,正如我的评论,使用 GetLastError() 这样你就知道失败了。我假设您得到 NTE_BAD_DATA,所有其他错误更容易处理,因为它们基本上意味着您错过了 API 调用序列中的某些步骤。

CryptDecrypt 因 NTE_BAD_DATA 失败的典型原因是您正在解密块密码的最后一个块(就像您一样)并且解密的填充字节不正确。如果输入被截断(并非所有加密字节都保存到文件中)或密钥不正确,则可能会发生这种情况。

我建议您有条不紊地进行此操作,因为有很多地方可能会失败,并且只会在 CryptDecrypt 时间出现:

  1. 确保您在 C# 中加密的文件可以在 C# 中解密。这将消除任何文件保存截断问题。
  2. 首先尝试使用固定的硬编码密钥进行加密和解密(不派生密码),这将确保您的密钥集代码 IV 初始化正确(以及填充模式和密码链模式)。
  3. 确保密码派生过程到达相同的哈希值。诸如 ANSI 与 Unicode 或终端 0 之类的东西可能会对 MD5 散列造成严重破坏,并导致与显然相同的密码散列完全不同的密钥。
于 2009-10-09T23:22:33.183 回答
1

有些人在操作系统之间移动时发现了问题。CryptDeriveKey 调用使用基于操作系统和所选算法的“默认密钥长度”。对于 RC2,默认生成的密钥长度在 Windows 2000 上为 40 位,在 Windows 2003 上为 128 位。当在 CryptDecrypt 调用中使用生成的密钥时,这会导致“错误数据”返回码。

据推测,这与尝试应用 128 位密钥解密 40 位加密流后出现在最终缓冲区末尾的“垃圾”有关。错误代码通常表示错误的填充字节 - 但根本原因可能是密钥生成问题。

要生成 40 位加密密钥,请在 CryptDeriveKey 调用的标志字段中使用 ( ( 40 <<16 ) )。

于 2011-01-05T23:31:53.440 回答