print digital signature infos - c++

As in the title specified, I'd like to print the digital signature information out to the console. Here is the code I wrote:
bool CheckDigSig(const std::wstring& filepath)
{
bool rval = false;
DWORD dwEncoding = 0;
DWORD dwContentType = 0;
DWORD dwFormatType = 0;
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
// Get message handle and store handle from the signed file.
BOOL fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
filepath.c_str(),
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
return false;
DWORD singer_info_size = 0;
// Get signer information size.
fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &singer_info_size);
if (!fResult)
{
CryptMsgClose(hMsg);
CertCloseStore(hStore, 0);
return false;
}
// Allocate memory for signer information.
std::vector<byte> signer_info_data(singer_info_size);
PCMSG_SIGNER_INFO pSignerInfo = reinterpret_cast<PCMSG_SIGNER_INFO>(signer_info_data.data());
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &singer_info_size);
if (fResult)
{
//pSignerInfo->Issuer;
//pSignerInfo->SerialNumber;
}
CryptMsgClose(hMsg);
CertCloseStore(hStore, 0);
return rval;
}
I would like to print those two variables at the end (which is now commendted):
pSignerInfo->Issuer;
pSignerInfo->SerialNumber;
I've got no idea how could I make it readable format, like a string, byte or char array. Could you help me with it?

This article http://support.microsoft.com/kb/323809 has the code you need. Here's a short snippet of it:
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
// ...
There's more code in there, obviously, covering all the various corners of this task. Including printing the SerialNumber as well

Related

How to retrieve information from multiple/dual code signatures on an executable file

