Что не так с этим простым кодом, предназначенным для дешифрования 256-битным ключом AES с помощью Windows CryptoAPI? - PullRequest
2 голосов
/ 10 июня 2019

У меня есть небольшая программа, которая шифрует файлы с помощью ключа AES-256. Ключ, используемый для шифрования файла, генерируется случайным образом.

Программа шифрования выглядит следующим образом:

  • Получить криптографический контекст - CryptAcquireContext
  • сгенерируйте ключ AES-256, используя CryptGenKey
  • Зашифровать файл с помощью этого ключа - CryptEncrypt
  • Освободить критоконтекст - CryptReleaseContext

Зашифрованный файл представляет собой небольшой test.txt файл, содержащий строку: «просто тест». Итак, необработанные шестнадцатеричные байты в файле:

6A 75 73 74 20 61 20 74 65 73 74

Ключ AES-256, используемый для шифрования в шестнадцатеричном формате:

3f10e23bb1a5dfd9c8ca06195e43043386a9ba4c63c35ac518f463ba768f001b

Зашифрованный файл test.enc имеет байты:

C8 B5 92 51 22 53 75 A1 34 80 EC AA 37 1C 6C BE 

Вопрос:

Как мне написать программу на c / c ++ для расшифровки этих байтов с помощью шестнадцатеричного ключа AES-256 с помощью функции CryptDecrypt Windows CryptoAPI?

Что я пробовал:

Я написал следующую программу дешифрования (небольшое изменение здесь .)

#include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
#pragma comment(lib, "crypt32.lib")

#define BLOCK_LEN 128

HCRYPTPROV hCryptProv;

int wmain(int argc, wchar_t* argv[])
{
    wchar_t default_key[] = L"PxDiO7Gl39nIygYZXkMEM4apukxjw1rFGPRjunaPABs";
    wchar_t* key_str = default_key;
    size_t len = lstrlenW(key_str);



    if (!CryptAcquireContext(
        &hCryptProv,
        NULL,
        MS_ENH_RSA_AES_PROV,
        PROV_RSA_AES,
        NULL))
    {
        /*std::cout << "error acquiring context\n";
        std::cout << GetLastErrorAsString();*/
        exit(1);
    }

    HCRYPTKEY hKey;


    wchar_t* filename = argv[1];
    wchar_t* filename2 = argv[2];

    printf("Key: %S\n", key_str);
    printf("Key len: %#x\n", len);
    printf("Input File: %S\n", filename);
    printf("Output File: %S\n", filename2);
    printf("----\n");

    HANDLE hInpFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hInpFile == INVALID_HANDLE_VALUE) {
        printf("Cannot open input file!\n");
        system("pause");
        return (-1);
    }
    printf("\nEncrypted file read.");

    HANDLE hOutFile = CreateFileW(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hOutFile == INVALID_HANDLE_VALUE) {
        printf("Cannot open output file!\n");
        system("pause");
        return (-1);
    }
    printf("\nDecryption file created.");

    DWORD dwStatus = 0;
    BOOL bResult = FALSE;
    wchar_t info[] = L"Microsoft Enhanced RSA and AES Cryptographic Provider";

    /*BOOL CryptDeriveKey(
        HCRYPTPROV hProv,
        ALG_ID     Algid,
        HCRYPTHASH hBaseData,
        DWORD      dwFlags,
        HCRYPTKEY * phKey
    );*/

    HCRYPTHASH hHash;
    if (!CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hHash)) {
        dwStatus = GetLastError();
        printf("CryptCreateHash failed: %x\n", dwStatus);
        CryptReleaseContext(hCryptProv, 0);
        system("pause");
        return dwStatus;
    }

    if (!CryptHashData(hHash, (BYTE*)key_str, len, 0)) {
        DWORD err = GetLastError();
        printf("CryptHashData Failed : %#x\n", err);
        system("pause");
        return (-1);
    }
    printf("[+] CryptHashData Success\n");

    if (!CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey)) {
        dwStatus = GetLastError();
        printf("CryptDeriveKey failed: %x\n", dwStatus);
        CryptReleaseContext(hCryptProv, 0);
        system("pause");
        return dwStatus;
    }
    printf("[+] CryptDeriveKey Success\n");


    const size_t chunk_size = BLOCK_LEN;
    BYTE chunk[chunk_size] = { 0 };
    DWORD out_len = 0;

    BOOL isFinal = FALSE;
    DWORD readTotalSize = 0;

    DWORD inputSize = GetFileSize(hInpFile, NULL);

    while (bResult = ReadFile(hInpFile, chunk, chunk_size, &out_len, NULL)) {
        if (0 == out_len) {
            break;
        }
        printf("\nFile read.");
        readTotalSize += out_len;
        if (readTotalSize == inputSize) {
            isFinal = TRUE;
            printf("\nFinal chunk set.\n");
        }

        printf("\n Now calling decryption routine...");
        if (!CryptDecrypt(hKey, NULL, isFinal, 0, chunk, &out_len)) {
            printf("[-] CryptDecrypt failed\n");
            break;
        }
        printf("CryptDecrypt succeeded.");

        DWORD written = 0;
        if (!WriteFile(hOutFile, chunk, out_len, &written, NULL)) {
            printf("writing failed!\n");
            break;
        }
        memset(chunk, 0, chunk_size);

    }
    CryptReleaseContext(hCryptProv, 0);
    CryptDestroyKey(hKey);
    CryptDestroyHash(hHash);

    CloseHandle(hInpFile);
    CloseHandle(hOutFile);
    printf("Finished. Processed %#x bytes.\n", readTotalSize);
    system("pause");
    return 0;
}

