Wincrypt:无法解密在C#中加密的文件。 CryptDecrypt的NTE_BAD_DATA

时间:2009-10-09 22:57:14

标签: c++ cryptography encryption cryptoapi

我正在尝试使用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;
}

input是传递给函数的数据,经过加密。 password与用于加密C#中的数据的密码相同。 size是加密时的数据大小 所有上述函数都返回true,直到CryptDecrypt,我似乎无法弄清楚为什么。同时,我不确定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创建

2 个答案:

答案 0 :(得分:3)

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

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

我建议你有条不紊地采用这个方法,因为有很多地方可能会失败,只会在CryptDecrypt时间显现:

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

答案 1 :(得分:1)

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

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

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