I've been using the following code (taken from KB323809 article) to retrieve information about the code signature on the executable file. This works fine for a single digital signature.
But how to retrieve information for multiple code signatures?
In that case the Microsoft code below simply retrives info only for the first signature.
My thought was to call CryptMsgGetParam with CMSG_SIGNER_COUNT_PARAM to get the number of signatures and then pass each signature index to the subsequent call to CryptMsgGetParam with CMSG_SIGNER_INFO_PARAM (in the code below.) But this approach always returns 1 signature, even if I clearly have more, like 3 in this example:
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
PCMSG_SIGNER_INFO *pCounterSignerInfo);
int _tmain(int argc, TCHAR *argv[])
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
SYSTEMTIME st;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo <filename>\n"));
return 0;
}
#ifdef UNICODE
lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
{
printf("Unable to convert to unicode.\n");
__leave;
}
#endif
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (ProgPubInfo.lpszProgramName != NULL)
{
wprintf(L"Program Name : %s\n",
ProgPubInfo.lpszProgramName);
}
if (ProgPubInfo.lpszPublisherLink != NULL)
{
wprintf(L"Publisher Link : %s\n",
ProgPubInfo.lpszPublisherLink);
}
if (ProgPubInfo.lpszMoreInfoLink != NULL)
{
wprintf(L"MoreInfo Link : %s\n",
ProgPubInfo.lpszMoreInfoLink);
}
}
_tprintf(_T("\n"));
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(_T("Signer Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Get the timestamp certificate signerinfo structure.
if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
BOOL fReturn = FALSE;
LPTSTR szName = NULL;
DWORD dwData;
__try
{
// Print Serial Number.
_tprintf(_T("Serial Number: "));
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for Issuer name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for issuer name.\n"));
__leave;
}
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// print Issuer name.
_tprintf(_T("Issuer Name: %s\n"), szName);
LocalFree(szName);
szName = NULL;
// Get Subject name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for subject name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for subject name.\n"));
__leave;
}
// Get subject name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Print Subject Name.
_tprintf(_T("Subject Name: %s\n"), szName);
fReturn = TRUE;
}
__finally
{
if (szName != NULL) LocalFree(szName);
}
return fReturn;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
outputString = (LPWSTR)LocalAlloc(LPTR,
(wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info)
{
BOOL fReturn = FALSE;
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
BOOL fResult;
__try
{
// Loop through authenticated attributes and find
// SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for SPC_SP_OPUS_INFO structure.
OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
if (!OpusInfo)
{
_tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
__leave;
}
// Decode and get SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Fill in Program Name if present.
if (OpusInfo->pwszProgramName)
{
Info->lpszProgramName =
AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
Info->lpszProgramName = NULL;
// Fill in Publisher Information if present.
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
Info->lpszPublisherLink = NULL;
break;
}
}
else
{
Info->lpszPublisherLink = NULL;
}
// Fill in More Info if present.
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
Info->lpszMoreInfoLink = NULL;
break;
}
}
else
{
Info->lpszMoreInfoLink = NULL;
}
fReturn = TRUE;
break; // Break from for loop.
} // lstrcmp SPC_SP_OPUS_INFO_OBJID
} // for
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
return fReturn;
}
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{
BOOL fResult;
FILETIME lft, ft;
DWORD dwData;
BOOL fReturn = FALSE;
// Loop through authenticated attributes and find
// szOID_RSA_signingTime OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Decode and get FILETIME structure.
dwData = sizeof(ft);
fResult = CryptDecodeObject(ENCODING,
szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ft,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
break;
}
// Convert to local time.
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, st);
fReturn = TRUE;
break; // Break from for loop.
} //lstrcmp szOID_RSA_signingTime
} // for
return fReturn;
}
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
BOOL fReturn = FALSE;
BOOL fResult;
DWORD dwSize;
__try
{
*pCounterSignerInfo = NULL;
// Loop through unathenticated attributes for
// szOID_RSA_counterSign OID.
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,
szOID_RSA_counterSign) == 0)
{
// Get size of CMSG_SIGNER_INFO structure.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for CMSG_SIGNER_INFO.
*pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
if (!*pCounterSignerInfo)
{
_tprintf(_T("Unable to allocate memory for timestamp info.\n"));
__leave;
}
// Decode and get CMSG_SIGNER_INFO structure
// for timestamp certificate.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
In addition to the answer of Daniel Sie.
Found an attribute (szOID_NESTED_SIGNATURE) that would contain the CMSG_SIGNER_INFO that need to be decoded with following steps (this is Delphi code but sense is clear):
LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);
CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, #LSize);
The acquired CMSG_SIGNER_INFO (LNestedSignerInfo) is the nested signature (in our case SHA2 signature).
To obtain the time-stamp information (RFC3161) of that signature - search the Unauthenticated attribute with pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).
Found attribute would contain the CMSG_SIGNER_INFO of the time-stamp counter signature, that also need to be decoded by previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).
The CERT_CONTEXT of nested signature or timestamp counter signature is need to be searched in store with is obtained from corresponding HCRYPTMSG (result of CryptMsgOpenToDecode).
LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);
LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg);
Example to decode szOID_RFC3161_counterSign attribute:
LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr;
for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do
begin
if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then
begin
LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);
if not Assigned(LNestedTimeStampMsg) then
RaiseLastOSError;
try
if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then
RaiseLastOSError;
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize) then
RaiseLastOSError;
GetMem(LTimeStampSigner, LSize);
try
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, #LSize) then
RaiseLastOSError;
LAttr := LTimeStampSigner.AuthAttrs.rgAttr;
for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do
begin
if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then
begin
LSize := SizeOf(LFileTime);
if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, #LFileTime, #LSize) then
RaiseLastOSError;
if FileTimeToLocalFileTime(#LFileTime, LLocalFileTime)
and FileTimeToSystemTime(#LLocalFileTime, LSystemTime) then
SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)
else
SHA2TimeStamp := 0;
end;
Inc(LAttr);
end;
finally
FreeMem(LTimeStampSigner);
end;
finally
if not CryptMsgClose(LNestedTimeStampMsg) then
RaiseLastOSError;
end;
end;
Inc(LNestedSignerAttr);
end;
Authenticode stores secondary signatures in the UnauthenticatedAttributes of primary signer (index 0), instead of additional PKCS 7 signer.
From the primary signature, search the UnauthenticatedAttribue for below:
//Indicates the attribute is an octet encoded PKCS7
define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
The encoded object of this attribute is a full PKCS 7 signer.
Thanks.

Getting Connection-State from Bluetooth Low Energy Device from Device Manager

i'm developing on a Bluetooth Low Energy Device and i need to see in code if the device is connected or not.
First thing i noticed was that there is in the Devicemanager a Attribute "Verbunden"-> English: Connected and it says true or false if my device is connected or not. So i need to read that Attribute in my program.
What i have tried till now:
Getting all Devices with SetupDiGetClassDevs
Getting the FriendlyName with SetupDiGetDeviceRegistryProperty
Searching for my Device with the name.
That works.
Now i wanted to get that Connected-Attribute but i didn't find out what i have to use at SetupDiGetDeviceRegistryProperty.
SetupDiGetDeviceRegistryProperty is described here https://msdn.microsoft.com/en-us/library/windows/hardware/ff551967(v=vs.85).aspx
Maybe someone knows what is the right value for Property.
My Code:
int get_device_info( void )
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
FILE * devices = fopen("devices.txt", "a+");
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
//SPDRP_DEVICEDESC,
//SPDRP_CAPABILITIES,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
if(buffer)
{
if( strcmp("Name of Device",AnsiString(buffer).c_str())==0)
{
fprintf(devices,"Result:[%s]",AnsiString(buffer).c_str());
if (buffer) LocalFree(buffer);
}
}
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
fclose(devices);
return 0;
}
Instead of using SetupDiEnumDeviceInfo, you would try:
1. using SetupDiEnumDeviceInterfaces
2. using SetupDiGetDeviceInterfaceProperty
3. using SetupDiGetDeviceInterfacePropertyKeys to get a list of all Property Keys available for the interface
4. using SetupDiGetDeviceProperty and/or SetupDiGetDeviceRegistryProperty
Instead of using SPDRP_XXX constants, you would use DEVPROP, as defined in 'devpkey.h' ...
Below are a few examples taken from the log of a test prog I wrote to discover the whole thing:
DEVPROPNAME: DEVPKEY_DeviceInterface_Bluetooth_DeviceAddress
DEVPROPGUID: {2BD67D8B-8BEB-48D5-87E0-6CDA3428040A}
DEVPROPPID: 1
DEVPROPTYPE: DEVPROP_TYPE_STRING
Value: c026df001017
DEVPROPNAME: DEVPKEY_Device_Children
DEVPROPGUID: {4340A6C5-93FA-4706-972C-7B648008A5A7}
DEVPROPPID: 9
DEVPROPTYPE: DEVPROP_TYPE_STRING_LIST
Value:
BTHLEDevice\{00001800-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0001
BTHLEDevice\{00001801-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0008
BTHLEDevice\{00001809-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&000c
BTHLEDevice\{0000180f-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0010
BTHLEDevice\{0000180a-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0014
BTHLEDevice\{00001523-1212-efde-1523-785feabcd123}_c026df001017\8&2fd07168&1&0019
On a second subject, you are 'working' on the 'device' itself ( SetupDiGetClassDevs(&BluetoothInterfaceGUID...) [and then working on the \BTHLE\ tree in Registry].
After listing all GattServices of this device and getting their uuids, you could restart that iteration on the device_guid itself SetupDiGetClassDevs(&GattServiceGUID...) [and then working on the \BTHLEDevice\ tree in Registry].
Now, to answer your question, I'm still searching myself :) But I'm not really sure:
1) that it is a working (dynamic) information to know the connection state
2) that it is a 'Property' you can access by the above methods
I have found out a solution.
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);//DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);//DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
LPTSTR buffer1 = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( // Get Name
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
{
if(strcmp("Your Device",AnsiString(buffer).c_str())==0) //Found your device
{
//########
DEVPROPTYPE ulPropertyType;
DWORD dwSize;
ULONG devst;
// memset(devst,0,sizeof(devst));
bool err = SetupDiGetDeviceProperty( //Checking Connection State
hDevInfo,
&DeviceInfoData,
&DEVPKEY_Device_DevNodeStatus, //Connected(0x02000000)
&ulPropertyType,
(BYTE *) &devst,
sizeof(devst),
&dwSize,
0);
DWORD error;
error = GetLastError();
if (devst &0x02000000) {
//"Status: Getrennt "
}
else
{
//"Status: Verbunden"
}
Hope this snippet helps.

Crypto API RSA public key can decrypt data, is not asymmetric as expected

The problem I am encountering is that I am able to decrypt data using the same RSA 2048-bit public key that was used to encrypt the data. It seems to me that this defeats the entire purpose of encrypting the data in the first place, if a public key can decrypt it. The only thing I can consider at this time is that I'm generating symmetric key exchange pairs when I think I'm generating asymmetric pairs.
The end-user purpose of this is to use it later for transmitting user credentials to be authenticated when using an application away from the office, when I am unable to use their cached credentials from their workstations on the domain. I would theoretically be able to then decrypt these credentials using only the private key.
I have produced a simple test class and code to reproduce my problem. The steps I'm taking are as follows:
Acquire a context to Microsoft Enhanced Cryptographic Provider v1.0
Generate a public / private key pair.
Export the public and private key BLOBs to separate files.
Load up the public key and encrypt some simple text.
Attempt to decrypt the same encrypted text using the public key (I expected it to fail here except for when I'm using the private key - yet both work).
TestEncryptDecrypt helper class: TestEncryptDecrypt.h
#pragma once
#include <Windows.h>
#include <wincrypt.h>
class TestEncryptDecrypt
{
public:
TestEncryptDecrypt()
{
}
~TestEncryptDecrypt()
{
if (hKey != NULL)
CryptDestroyKey(hKey);
if (hProvider != NULL)
CryptReleaseContext(hProvider, 0);
}
BOOL InitializeProvider(LPCTSTR pszProvider, DWORD dwProvType)
{
if (hProvider != NULL)
{
if (!CryptReleaseContext(hProvider, 0))
return 0;
}
return CryptAcquireContext(&hProvider, NULL, pszProvider, dwProvType, 0);
}
BOOL Generate2048BitKeys(ALG_ID Algid)
{
DWORD dwFlags = (0x800 << 16) | CRYPT_EXPORTABLE;
return CryptGenKey(hProvider, Algid, dwFlags, &hKey);
}
VOID ExportPrivatePublicKey(LPTSTR lpFileName)
{
if (hKey == NULL)
return;
DWORD dwDataLen = 0;
BOOL exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwDataLen);
LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, lpKeyBlob, &dwDataLen);
WriteBytesFile(lpFileName, lpKeyBlob, dwDataLen);
free(lpKeyBlob);
}
VOID ExportPublicKey(LPTSTR lpFileName)
{
if (hKey == NULL)
return;
DWORD dwDataLen = 0;
BOOL exportResult = CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwDataLen);
LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
exportResult = CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, lpKeyBlob, &dwDataLen);
WriteBytesFile(lpFileName, lpKeyBlob, dwDataLen);
free(lpKeyBlob);
}
BOOL ImportKey(LPTSTR lpFileName)
{
if (hProvider == NULL)
return 0;
if (hKey != NULL)
CryptDestroyKey(hKey);
LPBYTE lpKeyContent = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &lpKeyContent, &dwDataLen);
BOOL importResult = CryptImportKey(hProvider, lpKeyContent, dwDataLen, 0, 0, &hKey);
delete[] lpKeyContent;
return importResult;
}
BOOL EncryptDataWriteToFile(LPTSTR lpSimpleDataToEncrypt, LPTSTR lpFileName)
{
DWORD SimpleDataToEncryptLength = _tcslen(lpSimpleDataToEncrypt)*sizeof(TCHAR);
DWORD BufferLength = SimpleDataToEncryptLength * 10;
BYTE *EncryptedBuffer = new BYTE[BufferLength];
SecureZeroMemory(EncryptedBuffer, BufferLength);
CopyMemory(EncryptedBuffer, lpSimpleDataToEncrypt, SimpleDataToEncryptLength);
BOOL cryptResult = CryptEncrypt(hKey, NULL, TRUE, 0, EncryptedBuffer, &SimpleDataToEncryptLength, BufferLength);
DWORD dwGetLastError = GetLastError();
WriteBytesFile(lpFileName, EncryptedBuffer, SimpleDataToEncryptLength);
delete[] EncryptedBuffer;
return cryptResult;
}
BOOL DecryptDataFromFile(LPBYTE *lpDecryptedData, LPTSTR lpFileName, DWORD *dwDecryptedLen)
{
if (hKey == NULL)
return 0;
LPBYTE lpEncryptedData = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &lpEncryptedData, &dwDataLen);
BOOL decryptResult = CryptDecrypt(hKey, NULL, TRUE, 0, lpEncryptedData, &dwDataLen);
*dwDecryptedLen = dwDataLen;
//WriteBytesFile(L"decryptedtest.txt", lpEncryptedData, dwDataLen);
*lpDecryptedData = new BYTE[dwDataLen + 1];
SecureZeroMemory(*lpDecryptedData, dwDataLen + 1);
CopyMemory(*lpDecryptedData, lpEncryptedData, dwDataLen);
delete[]lpEncryptedData;
return decryptResult;
}
VOID WriteBytesFile(LPTSTR lpFileName, BYTE *content, DWORD dwDataLen)
{
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0x7, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwBytesWritten = 0;
WriteFile(hFile, content, dwDataLen, &dwBytesWritten, NULL);
CloseHandle(hFile);
}
private:
HCRYPTPROV hProvider = NULL;
HCRYPTKEY hKey = NULL;
VOID ReadBytesFile(LPTSTR lpFileName, BYTE **content, DWORD *dwDataLen)
{
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0x7, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileLength = 0;
DWORD dwBytesToRead = GetFileSize(hFile, NULL);
DWORD dwBytesRead = 0;
*content = new BYTE[dwBytesToRead + 1];
SecureZeroMemory(*content, dwBytesToRead + 1);
ReadFile(hFile, *content, dwBytesToRead, &dwBytesRead, NULL);
*dwDataLen = dwBytesRead;
CloseHandle(hFile);
}
};
Test Code: Main .cpp file
#include "stdafx.h"
#include "TestEncryptDecrypt.h"
#include <Windows.h>
#include <wincrypt.h>
int main()
{
TestEncryptDecrypt *edc = new TestEncryptDecrypt();
//Initialize the provider
edc->InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
//Generate a 2048-bit asymmetric key pair
edc->Generate2048BitKeys(CALG_RSA_KEYX);
//Export the private / public key pair
edc->ExportPrivatePublicKey(L"privpubkey.txt");
//Export only the public key
edc->ExportPublicKey(L"pubkey.txt");
//Import the public key (destroys the private/public key pair already set)
edc->ImportKey(L"pubkey.txt");
//Encrypt and write some test data to file
edc->EncryptDataWriteToFile(TEXT("Hello World!ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), L"encrypteddata.txt");
//Decrypt the data from file using the same public key (this should fail but it doesn't)
DWORD dwDataLen = 0;
LPBYTE lpDecryptedData = NULL;
edc->DecryptDataFromFile(&lpDecryptedData, L"encrypteddata.txt", &dwDataLen);
//Write the supposedly decrypted data to another file
edc->WriteBytesFile(L"decrypteddata.txt", lpDecryptedData, dwDataLen);
//Clear data
delete[] lpDecryptedData;
delete edc;
return 0;
}
Unfortunately I don't get the opportunity to work with C++ very often so you may notice some problems. Feel free to constructively criticize.
Does anyone know why I am able to decrypt data using the same public key?
My goal is to be able to irreversibly encrypt something on the client side where it can only be decrypted on the server, where the private key will hide.
Edit:
I had considered that the hKey wasn't being destroyed properly by the ImportKey method, so I wrote this test case instead (same results - public key can encrypt and decrypt the data):
// CPPTests.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "TestEncryptDecrypt.h"
#include <Windows.h>
#include <wincrypt.h>
int main()
{
TestEncryptDecrypt *edc = new TestEncryptDecrypt();
//Initialize the provider
edc->InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
//Generate a 2048-bit asymmetric key pair
edc->Generate2048BitKeys(CALG_RSA_KEYX);
//Export the private / public key pair
edc->ExportPrivatePublicKey(L"privpubkey.txt");
//Export only the public key
edc->ExportPublicKey(L"pubkey.txt");
//Destroy everything and load up only the public key to write some encrypted data
delete edc;
edc = new TestEncryptDecrypt();
edc->InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
edc->ImportKey(L"pubkey.txt");
//Encrypt and write some test data to file
edc->EncryptDataWriteToFile(TEXT("Hello World!ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), L"encrypteddata.txt");
//Destroy everything and load up only the public key to read some encrypted data
delete edc;
edc = new TestEncryptDecrypt();
edc->InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
edc->ImportKey(L"pubkey.txt");
//Decrypt the data from file using the same public key (this should fail but it doesn't)
DWORD dwDataLen = 0;
LPBYTE lpDecryptedData = NULL;
edc->DecryptDataFromFile(&lpDecryptedData, L"encrypteddata.txt", &dwDataLen);
//Write the supposedly decrypted data to another file
edc->WriteBytesFile(L"decrypteddata.txt", lpDecryptedData, dwDataLen);
//Clear data
delete[] lpDecryptedData;
delete edc;
return 0;
}
This API is deprecated according to Microsoft, so if you came here looking for a native cryptography API, you may want to look elsewhere.
After some fighting with the same problem I realized where the error was.
In your first code you were acquiring your context with the last flag set to zero:
CryptAcquireContext(&hProvider, NULL, pszProvider, dwProvType, 0);
But in your solution you changed it into CRYPT_VERIFYCONTEXT.
CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
You solved your problem by changing this flag, not by importing the keys from OpenSSL. I am pretty sure that if you test this in your initial code, it will work as expected.
This CRYPT_VERIFYCONTEXT flag is responsible for not allowing a key to achieve persistence in the system, a persistence which turned the public RSA able to encrypt and decrypt.
The problem is that for some reason Crypto API, using the Microsoft Enhanced Provider w/ RSA, produces symmetrical keys. I am unable to get it to produce asymmetrical keys. The algorithm will, however, work with asymmetrical keys. So this is good news for us. This means to get this to work we only have to generate keys. You could also export these from self signed certificates, use your companies CA, etc.
To solve this I produced a public/private key pair using OpenSSL. I compiled OpenSSL for Windows just for fun then ran the following statements to get myself a pair of unencrypted public/private key files:
openssl genpkey -out private2.pem -outform PEM -des3 -algorithm RSA -pkeyopt rsa_keygen_bits:2048
or
openssl genrsa -des3 -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
openssl rsa -in private.pem -outform PEM -out private_unencrypted.pem
Once I had those I added 2 new functions to my test helper class, ImportPublicKey and ImportPrivateKey. These will only import PEM files without a passphrase. I don't consider that much of a security threat, considering the public is public and the private should hide on a secure server somewhere, perhaps encoded with a hash.
TestEncryptDecrypt.h
#pragma once
#include <Windows.h>
#include <wincrypt.h>
class TestEncryptDecrypt
{
public:
TestEncryptDecrypt()
{
}
~TestEncryptDecrypt()
{
if (hKey != NULL)
CryptDestroyKey(hKey);
if (hProvider != NULL)
CryptReleaseContext(hProvider, 0);
}
BOOL InitializeProvider(LPCTSTR pszProvider, DWORD dwProvType)
{
if (hProvider != NULL)
{
if (!CryptReleaseContext(hProvider, 0))
return 0;
}
return CryptAcquireContext(&hProvider, NULL, pszProvider, dwProvType, CRYPT_VERIFYCONTEXT);
}
BOOL Generate2048BitKeys(ALG_ID Algid)
{
DWORD dwFlags = (0x800 << 16) | CRYPT_EXPORTABLE;
return CryptGenKey(hProvider, Algid, dwFlags, &hKey);
}
VOID ExportPrivatePublicKey(LPTSTR lpFileName)
{
if (hKey == NULL)
return;
DWORD dwDataLen = 0;
BOOL exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwDataLen);
LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, lpKeyBlob, &dwDataLen);
WriteBytesFile(lpFileName, lpKeyBlob, dwDataLen);
free(lpKeyBlob);
}
VOID ExportPublicKey(LPTSTR lpFileName)
{
if (hKey == NULL)
return;
DWORD dwDataLen = 0;
BOOL exportResult = CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwDataLen);
LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
exportResult = CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, lpKeyBlob, &dwDataLen);
WriteBytesFile(lpFileName, lpKeyBlob, dwDataLen);
free(lpKeyBlob);
}
BOOL ImportKey(LPTSTR lpFileName)
{
if (hProvider == NULL)
return 0;
if (hKey != NULL)
CryptDestroyKey(hKey);
LPBYTE lpKeyContent = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &lpKeyContent, &dwDataLen);
BOOL importResult = CryptImportKey(hProvider, lpKeyContent, dwDataLen, 0, 0, &hKey);
delete[] lpKeyContent;
return importResult;
}
BOOL ImportPublicKey(LPTSTR lpFileName)
{
//If a context doesn't exist acquire one
if (hProvider == NULL)
{
BOOL result = CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!result)
return result;
}
if (hKey != NULL)
CryptDestroyKey(hKey);
//Load the PEM
LPBYTE PublicBytes = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &PublicBytes, &dwDataLen);
//Convert to Unicode
int PublicPEMSize = MultiByteToWideChar(CP_ACP, 0, (LPCCH)PublicBytes, -1, NULL, 0);
TCHAR *PublicPEM = new TCHAR[PublicPEMSize];
MultiByteToWideChar(CP_ACP, 0, (LPCCH)PublicBytes, -1, PublicPEM, PublicPEMSize);
delete[]PublicBytes;
//Convert PEM to DER
LPBYTE PublicDER = NULL;
DWORD dwPublicDERLen = 0;
BOOL result = CryptStringToBinary(PublicPEM, 0, CRYPT_STRING_BASE64HEADER, NULL, &dwPublicDERLen, NULL, NULL);
if (!result)
return result;
PublicDER = new BYTE[dwPublicDERLen];
result = CryptStringToBinary(PublicPEM, 0, CRYPT_STRING_BASE64HEADER, PublicDER, &dwPublicDERLen, NULL, NULL);
if (!result)
return result;
delete[] PublicPEM;
//Decode the object into a public key info struct
CERT_PUBLIC_KEY_INFO *PublicKeyInfo = NULL;
DWORD dwPublicKeyInfoLen = 0;
result = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, PublicDER, dwPublicDERLen, CRYPT_ENCODE_ALLOC_FLAG, NULL, &PublicKeyInfo, &dwPublicKeyInfoLen);
if (!result)
return result;
//Import the public key
result = CryptImportPublicKeyInfo(hProvider, X509_ASN_ENCODING, PublicKeyInfo, &hKey);
if (!result)
return result;
//cleanup
delete[] PublicDER;
LocalFree(PublicKeyInfo);
return result;
}
BOOL ImportPrivateKey(LPTSTR lpFileName)
{
//If a context doesn't exist acquire one
if (hProvider == NULL)
{
BOOL result = CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!result)
return result;
}
if (hKey != NULL)
CryptDestroyKey(hKey);
//Load the PEM
LPBYTE PrivateBytes = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &PrivateBytes, &dwDataLen);
//Convert to Unicode
int PrivatePEMSize = MultiByteToWideChar(CP_ACP, 0, (LPCCH)PrivateBytes, -1, NULL, 0);
TCHAR *PrivatePEM = new TCHAR[PrivatePEMSize];
MultiByteToWideChar(CP_ACP, 0, (LPCCH)PrivateBytes, -1, PrivatePEM, PrivatePEMSize);
delete[]PrivateBytes;
//Convert PEM to DER
LPBYTE PrivateDER = NULL;
DWORD dwPrivateDERLen = 0;
BOOL result = CryptStringToBinary(PrivatePEM, 0, CRYPT_STRING_BASE64HEADER, NULL, &dwPrivateDERLen, NULL, NULL);
if (!result)
return result;
PrivateDER = new BYTE[dwPrivateDERLen];
result = CryptStringToBinary(PrivatePEM, 0, CRYPT_STRING_BASE64HEADER, PrivateDER, &dwPrivateDERLen, NULL, NULL);
if (!result)
return result;
delete[] PrivatePEM;
//Decode the object into a private key info struct
BYTE *PrivateKeyInfo = NULL;
DWORD dwPrivateKeyInfoLen = 0;
result = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, PrivateDER, dwPrivateDERLen, 0, NULL, NULL, &dwPrivateKeyInfoLen);
if (!result)
return result;
PrivateKeyInfo = new BYTE[dwPrivateKeyInfoLen];
result = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, PrivateDER, dwPrivateDERLen, 0, NULL, PrivateKeyInfo, &dwPrivateKeyInfoLen);
if (!result)
return result;
//Import the private key
result = CryptImportKey(hProvider, PrivateKeyInfo, dwPrivateKeyInfoLen, NULL, 0, &hKey);
if (!result)
return result;
//cleanup
delete[] PrivateDER;
delete[] PrivateKeyInfo;
return result;
}
BOOL EncryptDataWriteToFile(LPTSTR lpSimpleDataToEncrypt, LPTSTR lpFileName)
{
DWORD SimpleDataToEncryptLength = _tcslen(lpSimpleDataToEncrypt)*sizeof(TCHAR);
DWORD BufferLength = SimpleDataToEncryptLength * 10;
BYTE *EncryptedBuffer = new BYTE[BufferLength];
SecureZeroMemory(EncryptedBuffer, BufferLength);
CopyMemory(EncryptedBuffer, lpSimpleDataToEncrypt, SimpleDataToEncryptLength);
BOOL cryptResult = CryptEncrypt(hKey, NULL, TRUE, 0, EncryptedBuffer, &SimpleDataToEncryptLength, BufferLength);
DWORD dwGetLastError = GetLastError();
WriteBytesFile(lpFileName, EncryptedBuffer, SimpleDataToEncryptLength);
delete[] EncryptedBuffer;
return cryptResult;
}
BOOL DecryptDataFromFile(LPBYTE *lpDecryptedData, LPTSTR lpFileName, DWORD *dwDecryptedLen)
{
if (hKey == NULL)
return 0;
LPBYTE lpEncryptedData = NULL;
DWORD dwDataLen = 0;
ReadBytesFile(lpFileName, &lpEncryptedData, &dwDataLen);
BOOL decryptResult = CryptDecrypt(hKey, NULL, TRUE, 0, lpEncryptedData, &dwDataLen);
*dwDecryptedLen = dwDataLen;
//WriteBytesFile(L"decryptedtest.txt", lpEncryptedData, dwDataLen);
*lpDecryptedData = new BYTE[dwDataLen + 1];
SecureZeroMemory(*lpDecryptedData, dwDataLen + 1);
CopyMemory(*lpDecryptedData, lpEncryptedData, dwDataLen);
delete[]lpEncryptedData;
return decryptResult;
}
VOID WriteBytesFile(LPTSTR lpFileName, BYTE *content, DWORD dwDataLen)
{
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_WRITE, 0x7, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwBytesWritten = 0;
WriteFile(hFile, content, dwDataLen, &dwBytesWritten, NULL);
CloseHandle(hFile);
}
private:
HCRYPTPROV hProvider = NULL;
HCRYPTKEY hKey = NULL;
VOID ReadBytesFile(LPTSTR lpFileName, BYTE **content, DWORD *dwDataLen)
{
HANDLE hFile = CreateFile(lpFileName, GENERIC_READ, 0x7, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileLength = 0;
DWORD dwBytesToRead = GetFileSize(hFile, NULL);
DWORD dwBytesRead = 0;
*content = new BYTE[dwBytesToRead + 1];
SecureZeroMemory(*content, dwBytesToRead + 1);
ReadFile(hFile, *content, dwBytesToRead, &dwBytesRead, NULL);
*dwDataLen = dwBytesRead;
CloseHandle(hFile);
}
};
And here's the test, providing proof that it cannot decrypt using the public key but instead the private key .pem:
int main()
{
TestEncryptDecrypt *edc = new TestEncryptDecrypt();
edc->ImportPublicKey(L"public.pem");
edc->EncryptDataWriteToFile(L"Hello world! hahahahah", L"encrypted.txt");
LPBYTE decodedData = NULL; DWORD decodedLen = 0;
BOOL result = edc->DecryptDataFromFile(&decodedData, L"encrypted.txt", &decodedLen);
if (result == 1)
OutputDebugString(L"We were able to decrypt from a public key! That's not good.");
result = edc->ImportPrivateKey(L"private_unencrypted.pem");
result = edc->DecryptDataFromFile(&decodedData, L"encrypted.txt", &decodedLen);
edc->WriteBytesFile(L"decrypted.txt", decodedData, decodedLen);
return 0;
}
I think the title is a bit misleading in a way that the RSA keys are definitely asymmetric and the Public key is not able to decrypt anything on its own by its very mathematical definition.
However it seems that the Public and Private keys (being generated as a pair) somehow "know" about the existence of one another (they are linked internally). Once generated by the "CryptGenKey" function, the PrivatePublicKeyPair blob is saved in the CSP's (Cryptographic Service Provider) key container.
Even if you destroy the "hKey" handle to the Private key blob as well as the "hProvider" handle to the CSP, the data is not scrubbed from the memory space where it's been generated (unless you reboot your computer of course) and so when you import just the Public key from file it will know where the Private key was previously located even though the handle was destroyed.
Interestingly enough, when you use the "CryptDecrypt" function to decrypt data using just the imported Public key, even though it manages to locate the previously destroyed Private key and successfully decrypt the data, it will silently issue the error "1008 - An attempt was made to reference a token that does not exist". You wouldn't even know about the error being raised if you didn't check with "GetLastError"!
The solution to all this madness is the "CRYPT_VERIFYCONTEXT" flag that removes the persistence of the key container inside the CSP as someone has already mentioned above. Even when using this flag you still need to destroy the "hProvider" handle to the CSP if you used the "CryptGenKey" function to generate the keys before importing them from file. Only then will the imported Public key behave as expected, namely only able to be used for encryption! If you try to use it for decryption you will get error "0x8009000D - Key does not exist" since it won't be able to find its private counterpart anymore!
I realize this topic is rather old by now but since I was recently confronted with this same conundrum I thought I'd share my two cents on the matter.

