Downloading data via WinInet - c++

And so there is a code, that can download data with size not higher than 1024*100 bytes. Code brought from https://rsdn.org/article/inet/inetapi.xml.
As far as I understand, InternetReadFile after every call should move on the read characters count, or it's sensless, because it'll return the same data. I red, that there is a function,that moves reading start pointer. Have I to use it?
HINTERNET hInternetSession;
HINTERNET hURL;
char cBuffer[1024*100]; // I'm only going to access 1K of info.
BOOL bResult;
DWORD dwBytesRead;
// Make internet connection.
hInternetSession = InternetOpen(
L"tes", // agent
INTERNET_OPEN_TYPE_PRECONFIG, // access
NULL, NULL, 0); // defaults
// Make connection to desired page.
hURL = InternetOpenUrl(
hInternetSession, // session handle
L"https://www.google.com.ua/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", // URL to access
NULL, 0, 0, 0); // defaults
// Read page into memory buffer.
while(bResult = InternetReadFile(
hURL, // handle to URL
(LPSTR)cBuffer, // pointer to buffer
(DWORD)1024 * 100, // size of buffer
&dwBytesRead)==TRUE&&dwBytesRead>0) // pointer to var to hold return value
// Close down connections.
InternetCloseHandle(hURL);
InternetCloseHandle(hInternetSession);
DWORD dwTemp;
HANDLE hFile = CreateFile(L"googlelogo_color_272x92dp.png", GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return 0;
}
WriteFile(hFile, cBuffer, sizeof(cBuffer), &dwTemp, NULL);
Issue: I can't read more than 1024*1024 bytes, program crashes, when creates char[1024*1024]

Here is a complete program. Thanks to #RbMm
#include <windows.h>
#include <wininet.h>
#pragma comment(lib,"wininet")
int main(int argc, char* argv[])
{
HINTERNET hInternetSession;
HINTERNET hURL;
// I'm only going to access 1K of info.
BOOL bResult;
DWORD dwBytesRead=1;
// Make internet connection.
hInternetSession = InternetOpen(
L"tes", // agent
INTERNET_OPEN_TYPE_PRECONFIG, // access
NULL, NULL, 0); // defaults
// Make connection to desired page.
hURL = InternetOpenUrl(
hInternetSession, // session handle
L"http://wallpapers-images.ru/1920x1080/nature/wallpapers/wallpapers-nature-1.jpg", // URL to access
NULL, 0, 0, 0); // defaults
// Read page into memory buffer.
char buf[1024];
DWORD dwTemp;
HANDLE hFile = CreateFile(L"пример.jpg", GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) {
return 0;
}
for (;dwBytesRead>0;)
{
InternetReadFile(hURL, buf, (DWORD)sizeof(buf), &dwBytesRead);
WriteFile(hFile, buf, dwBytesRead, &dwTemp, NULL);
}
// Close down connections.
InternetCloseHandle(hURL);
InternetCloseHandle(hInternetSession);
CloseHandle(hFile);
return 0;
}

Related

Download image from HTTP request triggering a breakpoint

