Wincrypt: невозможно расшифровать файл, который был зашифрован в C #. NTE_BAD_DATA в CryptDecrypt - PullRequest
1 голос
/ 10 октября 2009

Я пытаюсь расшифровать часть файла с помощью wincrypt, и я не могу заставить эту функцию дешифровать правильно. Байты зашифрованы с помощью реализации RC2 в C #, и я предоставляю один и тот же пароль и 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 #. Размер - это размер данных в зашифрованном виде.
Все вышеперечисленные функции возвращают 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 ]

3 голосов
/ 10 октября 2009

Во-первых, как в моем комментарии, используйте GetLastError (), чтобы вы знали , что не удалось. Я предполагаю, что вы получите NTE_BAD_DATA, со всеми остальными ошибками справиться гораздо проще, поскольку они в основном означают, что вы пропустили какой-то шаг в последовательности вызовов API.

Типичная причина, по которой CryptDecrypt не работает с NTE_BAD_DATA, заключается в том, что вы дешифруете последний блок блочного шифра (как вы), а дешифрованные байты заполнения являются неверными. Это может произойти, если ввод обрезан (не все зашифрованные байты были сохранены в файл) или если ключ неверен.

Я бы посоветовал вам принять это методично, поскольку существует множество мест, где это может не получиться, и все они проявятся только во время CryptDecrypt:

  1. Убедитесь, что файл, который вы шифруете в C #, может быть расшифрован в C #. Это устранит любые проблемы с усечением сохранения файла.
  2. Попробуйте сначала зашифровать и расшифровать с помощью фиксированного жестко закодированного ключа (пароль не получен), это обеспечит правильную инициализацию кода IV набора ключей (а также режим заполнения и режим шифрования).
  3. Убедитесь, что процесс создания пароля проходит с тем же хешем. Такие вещи, как ANSI против Unicode или терминала 0, могут привести к хаосу в хеше MD5 и привести к сильно отличающимся ключам от явно одного и того же хэша пароля.
1 голос
/ 06 января 2011

Некоторые люди обнаружили проблемы при перемещении между операционными системами. В вызове CryptDeriveKey используется «длина ключа по умолчанию» в зависимости от выбранной операционной системы и алгоритма. Для RC2 длина сгенерированного ключа по умолчанию составляет 40 бит в Windows 2000 и 128 бит в Windows 2003. Это приводит к коду возврата «ПЛОХИЕ ДАННЫЕ», когда созданный ключ используется в вызове CryptDecrypt.

Предположительно, это связано с "мусором", появляющимся в конце последнего буфера после попытки применить 128-битный ключ для расшифровки 40-битного зашифрованного потока. Код ошибки обычно указывает на неправильные байты заполнения, но основной причиной может быть проблема генерации ключа.

Чтобы сгенерировать 40-битный ключ шифрования, используйте ((40 << 16)) в поле флагов вызова CryptDeriveKey. </p>

...