c - 这个旨在使用 Windows CryptoAPI 使用 AES 256 位 key 进行解密的简单代码有什么问题?

标签 c encryption cryptography aes cryptoapi

我有一个小程序,它使用 AES-256 key 加密文件。用于加密文件的 key 是随机生成的。

加密程序是这样的:

  • 获取加密上下文 - CryptAcquireContext
  • 使用 CryptGenKey 生成 AES-256 key
  • 使用此 key 加密文件 -- CryptEncrypt
  • 释放加密上下文 -- CryptReleaseContext

加密的文件是一个小test.txt文件,其中包含字符串:“just a test”。因此文件中的原始十六进制字节是:

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

用于十六进制格式加密的 AES-256 key 是:

3f10e23bb1a5dfd9c8ca06195e43043386a9ba4c63c35ac518f463ba768f001b

加密文件 test.enc 的字节如下:

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

问题:

如何编写 C/C++ 程序来使用 Windows CryptoAPI 的 CryptDecrypt 函数使用十六进制 AES-256 key 解密这些字节?

我尝试过的:

我编写了以下解密程序(对gist here稍作修改。)

#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失败了。所以我猜测 key 没有以正确的格式指定。我不知道如何使用十六进制格式的 AES-256 key 来解密数据。该 key 当前在程序中以 base64 格式进行硬编码,但我猜这是不正确的。

我所做的另一件事是我使用了 CryptoTester tool指定我拥有的十六进制格式的 AES key ,它实际上能够成功解密该文件。另外,这个在线解密工具还可以使用 key 来解密数据as shown here 。所以我知道我拥有正确的十六进制 key 和所有内容,并且该文件可以解密,但是如何重写上面的程序以正确解密该文件?

请注意,此处使用或显示的所有键都只是示例。

如何更正此程序以使用上面的 AES-256 key 成功解密数据?

最佳答案

简单的演示程序

这是一个小型 C 程序,它使用您的 key 及其提供的加密数据并再次解密原始文本。我试图让它变得简约。

为了简单起见,它没有从文件系统中读取文件,而是将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;
}

微软文档

KEYBLOB 结构记录如下: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/jj650836(v%3Dvs.85)

在这里您可以找到有关 BLOBHEADER 结构的信息: https://learn.microsoft.com/en-us/windows/desktop/api/wincrypt/ns-wincrypt-publickeystruc

关于c - 这个旨在使用 Windows CryptoAPI 使用 AES 256 位 key 进行解密的简单代码有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56530195/

相关文章:

c - 将文本文件读入c中的数组

c - 数组函数只返回第一个元素,我需要整个数组。请帮助

java - java填充错误中的AES解密

c - 通过 getauxval 检测 Power8 核心加密?

ios - 使用现有公钥的 RSA 加密

security - 如何编写利用英特尔 IPT 硬件的应用程序?

c - 如何在C中序列化数据

计算文本文件中单词出现的次数

c# - 将 Salt 作为明文存储在包含密文的文件中

javascript - 如何安全地存储服务器端使用的密码?