I am trying to download an image onto the user's desktop from a URL using Win32. I have taken care of all the HTTP request stuff and know for a fact that it is all working well. When I go to call CreateFile() the Visual Studios debugger just says "Exception: Application.exe has triggered a breakpoint" and that it will resume on the CreateFile() line. Also there is an error code "Critical error detected c0000374"
Here is my code:
VARIANT varResponse;
VariantInit(&varResponse);
...
hr = pIWinHttpRequest->get_ResponseBody(&varResponse);
...
if (SUCCEEDED(hr)) {
long upperBounds;
long lowerBounds;
unsigned char* buff;
//Make sure that varResponse is an array of unsigned bytes
if (varResponse.vt == (VT_ARRAY | VT_UI1)) {
long Dims = SafeArrayGetDim(varResponse.parray);
//It should only have one dimension
if (Dims == 1) {
//Get Array lower and upper bounds
SafeArrayGetLBound(varResponse.parray, 1, &lowerBounds);
SafeArrayGetUBound(varResponse.parray, 1, &upperBounds);
upperBounds++;
SafeArrayAccessData(varResponse.parray, (void**)&buff);
HANDLE hFile;
DWORD dwBytesWritten;
PWSTR filepath[MAX_PATH];
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &*filepath);
if (SUCCEEDED(hr)) {
//PathCombine(filepathForImage, filepathToDesktop, L"\\todaysDailyImage.jpg");
PathAppend(*filepath, L"todaysDailyImage.jpg");
MessageBox(NULL, *filepath, L"Check if filepath works", MB_OK);
}
hFile = CreateFile(*filepath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
//File failed
}
else {
WriteFile(hFile, buff, upperBounds - lowerBounds, &dwBytesWritten, NULL);
//File was written
}
CloseHandle(hFile);
CoTaskMemFree(filepath);
SafeArrayUnaccessData(varResponse.parray);
MessageBox(NULL, L"Everything was cleaned up", L"Update:", MB_OK);
}
}
}
Am I doing anything wrong?
The way you are using filepath is all wrong.
You are declaring it as an array of MAX_PATH (260) number of PWSTR pointers.
When you refer to an array by its name alone, you end up with a pointer to the 1st element of the array. So, &*filepath is the same as &*(&filepath[0]), which is effectively &filepath[0]. And *filepath is the same as *(&filepath[0]), which is effectively filepath[0]. So, as far as SHGetKnownFolderPath() and MessageBox() are concerned, they are only operating on the 1st PWSTR pointer in the array, and the other 259 array elements are ignored. That part is ok, but wasteful.
However, PathAppend() requires a destination buffer that is an array of MAX_PATH number of WCHAR elements. You are appending to the WCHAR[] array that SHGetKnownFolderPath() allocates as its output, which is not large enough to hold the filename you are trying to append to it. So, you are triggering errors because you are trying to modify memory that hasn’t been allocated to hold that modification.
You don’t need the PWSTR array at all. Try something more like this instead:
PWSTR folderpath;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &folderpath);
if (FAILED(hr)) {
// ...
}
else {
PWSTR filepath;
hr = PathAllocCombine(folderpath, L"todaysDailyImage.jpg", 0, &filepath);
if (FAIlED(hr)) {
// ...
}
else {
MessageBoxW(NULL, filepath, L"Check if filepath works", MB_OK);
hFile = CreateFileW(filepath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
//File failed
}
else {
WriteFile(hFile, buff, upperBounds - lowerBounds, &dwBytesWritten, NULL);
//File was written
CloseHandle(hFile);
}
LocalFree(filepath);
}
CoTaskMemFree(folderpath);
}

Unable to obtain POST request response using wininet

