I'd like to implement data encryption and decryption in a C++ application running on Windows. I've spent considerable time looking around the Web and am thinking I should probably use the Windows Cryptography API: Next Generation (CNG) functions (although I'm open to better alternatives).
What I find everywhere are complex examples that do all sorts of stuff. I don't feel that confident in this area and so I'd like to find a simple example. In the end, I need a method that takes a string and encrypts, and another methods that decrypts the data back to the string. The user would supply a password for both operations.
This must have been done countless times already. Can anyone point me to a complete and competent example? Ultimately, I'll end up with an Encrypt() and Decrypt() method.
Something that is both secure and performant would be ideal.
Before encrypting (and decrypting) you need to derive key from password with key derivation functions (for example PBKDF2 with SHA256). To prevent pre-computed dictionary attacks
in additional to password you will also need random string (called salt).
Next pick cipher algorithm (AES with 256-bit key is good one) and cipher mode (ECB cipher mode considered weak, so use any other for example CBC). Also it will require one more random string (called initialization vector).
So encrypting algorithm will be:
Generate random salt
Derive key(password, salt) = key
Generate random IV
Encrypt(key, IV, plain text) = cipher text
Input parameters: plain text, password
Output parameters: cipher text, salt, IV
Decrypting algorithm will be:
Derive key(password, salt) = key
Decrypt(key, iv, cipher text) = plain text
Input parameters: cipher text, salt, iv, password
Output parameters: plain text
Sample code:
#include <Windows.h>
#include <iostream>
#include <vector>
#include <array>
#pragma comment(lib, "bcrypt")
static NTSTATUS gen_random(BYTE* buf, ULONG buf_len)
{
BCRYPT_ALG_HANDLE hAlg = nullptr;
NTSTATUS status = NTE_FAIL;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"RNG", nullptr, 0);
if (status != ERROR_SUCCESS) {
return status;
}
status = BCryptGenRandom(hAlg, buf, buf_len, 0);
} while (0);
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
static NTSTATUS derive_key(BYTE* pass, ULONG pass_len, BYTE* salt,
ULONG salt_len, const ULONG iteration, BYTE* derived_key, ULONG derived_key_len)
{
BCRYPT_ALG_HANDLE hPrf = nullptr;
NTSTATUS status = ERROR_SUCCESS;
do {
status = BCryptOpenAlgorithmProvider(&hPrf, L"SHA256", nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != ERROR_SUCCESS) {
break;
}
status = BCryptDeriveKeyPBKDF2(hPrf, pass, pass_len, salt, salt_len, iteration, derived_key, derived_key_len, 0);
} while (0);
if (hPrf) {
BCryptCloseAlgorithmProvider(hPrf, 0);
}
return status;
}
static NTSTATUS do_encrypt(BYTE* key, ULONG key_len, BYTE* plain_text, ULONG plain_text_len,
std::vector<BYTE>& iv, std::vector<BYTE>& cipher_text)
{
NTSTATUS status = NTE_FAIL;
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"AES", nullptr, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* create key object */
status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, key, key_len, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* set chaining mode */
std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, ptr, size, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* generate iv */
ULONG block_len = 0;
ULONG res = 0;
status = BCryptGetProperty(hAlg, BCRYPT_BLOCK_LENGTH, reinterpret_cast<BYTE*>(&block_len), sizeof(block_len), &res, 0);
if (status != ERROR_SUCCESS) {
break;
}
iv.resize(block_len);
status = gen_random(iv.data(), static_cast<ULONG>(iv.size()));
if (status != ERROR_SUCCESS) {
break;
}
/* BCryptEncrypt modify iv parameter, so we need to make copy */
std::vector<BYTE> iv_copy = iv;
/* get cipher text length */
ULONG cipher_text_len = 0;
status = BCryptEncrypt(hKey, plain_text, plain_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
nullptr, cipher_text_len, &cipher_text_len, BCRYPT_BLOCK_PADDING);
if (status != ERROR_SUCCESS) {
break;
}
cipher_text.resize(static_cast<size_t>(cipher_text_len));
/* now encrypt */
status = BCryptEncrypt(hKey, plain_text, plain_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
cipher_text.data(), cipher_text_len, &cipher_text_len, BCRYPT_BLOCK_PADDING);
} while (0);
/* cleanup */
if (hKey) {
BCryptDestroyKey(hKey);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
static NTSTATUS do_decrypt(BYTE* key, ULONG key_len, BYTE* cipher_text, ULONG cipher_text_len,
const std::vector<BYTE>& iv, std::vector<BYTE>& plain_text)
{
NTSTATUS status = NTE_FAIL;
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
do {
status = BCryptOpenAlgorithmProvider(&hAlg, L"AES", nullptr, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* create key object */
status = BCryptGenerateSymmetricKey(hAlg, &hKey, nullptr, 0, key, key_len, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* set chaining mode */
std::wstring mode = BCRYPT_CHAIN_MODE_CBC;
BYTE* ptr = reinterpret_cast<BYTE*>(const_cast<wchar_t*>(mode.data()));
ULONG size = static_cast<ULONG>(sizeof(wchar_t) * (mode.size() + 1));
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, ptr, size, 0);
if (status != ERROR_SUCCESS) {
break;
}
/* BCryptEncrypt modify iv parameter, so we need to make copy */
std::vector<BYTE> iv_copy = iv;
/* get expected plain text length */
ULONG plain_text_len = 0;
status = BCryptDecrypt(hKey, cipher_text, cipher_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
nullptr, plain_text_len, &plain_text_len, BCRYPT_BLOCK_PADDING);
plain_text.resize(static_cast<size_t>(plain_text_len));
/* decrypt */
status = BCryptDecrypt(hKey, cipher_text, cipher_text_len, nullptr, iv_copy.data(), static_cast<ULONG>(iv_copy.size()),
plain_text.data(), plain_text_len, &plain_text_len, BCRYPT_BLOCK_PADDING);
/* actualize size */
plain_text.resize(static_cast<size_t>(plain_text_len));
} while (0);
/* cleanup */
if (hKey) {
BCryptDestroyKey(hKey);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return status;
}
NTSTATUS encrypt(BYTE* pass, ULONG pass_len, const std::vector<BYTE>& plain_text,
std::vector<BYTE>& salt, std::vector<BYTE>& iv, std::vector<BYTE>& cipher_text)
{
NTSTATUS status = NTE_FAIL;
salt.resize(8);
std::array<BYTE, 32> key{0x00};
do {
/* generate salt */
status = gen_random(salt.data(), static_cast<ULONG>(salt.size()));
if (status != ERROR_SUCCESS) {
break;
}
/* derive key from password using SHA256 algorithm and 20000 iteration */
status = derive_key(pass, pass_len, salt.data(), static_cast<ULONG>(salt.size()), 20000, key.data(), key.size());
if (status != ERROR_SUCCESS) {
break;
}
/* encrypt */
status = do_encrypt(key.data(), static_cast<ULONG>(key.size()), const_cast<BYTE*>(plain_text.data()),
static_cast<ULONG>(plain_text.size()), iv, cipher_text);
} while (0);
SecureZeroMemory(key.data(), key.size());
return status;
}
NTSTATUS decrypt(BYTE* pass, ULONG pass_len, const std::vector<BYTE>& salt, const std::vector<BYTE>& iv,
const std::vector<BYTE>& cipher_text, std::vector<BYTE>& plain_text)
{
NTSTATUS status = NTE_FAIL;
std::array<BYTE, 32> key{0x00};
do {
/* derive key from password using same algorithm, salt and iteraion count */
status = derive_key(pass, pass_len, const_cast<BYTE*>(salt.data()), static_cast<ULONG>(salt.size()),
20000, key.data(), key.size());
if (status != ERROR_SUCCESS) {
break;
}
/* decrypt */
status = do_decrypt(key.data(), static_cast<ULONG>(key.size()), const_cast<BYTE*>(cipher_text.data()),
static_cast<ULONG>(cipher_text.size()), const_cast<BYTE*>(iv.data()),
static_cast<ULONG>(iv.size()), plain_text);
} while (0);
SecureZeroMemory(key.data(), key.size());
return status;
}
How to use CryptoAPI Next Generation (CNG): Don't use it. Use OTP.
If your user is physically sent the key, then use the simple but unbreakable OTP or One Time Pad (See https://en.wikipedia.org/wiki/One-time_pad).
It is very easy to use.
It is very easy to understand.
Simply supply enough OTP keys to the user, which you or someone randomly types in via a keyboard (I told you it is easy), to last for what you think is a reasonable use by the user.
Do NOT derive the keys from passwords or from anything else that the previous posters listed. That is lazy and is NOT secure. Make the keys as I said and then they will NOT be linked to any code source at all.
Do NOT waste your time with "learn some of the fundamentals of encryption and cryptography" (if that learning is not directly in support of your using One Time Pads).
For you to read some discussions and definitions related to OTP's:
https://www.slideshare.net/AsadAli108/3-l4
https://www.slideshare.net/Jonlitan/one-time-pad-encryption-technique
There is no commonly known encryption that even comes close to OTPs. Example: OTPs can not be mathematically or computationally broken down into a mathematical or computational process.
I think that you have asked for simple, and easy, and not so complicated, but secure, and One Time Pads are all that. If you supply the keys to the user directly, then no human (other than Christians with enough faith) can break them.
Some links to help you with hopefully useful independent examples in C and C++:
A quick search gave me these examples which due to the limit on time for this bounty I have not tested:
https://www.sanfoundry.com/cpp-program-implement-one-time-pad-algorithm/
and
http://www.cplusplus.com/forum/beginner/179981/
and
https://github.com/DDomjosa/One-time-pad-encryption/blob/master/One%20time%20pad%20encryption.cpp
ps: In case you are wondering why I post but do not reply to comments: My browser does not seem to support the "add a comment" on these Stack Overflow pages, so I can post but I cannot (until SO makes their pages backward compatible sufficient for me) reply or comment beyond that.
Related
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.
How can I convert the following cryptography code (VB.NET 4.0) to the C++ equivalent one, using Microsoft CryptoAPI (CryptDeriveKey, BCrypt[...] functions, CryptAcquireContext, etc.)? (I haven't found a single article on the Internet describing AES using Microsoft CryptoAPI...)
Dim Key(31) As Byte
Dim IV(15) As Byte
Array.Copy(SomeByteArray, IV, 16)
Array.Copy((New SHA512Managed).ComputeHash(SomeByteArray), Key, 32)
Using AESEncr As New RijndaelManaged() With {.Padding = PaddingMode.ISO10126}
FinalEncrypted = AESEncr.CreateEncryptor(Key, IV).TransformFinalBlock(AnotherByteArray, 0, AnotherByteArray.GetLength(0))
End Using
and the decrypting one:
Dim Key(31) As Byte
Dim IV(15) As Byte
Array.Copy(SomeByteArray, IV, 16)
Array.Copy((New SHA512Managed).ComputeHash(SomeByteArray), Key, 32)
Using AESEncr As New RijndaelManaged() With {.Padding = PaddingMode.ISO10126}
FinalDecrypted = AESEncr.CreateDecryptor(Key, IV).TransformFinalBlock(FinalEncrypted, 0, FinalEncrypted.GetLength(0))
End Using
(Note: I already have C++ code about the SHA-512 method, so don't bother with that.)
So, the code I made for AES-256 Encryption/Decryption is the following: (it takes BYTE* Data and BYTE* IV as parameters)
BYTE *hash, *res;
HCRYPTPROV hCrypt = NULL;
HCRYPTKEY hKey = NULL;
struct {
BLOBHEADER hdr;
DWORD len;
BYTE key[32];
} key_blob;
key_blob.hdr.bType = PLAINTEXTKEYBLOB;
key_blob.hdr.bVersion = CUR_BLOB_VERSION;
key_blob.hdr.reserved = 0;
key_blob.hdr.aiKeyAlg = CALG_AES_256;
key_blob.len = 32;
hash = ComputeSHA512Hash(IV);
copy(hash, hash + 32, key_blob.key);
res = new BYTE[16];
copy(Data, Data + 15, res);
res[15] = 0;
// Get the Microsoft Enhanced RSA and AES Cryptographic Service Provider
if (!CryptAcquireContext(&hCrypt, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0))
throw E_FAIL;
// Import our key blob
if (!CryptImportKey(hCrypt, (BYTE *)&key_blob, sizeof(key_blob), NULL, 0, &hKey))
throw E_FAIL;
// Set the mode to Cipher Block Chaining
DWORD dwMode = CRYPT_MODE_CBC;
if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE *)&dwMode, 0))
throw E_FAIL;
// Set the Initialization Vector to ours
if (!CryptSetKeyParam(hKey, KP_IV, IV, 0))
throw E_FAIL;
// Do the main encryption
DWORD pdwDataLen = 15;
if (!CryptEncrypt(hKey, NULL, TRUE, 0, res, &pdwDataLen, 16))
throw E_FAIL;
// Do the main decryption
pdwDataLen = 16;
if (!CryptDecrypt(hKey, NULL, TRUE, 0, res, &pdwDataLen))
throw E_FAIL;
// Destroy whatever was created before (free memory)
delete hash;
delete res;
if (hKey)
CryptDestroyKey(hKey);
if (hCrypt)
CryptReleaseContext(hCrypt, 0);
As I previously said, I already have code for the ComputeSHA512Hash() function, so my code is complete for my purposes. I hope this code will be useful for everyone wanting to write AES-256 code.
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
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
I'm trying to decrypt - using the microsoft's CryptoAPI in C++ - a short message encrypted using mcrypt_encrypt in PHP. The php line is:
mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $key, $msg, MCRYPT_MODE_CBC);
where $key and $msg are strings.
In C++ I have the key, and my decryption function looks like this:
bool decrypt( const unsigned char* input_buffer,
const size_t& input_size,
const unsigned char* key,
const size_t& key_size,
unsigned char* output_buffer,
DWORD* out_size)
{
Log(L"START init_crypto");
bool ret = false;
HCRYPTKEY hKey = NULL;
HCRYPTPROV hCryptProv = NULL;
DWORD dwDataLen = 0;
// Attempt to acquire a handle to the crypto provider for AES
if(0 == CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) ){//PROV_RSA_AES
Log(L"CryptAcquireContext failed with code %ld", GetLastError());
goto end;
}
// key creation based on
// http://mirror.leaseweb.com/NetBSD/NetBSD-release-5-0/src/dist/wpa/src/crypto/crypto_cryptoapi.c
struct {
BLOBHEADER hdr;
DWORD len;
BYTE key[32];
} key_blob;
key_blob.hdr.bType = PLAINTEXTKEYBLOB;
key_blob.hdr.bVersion = CUR_BLOB_VERSION;
key_blob.hdr.reserved = 0;
key_blob.hdr.aiKeyAlg = CALG_AES_256;
key_blob.len = 32;//key_size;
memset(key_blob.key, '\0', sizeof(key_blob.key));
assert(key_size <= sizeof(key_blob.key));
CopyMemory(key_blob.key, key, min(key_size, sizeof(key_blob.key)));
if (!CryptImportKey( hCryptProv,
(BYTE *)&key_blob,
sizeof(key_blob),
0,
CRYPT_EXPORTABLE,
&hKey)){
Log(L"Error in CryptImportKey 0x%08x \n", GetLastError());
goto free_context;
}
else{
//---------------------------
// Set Mode
DWORD dwMode = CRYPT_MODE_CBC;
if(!CryptSetKeyParam( hKey, KP_MODE, (BYTE*)&dwMode, 0 )){
// Handle error
Log(L"Cannot set the cbc mode for decryption 0x%08x \n", GetLastError());
goto free_key;
}
//----------------------------
// Set IV
DWORD dwBlockLen = 0;
DWORD dwDataLen = sizeof(dwBlockLen);
if (!CryptGetKeyParam(hKey, KP_BLOCKLEN, (BYTE *)&dwBlockLen, &dwDataLen, 0)){
// Handle error
Log(_USTR("Cannot get the block length 0x%08x \n"), GetLastError());
goto free_key;
}
dwBlockLen /= 8;
BYTE *pbTemp = NULL;
if (!(pbTemp = (BYTE *)LocalAlloc(LMEM_FIXED, dwBlockLen))){
// Handle error
Log(L"Cannot allcoate the IV block 0x%08x \n", GetLastError());
goto free_key;
}
memset(pbTemp, '\0', dwBlockLen);
if (!CryptSetKeyParam(hKey, KP_IV, pbTemp, 0)){
// Handle error
Log(L"Cannot set the IV block 0x%08x \n", GetLastError());
LocalFree(pbTemp);
goto free_key;
}
LocalFree(pbTemp);
}
CopyMemory(output_buffer, input_buffer, min(*out_size, input_size));
*out_size = input_size;
if (!CryptDecrypt(hKey, NULL, TRUE, 0, output_buffer, out_size)){
Log(L"CryptDecrypt failed with code %ld", GetLastError());
goto free_key;
}
else{
Log(L"Decryption...");
ret = true;
}
free_key:
if (hKey)
CryptDestroyKey( hKey );
free_context:
if (hCryptProv)
CryptReleaseContext(hCryptProv, 0);
end:
return ret;
}
I consistently get the "bad data" error at CryptDecrypt()... I may be missing something obvious - if so, please help.
EDIT - To isolate the cause of the problem I replaced the two lines before CryptDecrypt (the CopyMemory stuff) with the following code:
....
strcpy((char*)output_buffer, "stuff");
DWORD plain_txt_len = strlen((char*)output_buffer);
if (!CryptEncrypt(hKey, NULL, TRUE, 0, output_buffer, &plain_txt_len, *out_size)){
Log(L"CryptEncrypt failed with code 0x%08x", GetLastError());
goto free_key;
}
...
and the CryptDecrypt is working -- which makes me believe that the problem is the key/and or message transmission from php to C++ ... If you agree can you give me a hint on how to make sure that the strings I use in PHP are the same with the ones in C++ (at byte level?)
EDIT2 --
After I changed my strings in binary streams (using pack) in php, and after I implemented the workaround(?) for AES vs Rijndael from here : http://kix.in/2008/07/22/aes-256-using-php-mcrypt/ I finaly have CryptDecrypt decrypting my PHP message... the problem is that it also still fails - even if the output contains the decrypted text. Any ideas about why could this happen?
Try passing NULL instead of CRYPT_VERIFYCONTEXT when acquiring the context.
With block encryption algorithms such as AES you need to add padding to the data being encrypted up to a multiple of the block length. Using your code example that already retrieves the block size you can calculate the padding required for encryption:
dwPadding = dwBlockLen - dwDataLen % dwBlockLen
Then append "dwPadding" number of characters (NULL works fine) to the data being encrypted and you will no longer get "Bad Data" errors in decryption.
You can also find out the size of the required encryption buffer directly by making an additional call to "CryptEncrypt" with a NULL buffer before the actual encryption:
dwBuffSize = dwDataLen
CryptEncrypt(hKey, NULL, TRUE, 0, NULL, &dwBuffSize, 0)
dwPadding = dwBuffsize - dwDataLen
Both methods are equivalent and produce the same desired result. The "CryptDecrypt" function already knows about padding and will return the actual size of the decryption buffer in one go so you will know exactly where your decrypted data ends.