我有一个小程序,它使用AES-256密钥加密文件。用于加密文件的密钥是随机生成的。
加密程序如下:
CryptAcquireContext
CryptGenKey
生成AES-256密钥CryptEncrypt
CryptReleaseContext
加密的文件是一个小的test.txt
文件,其中包含字符串:“ just a test”。因此,文件中的原始十六进制字节为:
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 ++程序来使用Windows CryptoAPI的CryptDecrypt
函数使用十六进制AES-256密钥解密这些字节?
我尝试过的事情:
我编写了以下解密程序(对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
失败了。因此,我猜测密钥没有以正确的格式指定。我不知道如何使用十六进制格式的AES-256密钥解密数据。该密钥当前在程序中以base64
格式进行了硬编码,但我猜这是不正确的。
我要做的另一件事是,我使用CryptoTester tool指定了十六进制格式的AES密钥,它实际上能够成功解密该文件。而且,此在线解密工具还能够使用密钥来解密数据as shown here。因此,我知道我拥有正确的十六进制密钥以及所有内容,并且文件可以解密,但是如何在上面重写程序以正确解密文件?
请注意,此处使用或显示的所有键只是示例。
如何纠正该程序以使用上面的AES-256密钥成功地成功解密数据?
答案 0 :(得分:2)
简单演示程序
这是一个小型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