I need to make a POST request to an API to get some XML data (http://freecite.library.brown.edu/welcome/api_instructions). This works fine with curl:
curl -H "Accept: application/xml" --data "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536. " http://freecite.library.brown.edu:80/citations/create
So I am trying to do a similar thingy using Win32 SDK. This is my code:
void LoadData()
{
wil::unique_hinternet hInternet(InternetOpen(L"Dummy", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0));
wil::unique_hinternet hConnect(InternetConnect(hInternet.get(), L"http://freecite.library.brown.edu", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0));
wil::unique_hinternet hRequest(HttpOpenRequest(hConnect.get(), L"POST", L"/citations/create", NULL, NULL, NULL, NULL, NULL));
wstring data = L"citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536.";
PCWSTR szHeaders = L"Accept: application/xml";
HttpSendRequest(hRequest.get(), szHeaders, 0, (LPVOID)data.c_str(), static_cast<int>(data.length()));
DWORD availableBytes = 0;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
PBYTE outputBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, availableBytes);
PBYTE nextBytes = outputBuffer;
DWORD bytesUsed = 0; // number of used bytes.
while (availableBytes)
{
DWORD downloadedBytes;
InternetReadFile(hRequest.get(), nextBytes, availableBytes, &downloadedBytes);
bytesUsed = bytesUsed + downloadedBytes;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
if (availableBytes > 0)
{
// lazy buffer growth here. Only alloc for what we need. could be optimized if we end up with huge payloads (>10MB).
// otherwise, this is good enough.
outputBuffer = (PBYTE)HeapReAlloc(GetProcessHeap(), 0, outputBuffer, bytesUsed + availableBytes);
nextBytes = outputBuffer + bytesUsed; // careful, pointer might have moved! Update cursor.
}
}
// Convert outputed XML to wide char
int size_needed = MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, NULL, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, &wstrTo[0], size_needed);
wstring res = wstrTo;
}
The problem is, before entering the for loop, even after the call to InternetQueryDataAvailable, availableBytes comes out to be 0. As a result, I finally end up getting a blank string as response, whereas I was expecting a XML response.
Can anyone point me what am I doing wrongly, and how to fix it?
InternetConnect expects server name or IP address, so don't include "http://" in the address. Change to:
InternetConnect(handle, L"freecite.library.brown.edu"...);
Use UTF-8 for data. Other parameters for WinAPI functions are correctly using UTF-16, they automatically make the necessary conversions.
Change the header:
std::wstring szHeaders = L"Content-Type: application/x-www-form-urlencoded\r\n";
accept should be sent through HttpOpenRequest
const wchar_t *accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", L"/citations/create",
NULL, NULL, accept, 0, 0);
Note, if you don't specify accept (use NULL in its place) then the result can be in plain html.
The example below should return XML.
Note, for simplicity I put optional as ANSI string, but it should be UTF8, then you convert it to UTF16 woptional and send it. result will be UTF8 string, it should be converted to UTF16 for Windows's display.
#include <iostream>
#include <string>
#include <Windows.h>
#include <WinINet.h>
#pragma comment(lib, "wininet.lib")//include WinINet library
int main()
{
std::string result;
std::wstring server = L"freecite.library.brown.edu";
std::wstring objectname = L"/citations/create"; //file in this case!
std::wstring header = L"Content-Type: application/x-www-form-urlencoded\r\n";
std::string optional = "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein";
HINTERNET hsession = InternetOpen(L"appname",
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hconnect = InternetConnect(hsession, server.c_str(),
INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
const wchar_t* accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", objectname.c_str(),
NULL, NULL, accept, 0, 0);
if(HttpSendRequest(hrequest, header.c_str(), header.size(),
&optional[0], optional.size()))
{
DWORD blocksize = 4096;
DWORD received = 0;
std::string block(blocksize, 0);
while (InternetReadFile(hrequest, &block[0], blocksize, &received)
&& received)
{
block.resize(received);
result += block;
}
std::cout << result << std::endl;
}
if (hrequest) InternetCloseHandle(hrequest);
if (hconnect) InternetCloseHandle(hconnect);
if (hsession) InternetCloseHandle(hsession);
return 0;
}

DeviceIoControl() with IOCTL_DISK_GET_DRIVE_GEOMETRY is failing and returning error code 87. Why?

Relevant code is as follows:
std::wstring path = ApplicationData::Current->LocalFolder->Path->Data();
std::wstring testFileName = path + std::wstring(L"\\TestVariablySized");
this->hMappedFile = CreateFile2(
testFileName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_ALWAYS,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
(HANDLE)hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
After this, controlCheck is false and check is ERROR_INVALID_PARAMETER. checkF is ERROR_ALREADY_EXISTS, which shouldn't be a problem here.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation.
, but clearly I'm missing something. Your help is most appreciated.
Edit:
Per responses received, I altered things to be as follows:
HANDLE hDevice = CreateFile2(
L"\\.\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
hDevice, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
CloseHandle(hDevice);
Which should be closer to being correct, even if it's not quite correct yet. checkF is ERROR_FILE_NOT_FOUND, which I found strange. I tried "\\.\PhysicalDrive1" and "\\.\PhysicalDrive2" as well, but receive the same result. controlCheck is still false, but check is now ERROR_INVALID_HANDLE.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation
Actually, you are not, because you did not pay attention to this tidbit of the documentation:
hDevice
A handle to the disk device from which the geometry is to be retrieved. To retrieve a device handle, call the CreateFile function.
You are not passing a handle to a disk device, you are passing a handle to a filesystem path instead.
When calling CreateFile2() to get a handle to a disk device, you need to specify a physical device in \\.\PhysicalDriveX format instead, not a filesystem path.
Also, as the CreateFile2() documentation says:
The following requirements must be met for such a call to succeed:
The caller must have administrative privileges. For more information, see Running with Special Privileges.
The dwCreationDisposition parameter must have the OPEN_EXISTING flag.
When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITE flag.
You are using OPEN_ALWAYS instead of OPEN_EXISTING.
Please read the "Physical Disks and Volumes" section of the CreateFile2() documentation more carefully.
Try something more like this instead:
std::wstring path = L"\\\\.\\PhysicalDrive0";
DWORD errCode;
hMappedFile = CreateFile2(
path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_EXISTING,
NULL);
if (this->hMappedFile == INVALID_HANDLE_VALUE)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
DISK_GEOMETRY geo = { 0 };
DWORD dwReturned = 0;
bool controlCheck = DeviceIoControl(
hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
&geo, // output buffer
sizeof(geo), // size of output buffer
&dwReturned, // number of bytes returned
NULL);
if (!controlCheck)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
// use drive as needed...
}
CloseHandle(hMappedFile);
}

write file in raw usb c++

this is my code:
int main(int argc, CHAR* argv[]) {
using namespace std;
PVOID data[1024];
DWORD dwBytesRead = 0;
DWORD dwBytesWrite = 512;
HANDLE hFile = CreateFile(L"\\\\.\\E:", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);//open usb
if (hFile == INVALID_HANDLE_VALUE) {
printf("Error %x", GetLastError());
return 1;
}
printf ("created usb hendle\n");
LARGE_INTEGER a = { 50688 };
SetFilePointerEx(hFile, a,NULL,0); //set the pointer to c600
printf("got usb pointer set\n");
PVOID ToBe = ":) hello this is our file -> ";
if (WriteFile(hFile,ToBe,512 ,&dwBytesWrite, NULL) == 0)
{
printf("writeFile error: %x", GetLastError());
CloseHandle(hFile);
return 1;
}
printf("write the first string in isb\n");
HANDLE aFile = CreateFile(L"C:\\Users\\h7080y_dxlq\\Downloads\\Video\\88250.mp4", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); //open the file handle
printf("created mp4 hendle\n");
if (aFile == INVALID_HANDLE_VALUE) {
printf("Error %x", GetLastError());
return 1;
}
if (ReadFile(aFile, &data, 512, &dwBytesRead, NULL) == 0) {
printf("ReadFile error: %x", GetLastError());
return 1;
}
DWORD dwPos;
printf("checked for read errors in mp4 passed o.k.\n");
while (ReadFile(aFile, data,512, &dwBytesRead, NULL) && dwBytesRead > 0) //read file
{
dwPos = SetFilePointerEx(hFile, a, NULL, 0);
LockFile(hFile, dwPos, 0, dwBytesRead, 0);
WriteFile(hFile, data, 512, &dwBytesWrite, NULL); // write 512 bit chunk at the time to usb
UnlockFile(hFile, dwPos, 0, dwBytesRead, 0);
a = { 50688+512 }; // promot
}
printf("write all mp4 to the usb directtly\n");
ToBe = "<- this is the end of file , see you soon :)";
if (WriteFile(hFile, ToBe, 512, &dwBytesWrite, NULL) == 0)
{
printf("writeFile error: %x", GetLastError());
CloseHandle(hFile);
return 1;
}
printf("after end massage \n");
CloseHandle(hFile);
system("pause");
return 0;
}
I try to take a file (mp4 in this case) , and read it chunk by chunk (512 bit at the time) , take the chunk and write it to usb and so on till end of file .
Now, the problem is:
A the loop never ends.
B that it don't write the file to the USB, it looks like its write on the same spot again and again...
How can I fix it?
LARGE_INTEGER a = { 50688 };
while (ReadFile(aFile, data,512, &dwBytesRead, NULL) && dwBytesRead > 0)
{
dwPos = SetFilePointerEx(hFile, a, NULL, 0);
LockFile(hFile, dwPos, 0, dwBytesRead, 0);
WriteFile(hFile, data, 512, &dwBytesWrite, NULL);
UnlockFile(hFile, dwPos, 0, dwBytesRead, 0);
a = { 50688+512 };
}
The first time round the loop you set the file pointer to 50688 and write there. Each subsequent time round the loop you set the file pointer to 50688+512 and write there.
It looks like it writes to the same spot again and again.
Yes indeed. That's exactly what your code specifies. Your should set the file pointer on aFile outside the loop, and let it advance naturally as the file is written. Something like this:
dwPos = 50688;
LARGE_INTEGER a = { dwPos };
if (!SetFilePointerEx(hFile, a, NULL, 0))
{
// handle error
}
while (ReadFile(aFile, data, 512, &dwBytesRead, NULL) && dwBytesRead > 0)
{
LockFile(hFile, dwPos, 0, dwBytesRead, 0);
WriteFile(hFile, data, 512, &dwBytesWrite, NULL);
UnlockFile(hFile, dwPos, 0, dwBytesRead, 0);
dwPos += 512;
}
Note that your calls to LockFile, and the use of a DWORD for dwPos, means that you cannot write a file larger than 4GB.
It is also far from clear to me that the calls to LockFile are needed. Since your original code got the handling of dwPos wrong, it's clear that you weren't locking the parts of the file you intended to. It is my belief that you should simply remove them. In which case the code will become:
LARGE_INTEGER a = { 50688 };
if (!SetFilePointerEx(hFile, a, NULL, 0))
{
// handle error
}
while (ReadFile(aFile, data, 512, &dwBytesRead, NULL) && dwBytesRead > 0)
{
if (!WriteFile(hFile, data, 512, &dwBytesWrite, NULL))
{
// handle error
}
}
You have also omitted large amounts of error checking in this code. I would not be surprised to find that there are a number of other problems with it. I don't particularly want to try to find every single error in your code, and hope that what I have written is enough to help you on your way.

Digital signature with CryptVerifySignature

I want to implement digital signature app using CryptVerifySignature. I've written this code(with help of MSDN example):
#define Check(condition, message) if (!(condition)) { fprintf(stderr, "%s\n", message); goto Exit; };
void DigSign(const char* inputFileName, const char* signFileName)
{
HCRYPTPROV hProv;
BYTE *pbBuffer= NULL;
DWORD dwBufferLen = 0;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
HCRYPTKEY hPubKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
FILE* inputFile = NULL;
Check((inputFile = fopen(inputFileName, "r")) != NULL, "File does not exists");
dwBufferLen = GetFileSize(inputFile);
Check(pbBuffer = (BYTE*)malloc(dwBufferLen + 1), "cannot allocate memory");
fread(pbBuffer, 1, dwBufferLen, inputFile);
pbBuffer[dwBufferLen] = '\0';
fclose(inputFile);
//-------------------------------------------------------------------
// Acquire a cryptographic provider context handle.
Check(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0), "Error during CryptAcquireContext.");
if(!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey))
{
if(NTE_NO_KEY == GetLastError())
{
Check(CryptGenKey(hProv, AT_SIGNATURE, CRYPT_EXPORTABLE, &hKey), "Could not create a user public key.\n");
}
else
{
goto Exit;
}
}
Check(CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwBlobLen), "Error computing BLOB length.");
Check(pbKeyBlob = (BYTE*)malloc(dwBlobLen), "Out of memory. \n");
Check(CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbKeyBlob, &dwBlobLen), "Error during CryptExportKey.");
//-------------------------------------------------------------------
// Create the hash object.
Check(CryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash), "Error during CryptCreateHash.");
//-------------------------------------------------------------------
// Compute the cryptographic hash of the buffer.
Check(CryptHashData(hHash, pbBuffer, dwBufferLen, 0), "Error during CryptHashData.");
//-------------------------------------------------------------------
// Determine the size of the signature and allocate memory.
dwSigLen= 0;
Check(CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSigLen), "Error during CryptSignHash.");
//-------------------------------------------------------------------
// Allocate memory for the signature buffer.
Check(pbSignature = (BYTE *)malloc(dwSigLen), "Out of memory.");
//-------------------------------------------------------------------
// Sign the hash object.
Check(CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen), "Error during CryptSignHash.");
FILE* f = fopen(signFileName, "w");
fwrite(pbSignature, dwSigLen, 1, f);
printf("W: %.128s\n", pbSignature);
fwrite(pbKeyBlob, dwBlobLen, 1, f);
printf("W: %.148s\n", pbKeyBlob);
fclose(f);
//-------------------------------------------------------------------
// Destroy the hash object.
if(hHash)
CryptDestroyHash(hHash);
free(pbSignature);
free(pbKeyBlob);
if(hProv)
CryptReleaseContext(hProv, 0);
Exit:;
}
bool CheckDigSign(const char* inputFileName, const char* signFileName, const char* userName)
{
bool result = false;
HCRYPTPROV hProv;
BYTE *pbBuffer= (BYTE *)"The data that is to be hashed and signed.";
DWORD dwBufferLen = strlen((char *)pbBuffer)+1;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
HCRYPTKEY hPubKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
FILE* inputFile = NULL;
Check((inputFile = fopen(inputFileName, "r")) != NULL, "File does not exists");
dwBufferLen = GetFileSize(inputFile);
Check(pbBuffer = (BYTE*)malloc(dwBufferLen + 1), "cannot allocate memory");
fread(pbBuffer, 1, dwBufferLen, inputFile);
pbBuffer[dwBufferLen] = '\0';
fclose(inputFile);
FILE* signFile = NULL;
Check((signFile = fopen(signFileName, "r")) != NULL, "File does not exists");
DWORD dwSignFileLen = GetFileSize(signFile);
dwSigLen = 128;
pbSignature = (BYTE*)malloc(dwSigLen);
dwBlobLen = dwSignFileLen - dwSigLen;
pbKeyBlob = (BYTE*)malloc(dwBlobLen);
fread(pbSignature, 1, dwSigLen, signFile);
fread(pbKeyBlob, 1, dwBlobLen, signFile);
fclose(signFile);
printf("R: %.128s\n", pbSignature);
printf("R: %.148s\n", pbKeyBlob);
Check(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0), "Error during CryptAcquireContext.");
Check(CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hPubKey), "Public key import failed.");
//-------------------------------------------------------------------
// Create a new hash object.
Check(CryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash), "Error during CryptCreateHash.");
//-------------------------------------------------------------------
// Compute the cryptographic hash of the buffer.
Check(CryptHashData(hHash, pbBuffer, dwBufferLen, 0), "Error during CryptHashData.");
//-------------------------------------------------------------------
// Validate the digital signature.
result = CryptVerifySignature(hHash, pbSignature, dwSigLen, hPubKey, NULL, 0);
printf("%u %x", GetLastError(), GetLastError());
//-------------------------------------------------------------------
// Free memory to be used to store signature.
if(pbSignature)
free(pbSignature);
//-------------------------------------------------------------------
// Destroy the hash object.
if(hHash)
CryptDestroyHash(hHash);
//-------------------------------------------------------------------
// Release the provider handle.
if(hProv)
CryptReleaseContext(hProv, 0);
Exit:
return result;
}
int _tmain(int argc, _TCHAR* argv[])
{
DigSign("test3.txt", "test.sig");
printf("TEST: %u\n", CheckDigSign("test3.txt", "test.sig", ""));
}
DigSign function must sign content of file and write signature and public key to another file. CheckSign must return true if sign is right. But I don't understand why my code doesn't work. CheckDigSign in _tmain must return true, but it returns false. Can anybody help me pls?
I took your entire code sample, hacked it up a little, and used CreateFile, ReadFile, and WriteFile for all the file I/O. It works. The signature file with the appended public key checked against the source file just fine.
I therefore suspect it is the method of reading/writing your files, and specifically, the "w" and "r" vs. "wb" and "rb" that is horking over your bytes. Try changing those and see what you come up with.
For reference, the modified code is below, and there is NO error checking in the changes I made, so DON'T use this for anything special as it is literally worth less than the paper its printed on (i.e. nothing).
#define Check(condition, message) if (!(condition)) { fprintf(stderr, "%s\n", message); goto Exit; };
void DigSign(const char* inputFileName, const char* signFileName)
{
HCRYPTPROV hProv;
BYTE *pbBuffer= NULL;
DWORD dwBufferLen = 0;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
FILE* inputFile = NULL;
HANDLE hFileInput = CreateFile(inputFileName, // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
dwBufferLen = GetFileSize(hFileInput, NULL);
Check(pbBuffer = (BYTE*)malloc(dwBufferLen + 1), "cannot allocate memory");
DWORD dwBytesRead = 0L;
ReadFile(hFileInput, pbBuffer, dwBufferLen, &dwBytesRead, NULL);
pbBuffer[dwBufferLen] = 0;
CloseHandle(hFileInput);
//-------------------------------------------------------------------
// Acquire a cryptographic provider context handle.
Check(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0), "Error during CryptAcquireContext.");
if(!CryptGetUserKey(hProv, AT_SIGNATURE, &hKey))
{
if(NTE_NO_KEY == GetLastError())
{
Check(CryptGenKey(hProv, AT_SIGNATURE, CRYPT_EXPORTABLE, &hKey), "Could not create a user public key.\n");
}
else
{
goto Exit;
}
}
Check(CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwBlobLen), "Error computing BLOB length.");
Check(pbKeyBlob = (BYTE*)malloc(dwBlobLen), "Out of memory. \n");
Check(CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbKeyBlob, &dwBlobLen), "Error during CryptExportKey.");
//-------------------------------------------------------------------
// Create the hash object.
Check(CryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash), "Error during CryptCreateHash.");
//-------------------------------------------------------------------
// Compute the cryptographic hash of the buffer.
Check(CryptHashData(hHash, pbBuffer, dwBufferLen, 0), "Error during CryptHashData.");
//-------------------------------------------------------------------
// Determine the size of the signature and allocate memory.
dwSigLen= 0;
Check(CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSigLen), "Error during CryptSignHash.");
//-------------------------------------------------------------------
// Allocate memory for the signature buffer.
Check(pbSignature = (BYTE *)malloc(dwSigLen), "Out of memory.");
//-------------------------------------------------------------------
// Sign the hash object.
Check(CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen), "Error during CryptSignHash.");
HANDLE hFileSign = CreateFile(signFileName, // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
DWORD dwBytesWritten = 0;
WriteFile(hFileSign, pbSignature, dwSigLen, &dwBytesWritten, NULL);
WriteFile(hFileSign, pbKeyBlob, dwBlobLen, &dwBytesWritten, NULL);
CloseHandle(hFileSign);
printf("W: %.128s\n", pbSignature);
printf("W: %.148s\n", pbKeyBlob);
//-------------------------------------------------------------------
// Destroy the hash object.
if(hHash)
CryptDestroyHash(hHash);
free(pbSignature);
free(pbKeyBlob);
if(hProv)
CryptReleaseContext(hProv, 0);
Exit:;
}
bool CheckDigSign(const char* inputFileName, const char* signFileName, const char* userName)
{
BOOL result = false;
HCRYPTPROV hProv;
BYTE *pbBuffer= (BYTE *)"The data that is to be hashed and signed.";
DWORD dwBufferLen = strlen((char *)pbBuffer)+1;
HCRYPTHASH hHash;
HCRYPTKEY hPubKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
HANDLE hFileInput = CreateFile(inputFileName, // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
dwBufferLen = GetFileSize(hFileInput, NULL);
Check(pbBuffer = (BYTE*)malloc(dwBufferLen + 1), "cannot allocate memory");
DWORD dwBytesRead = 0;
ReadFile(hFileInput, pbBuffer, dwBufferLen, &dwBytesRead, NULL);
pbBuffer[dwBufferLen] = 0;
CloseHandle(hFileInput);
HANDLE hFileSig = CreateFile(signFileName, // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
DWORD dwSignFileLen = GetFileSize(hFileSig, NULL);
dwSigLen = 128;
pbSignature = (BYTE*)malloc(dwSigLen);
dwBlobLen = dwSignFileLen - dwSigLen;
pbKeyBlob = (BYTE*)malloc(dwBlobLen);
ReadFile(hFileSig, pbSignature, dwSigLen, &dwBytesRead, NULL);
ReadFile(hFileSig, pbKeyBlob, dwBlobLen, &dwBytesRead, NULL);
CloseHandle(hFileSig);
printf("R: %.128s\n", pbSignature);
printf("R: %.148s\n", pbKeyBlob);
Check(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0), "Error during CryptAcquireContext.");
Check(CryptImportKey(hProv, pbKeyBlob, dwBlobLen, 0, 0, &hPubKey), "Public key import failed.");
//-------------------------------------------------------------------
// Create a new hash object.
Check(CryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash), "Error during CryptCreateHash.");
//-------------------------------------------------------------------
// Compute the cryptographic hash of the buffer.
Check(CryptHashData(hHash, pbBuffer, dwBufferLen, 0), "Error during CryptHashData.");
//-------------------------------------------------------------------
// Validate the digital signature.
result = CryptVerifySignature(hHash, pbSignature, dwSigLen, hPubKey, NULL, 0);
printf("%u %x", GetLastError(), GetLastError());
//-------------------------------------------------------------------
// Free memory to be used to store signature.
if(pbSignature)
free(pbSignature);
//-------------------------------------------------------------------
// Destroy the hash object.
if(hHash)
CryptDestroyHash(hHash);
//-------------------------------------------------------------------
// Release the provider handle.
if(hProv)
CryptReleaseContext(hProv, 0);
Exit:
return !!result;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc == 3)
{
DigSign(argv[1], argv[2]);
printf("TEST: %u\n", CheckDigSign(argv[1], argv[2],""));
return 0;
}
return 1;
}