Это просто заканчивается тем, что CryptDecrypt не удалось. Я предполагаю, что ключ не был указан в правильном формате. Я не знаю, как использовать мой ключ AES-256 в шестнадцатеричном формате для расшифровки данных. В настоящее время ключ жестко запрограммирован в формате base64 в программе, но я предполагаю, что это неправильно.

Еще одна вещь, которую я сделал, это то, что я использовал инструмент CryptoTester , чтобы указать ключ AES, который у меня есть в шестнадцатеричном формате, и он действительно может успешно расшифровать файл. Кроме того, этот онлайн-инструмент дешифрования также может использовать ключ для дешифрования данных , как показано здесь . Итак, я знаю, что у меня есть правильный шестнадцатеричный ключ и все, и что файл можно расшифровать, но как мне переписать мою программу выше, чтобы правильно расшифровать файл?

Обратите внимание, что все ключи, используемые или показанные здесь, являются только примерами.

Как мне исправить эту программу, чтобы фактически успешно расшифровать данные, используя ключ AES-256 выше?

1 Ответ

2 голосов
/ 10 июня 2019

Простая демонстрационная программа

Вот небольшая программа на C, которая использует ваш ключ и зашифрованные данные, поставляемые с ним, и снова расшифровывает исходный текст.Я попытался сделать его минималистичным.

Ради простоты он не считывает файлы из файловой системы, но определяет данные в программе на C как шестнадцатеричные строки.

Результат

При запуске программы на консоль выводятся следующие выходные данные:

decrypted result: 'just a test'

Код C

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>

void error(const char* what) {
    fprintf(stderr, "%s failed with last error 0x%x\n", what, GetLastError());
    exit(1);
}

#define AES_KEY_SIZE 32
typedef struct {
    BLOBHEADER hdr;
    DWORD dwKeySize;
    BYTE rgbKeyData[AES_KEY_SIZE];
} AES256KEYBLOB;

BYTE *hex2byte(const char *hex) {
    int len = strlen(hex) / 2;
    BYTE* bytes = malloc(len);
    if (bytes == NULL) { 
        error("malloc");  
        return NULL; 
    }
    unsigned char val[2];

    for (int i = 0; i < len; i++) {
        sscanf_s(&hex[i * 2], "%2hhx", &val);
        bytes[i] = val[0];
    }
    return bytes;
}

int main() {
    BYTE *key = hex2byte("3F10E23BB1A5DFD9C8CA06195E43043386A9BA4C63C35AC518F463BA768F001B");

    AES256KEYBLOB aes256KeyBlob;
    aes256KeyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    aes256KeyBlob.hdr.bVersion = CUR_BLOB_VERSION;
    aes256KeyBlob.hdr.reserved = 0;
    aes256KeyBlob.hdr.aiKeyAlg = CALG_AES_256;
    aes256KeyBlob.dwKeySize = AES_KEY_SIZE;
    memcpy(aes256KeyBlob.rgbKeyData, key, AES_KEY_SIZE);

    HCRYPTPROV hProv;
    if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
        error("CryptAcquireContext");
    }

    HCRYPTKEY hKey;
    if (!CryptImportKey(hProv, (BYTE*)& aes256KeyBlob, sizeof(AES256KEYBLOB), 0, CRYPT_EXPORTABLE, &hKey)) {
        CryptReleaseContext(hProv, 0);
        error("CryptImportKey");
    }

    const char *encodedHex = "C8B59251225375A13480ECAA371C6CBE";
    DWORD numBytes = strlen(encodedHex) / 2;
    BYTE *encoded = hex2byte(encodedHex);

    if (CryptDecrypt(hKey, 0, TRUE, 0, encoded, &numBytes)) {
        printf("decrypted result: '");
        for (DWORD i = 0; i < numBytes; i++) {
            printf("%c", encoded[i]);
        }
        printf("'\n");
    } else {
        CryptDestroyKey(hKey);
        CryptReleaseContext(hProv, 0);
        error("CryptDecrypt");
    }


    free(key);
    free(encoded);

    CryptDestroyKey(hKey); 
    CryptReleaseContext(hProv, 0);
    return 0;
}

Документы Microsoft

Структура KEYBLOB задокументирована здесь: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/jj650836(v%3Dvs.85)

, тогда как здесь вы найдете информацию о структуре BLOBHEADER: https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/ns-wincrypt-publickeystruc

...