当前位置: 首页 > 知识库问答 >
问题:

使用Windows CryptoAPI使用AES 256位密钥解密的简单代码有什么问题?

水渊
2023-03-14

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

加密程序如下所示:

  • 获取加密上下文-CryptAcquireContext

加密的文件是一个小的测试。txt包含字符串的文件:“只是一个测试”。因此,文件中的原始十六进制字节是:

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

用于十六进制格式加密的AES-256密钥为:

3f10e23bb1a5dfd9c8ca06195e43043386a9ba4c63c35ac518f463ba768f001b

加密文件测试。enc则具有以下字节:

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

问题:

如何编写c/c程序,使用Windows CryptoAPI的cryptoDecrypt函数,使用十六进制AES-256密钥对这些字节进行解密?

我所尝试过的:

我已经编写了以下解密程序(对这里的要点稍加修改)

#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;
}

结果告诉我密码解密失败了。所以我猜密钥的格式不正确。我不知道如何使用十六进制格式的AES-256密钥解密数据。该密钥目前在程序中以base64格式硬编码,但我猜这是不正确的。

我所做的另一件事是,我使用CryptoTester工具指定了十六进制格式的AES密钥,它实际上能够成功地解密文件。此外,此在线解密工具还可以使用密钥对数据进行解密,如图所示。所以我知道我有正确的十六进制密钥和所有东西,文件可以解密,但是我如何重写上面的程序来正确解密文件呢?

请注意,这里使用或显示的所有键只是示例。

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


共有1个答案

晋天逸
2023-03-14

简单的演示程序

这是一个小型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;
}

微软文档

这里记录了KEYBLOB结构:https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/jj650836(v=vs.85)

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

 类似资料:
  • 下面是我的Bean类 当我运行代码时,我得到了下面的错误,字段passwordEncoder在com.naveen.controller.UserController需要一个类型为'PasswordEncoder'的beancom.naveen.entity.找不到。注入点有以下注释:-@org.springframework.beans.factory.annotation.自动加载(必需=tr

  • 问题内容: 我这样在Node.js中加密了一个字符串。 我注意到nodejs中的缓冲区就像十六进制,但每2个连续字符都成对出现。因此,如果我将其转换为十六进制,则长度只有一半。 例: 缓冲: 十六进制: 现在,我在aes256中使用的密钥的长度不能为64。这里,缓冲区的长度为32,十六进制的长度为64。 我想在golang中解密此密码,我将不得不使用此密钥和iv对其进行解密。 golang中的ae

  • 假设我有一个IP地址,192.168.1.1 我想让我的程序基于这个IP地址创建一个随机的单字字符串,它可以很容易地解密,而无需密钥或密码或额外的安全措施。 例如。 我进入192.168.1.1 程序将其转换为AzlQrEHCSD或其他一些随机字符串 我在程序中输入这个字符串 它被转换回192.168.1.1 有没有简单的算法可以在不生成密钥或其他密码的情况下做到这一点?我知道密钥和密码是加密和解

  • 我正在寻找比ROT13更复杂的东西,但它不需要库(最好甚至不需要单元,只需要一个插入函数)。 我想使用用户提供的密码对称加密/解密给定的字符串。但是,结果必须是一个字符串,从某种意义上说,我必须能够将其存储在 .INI 文件。 有没有人有一个简单的函数可以做到这一点(delphi XE2)?谷歌今天不是我的朋友。 先谢谢了 [Update]/[Bounty]只是为了澄清(如果最初不是这样,请重复)

  • 我遇到了许多API,它们为用户提供了一个API密钥和一个秘密。但我的问题是:两者之间有什么区别? 在我看来,一把钥匙就足够了。假设我有钥匙,只有我和服务器知道。我用这个键创建了一个HMAC哈希,并进行了一个API调用。在服务器上,我们再次创建HMAC哈希,并将其与发送的哈希进行比较。如果是相同的,则呼叫经过身份验证。 那为什么要用两把钥匙呢? 编辑:或者该API密钥用于查找API机密?

  • 这是可能的还是加密必须共享和使用相同的密钥? 主要目的就是这样。 我将有两个客户端可以发送和接收加密数据到彼此。