Im trying to decrypt a .png file but for some reson CryptDecrypt returns Bad Data error. I'm new to C and Cryptography so it would be awesome if someone could tell me why is it happaning and how to fix it. Also I used CryptEncrypt function to encrypt the image and it worked well.
Here is a descryption from microsoft documentation of NTE_BAD_DATA error:
The data to be decrypted is not valid. For example, when a block cipher is used and the Final flag is FALSE, the value specified by pdwDataLen must be a multiple of the block size. This error can also be returned when the padding is found to be not valid.
Link to the CryptDecrypt function docs:
https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptdecrypt
Here is full code:
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
void displayErrorMessage(DWORD errorCode) {
LPSTR messageBuffer = NULL;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&messageBuffer,
0,
NULL);
if (messageBuffer != NULL) {
printf("Error: %s\n", messageBuffer);
LocalFree(messageBuffer);
}
else {
printf("Error: Unable to get error message for code %d\n", errorCode);
}
}
void decryptFile(const char* filePath, const char* password) {
HANDLE hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Error opening file \n");
return;
}
printf("[+] Succesfully opened the file\n");
HCRYPTPROV hCryptProv;
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
printf("Error acquiring crypto context\n");
CloseHandle(hFile);
return;
}
printf("[+] Succesfully acquired crypto context\n");
HCRYPTHASH hHash;
if (!CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hHash)) {
printf("Error creating hash\n");
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully created hash: %d\n", hHash);
if (!CryptHashData(hHash, (const BYTE*)password, strlen(password), 0)) {
printf("Error hashing data\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully hashed the key: %s\n", password);
HCRYPTKEY hKey;
if (!CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey)) {
printf("Error deriving key\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully derived AES key: %s\n", hKey);
DWORD dwFileSize = GetFileSize(hFile, NULL);
BYTE* pBuffer = (BYTE*)malloc(dwFileSize);
DWORD dwBytesRead = 0;
if (!ReadFile(hFile, pBuffer, dwFileSize, &dwBytesRead, NULL)) {
printf("Error reading file\n");
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully saved encrypted file to a buffer: %s\n", pBuffer);
if (!CryptDecrypt(hKey, 0, TRUE, 0, pBuffer, &dwBytesRead)) {
printf("Error Decrypting data \n");
displayErrorMessage(GetLastError());
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
DWORD dwBytesWritten = 0;
if (!WriteFile(hFile, pBuffer, dwBytesRead, &dwBytesWritten, NULL)) {
printf("Error writing decrypted data to file\n");
}
else {
printf("File decrypted successfully\n");
}
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
}
int main() {
const char* filePath = "C:/Users/mjank/Desktop/a.png";
const char* password = "mysecretkey";
decryptFile(filePath, password);
return 0;
}
As people below suggested, here is the encryption code:
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
void encryptFile(const char* filePath, const char* password) {
HANDLE hFile = CreateFileA(filePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Error opening file %d\n", GetLastError());
return;
}
HCRYPTPROV hCryptProv;
if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
printf("Error acquiring crypto context\n");
CloseHandle(hFile);
return;
}
HCRYPTHASH hHash;
if (!CryptCreateHash(hCryptProv, CALG_SHA_256, 0, 0, &hHash)) {
printf("Error creating hash\n");
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
if (!CryptHashData(hHash, (const BYTE*)password, strlen(password), 0)) {
printf("Error hashing data\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
HCRYPTKEY hKey;
if (!CryptDeriveKey(hCryptProv, CALG_AES_256, hHash, 0, &hKey)) {
printf("Error deriving key\n");
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
DWORD dwFileSize = GetFileSize(hFile, NULL);
BYTE* pBuffer = (BYTE*)malloc(dwFileSize);
DWORD dwBytesRead = 0;
if (!ReadFile(hFile, pBuffer, dwFileSize, &dwBytesRead, NULL)) {
printf("Error reading file\n");
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully saved plaintext file to a buffer: %s\n", pBuffer);
DWORD dwBlockLen = 0;
if (!CryptEncrypt(hKey, NULL, TRUE, 0, pBuffer, &dwBlockLen, dwFileSize)) {
printf("Error encrypting data %d\n", GetLastError());
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
return;
}
printf("[+] Succesfully encrypted the buffer: %s\n", pBuffer);
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
DWORD dwBytesWritten = 0;
if (!WriteFile(hFile, pBuffer, dwBytesRead, &dwBytesWritten, NULL)) {
printf("Error writing encrypted data to file\n");
}
else {
printf("File encrypted successfully\n");
}
free(pBuffer);
CryptDestroyKey(hKey);
CryptDestroyHash(hHash);
CryptReleaseContext(hCryptProv, 0);
CloseHandle(hFile);
}
int main() {
const char* filePath = "C:/Users/mjank/Desktop/a.png";
const char* key = "mysecretkey";
encryptFile(filePath, key);
return 0;
}
SetEndOfFile()
is required to let the system know where valid data ends.ReadFile
API really is nice proza. Glad I don't have to deal with that API. Trying to find the various failing modes must require a day of research or something. Closing as without the encryption function this exercise is hopeless. Note that a wrong password would also likely result in this error (the non-password hash function will never fail after all).SetEndOfFile()
function mentioned above should be called. Apart from that, decryption works on my machine with your code (testing with a 5098 bytes .png file, encrypted with the SHA256 hash of your password, a Zero-IV and AES-256/CBC/PKCS#7), even without removing the padding bytes. Add the encryption code. Also consider writing the decrypted data to a separate file (at least for troubleshooting).