Add Application to Startup (Registry)

I'm trying to add my software to registry, I have found some pieces of the codes I can use but not full working code C/C++ is new to me and can't create it on my own. But here is the basic idea: Check if reg key set if not create it.
I was able to get my program location using this code:
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
And was able to create the key with: (Not sure if it's the right way)
HKEY newValue;
RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&newValue);
RegSetValueEx(newValue,"myprogram",0,REG_SZ,(LPBYTE)szPath,sizeof(szPath));
RegCloseKey(newValue);
return 0;
What is missing, A small check if the key isn't already there...
Thank you!
Here's some code that likely does what you want. Call RegisterProgram for your EXE to self-register itself for automatically being started when the user logs in. This function calls GetModuleFileName and then invokes another helper function called RegisterMyProgramForStartup that does the writing to the registry.
Call IsMyProgramRegisteredForStartup(L"My_Program") to detect if the registration actually exists and appears valid.
One quick note. The performance impact of checking to see if the key exists before actually writing it out again is negligible. You could just call RegisterProgram blindly and it will overwrite the key if it already exists. Detecting if the registration exists is useful for initializing your UI checkbox that enables or disables auto-start. (You are giving your users a choice, right? Because I hate apps that automatically install themselves to run automatically without giving me a choice.)
BOOL IsMyProgramRegisteredForStartup(PCWSTR pszAppName)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwRegType = REG_SZ;
wchar_t szPathToExe[MAX_PATH] = {};
DWORD dwSize = sizeof(szPathToExe);
lResult = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_READ, &hKey);
fSuccess = (lResult == 0);
if (fSuccess)
{
lResult = RegGetValueW(hKey, NULL, pszAppName, RRF_RT_REG_SZ, &dwRegType, szPathToExe, &dwSize);
fSuccess = (lResult == 0);
}
if (fSuccess)
{
fSuccess = (wcslen(szPathToExe) > 0) ? TRUE : FALSE;
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
BOOL RegisterMyProgramForStartup(PCWSTR pszAppName, PCWSTR pathToExe, PCWSTR args)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwSize;
const size_t count = MAX_PATH*2;
wchar_t szValue[count] = {};
wcscpy_s(szValue, count, L"\"");
wcscat_s(szValue, count, pathToExe);
wcscat_s(szValue, count, L"\" ");
if (args != NULL)
{
// caller should make sure "args" is quoted if any single argument has a space
// e.g. (L"-name \"Mark Voidale\"");
wcscat_s(szValue, count, args);
}
lResult = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, 0, (KEY_WRITE | KEY_READ), NULL, &hKey, NULL);
fSuccess = (lResult == 0);
if (fSuccess)
{
dwSize = (wcslen(szValue)+1)*2;
lResult = RegSetValueExW(hKey, pszAppName, 0, REG_SZ, (BYTE*)szValue, dwSize);
fSuccess = (lResult == 0);
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
void RegisterProgram()
{
wchar_t szPathToExe[MAX_PATH];
GetModuleFileNameW(NULL, szPathToExe, MAX_PATH);
RegisterMyProgramForStartup(L"My_Program", szPathToExe, L"-foobar");
}
int _tmain(int argc, _TCHAR* argv[])
{
RegisterProgram();
IsMyProgramRegisteredForStartup(L"My_Program");
return 0;
}
To check whether or not the value exists, call RegQueryValueEx.
LONG retval = RegQueryValueEx(hKey, "myprogram", NULL, NULL, NULL, NULL);
Note that what you called newValue is actually a key rather than a value. To avoid confusion you should name it such. I used the name hKey.
Then to check whether or not the value exists, compare retval against ERROR_SUCCESS as described in the documentation.
The other problem with your code is that there is absolutely no error checking. I'll leave that to you to address.
You forget to write an argument about security access

Save Me Simple Add to Startup via Registry [duplicate]

I'm trying to add my software to registry, I have found some pieces of the codes I can use but not full working code C/C++ is new to me and can't create it on my own. But here is the basic idea: Check if reg key set if not create it.
I was able to get my program location using this code:
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
And was able to create the key with: (Not sure if it's the right way)
HKEY newValue;
RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&newValue);
RegSetValueEx(newValue,"myprogram",0,REG_SZ,(LPBYTE)szPath,sizeof(szPath));
RegCloseKey(newValue);
return 0;
What is missing, A small check if the key isn't already there...
Thank you!
Here's some code that likely does what you want. Call RegisterProgram for your EXE to self-register itself for automatically being started when the user logs in. This function calls GetModuleFileName and then invokes another helper function called RegisterMyProgramForStartup that does the writing to the registry.
Call IsMyProgramRegisteredForStartup(L"My_Program") to detect if the registration actually exists and appears valid.
One quick note. The performance impact of checking to see if the key exists before actually writing it out again is negligible. You could just call RegisterProgram blindly and it will overwrite the key if it already exists. Detecting if the registration exists is useful for initializing your UI checkbox that enables or disables auto-start. (You are giving your users a choice, right? Because I hate apps that automatically install themselves to run automatically without giving me a choice.)
BOOL IsMyProgramRegisteredForStartup(PCWSTR pszAppName)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwRegType = REG_SZ;
wchar_t szPathToExe[MAX_PATH] = {};
DWORD dwSize = sizeof(szPathToExe);
lResult = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_READ, &hKey);
fSuccess = (lResult == 0);
if (fSuccess)
{
lResult = RegGetValueW(hKey, NULL, pszAppName, RRF_RT_REG_SZ, &dwRegType, szPathToExe, &dwSize);
fSuccess = (lResult == 0);
}
if (fSuccess)
{
fSuccess = (wcslen(szPathToExe) > 0) ? TRUE : FALSE;
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
BOOL RegisterMyProgramForStartup(PCWSTR pszAppName, PCWSTR pathToExe, PCWSTR args)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwSize;
const size_t count = MAX_PATH*2;
wchar_t szValue[count] = {};
wcscpy_s(szValue, count, L"\"");
wcscat_s(szValue, count, pathToExe);
wcscat_s(szValue, count, L"\" ");
if (args != NULL)
{
// caller should make sure "args" is quoted if any single argument has a space
// e.g. (L"-name \"Mark Voidale\"");
wcscat_s(szValue, count, args);
}
lResult = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, 0, (KEY_WRITE | KEY_READ), NULL, &hKey, NULL);
fSuccess = (lResult == 0);
if (fSuccess)
{
dwSize = (wcslen(szValue)+1)*2;
lResult = RegSetValueExW(hKey, pszAppName, 0, REG_SZ, (BYTE*)szValue, dwSize);
fSuccess = (lResult == 0);
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
void RegisterProgram()
{
wchar_t szPathToExe[MAX_PATH];
GetModuleFileNameW(NULL, szPathToExe, MAX_PATH);
RegisterMyProgramForStartup(L"My_Program", szPathToExe, L"-foobar");
}
int _tmain(int argc, _TCHAR* argv[])
{
RegisterProgram();
IsMyProgramRegisteredForStartup(L"My_Program");
return 0;
}
To check whether or not the value exists, call RegQueryValueEx.
LONG retval = RegQueryValueEx(hKey, "myprogram", NULL, NULL, NULL, NULL);
Note that what you called newValue is actually a key rather than a value. To avoid confusion you should name it such. I used the name hKey.
Then to check whether or not the value exists, compare retval against ERROR_SUCCESS as described in the documentation.
The other problem with your code is that there is absolutely no error checking. I'll leave that to you to address.
You forget to write an argument about security access