I tried everything to make it working, so i decided to do full example. I hope someone would help me.
Message to sign: Hello World
Signing example:
https://gchq.github.io/CyberChef/#recipe=RSA_Sign('-----BEGIN%20RSA%20PRIVATE%20KEY-----%5CnMIICWwIBAAKBgQDCLDny1MwbGVFrjtNXH%2BHFxZ/4C5Gz0q2b5exrJLHums5YDSFl%5Cn6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi%2B53nP7zJsxmLnpN5J8QEQJgTlkw%2Bk%5CnIa8%2Bc8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQpGuPpUTCtyfRmBNuGbQIDAQAB%5CnAoGABH78qoRF45y%2B92Qbvak105wDW7181rKapYD56/MyEYnRHFXVf6QdzU3zyTSr%5Cn4rMPov6ygDtRNadCK2DiPqDsvOiQU%2By7ZSt%2Bt0hdUv8nkQOzR167dM8dLAoVMVtm%5CnKgUYO1MUnERvh%2BpmPKOmYozBAZbJJshOLbmqfGoJH1wUzCECQQDkC1SDsmLbT9uA%5Cn8MR6u4sn%2Bkghli5oxP3dnj/1WRAATt3SrQNGUyJsLCCDc70EOPwx/nIY4aDog/3K%5Cnixut8zVdAkEA2fnp0rCOXXikNV69z5Y2lj4OYpLtJiaJQK6NOYF997zso2oln/xX%5Cnddxle0%2Ba5KufdkP4DGP0QSnZKA%2B6qIf0UQJAf9hOQCrQuwzRDT9tlzTu9bGdoJ62%5CnU%2BwkOotOZfjRPKr6NvLhxBo1URmH/Mn07JoZ4Nk6E/LiJ5hfvp4wHVwczQJAIDxR%5CnVBNAOpqIzkvAjl6MnBN5VSKdZ7LzQVmPER4RXv3VkSU1gz9yP7/kUiQnqAGph3ft%5CnywdNLAXgU4hf9mSEwQJAbLV0c7GWXPCSk3k0gwOZCjHKaIxDIBjZudv5cvO9sPQx%5CnsqE17eyl5%2BufQq1xOQZL4HL%2BnUjlOcgT/pIdI9430A%3D%3D%5Cn-----END%20RSA%20PRIVATE%20KEY-----','','MD5')To_Hex('0x%20with%20comma',0)&input=SGVsbG8gV29ybGQ
Verification example: https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')RSA_Verify('-----BEGIN%20PUBLIC%20KEY-----%5CnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH%2BHFxZ/4%5CnC5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi%2B53nP%5Cn7zJsxmLnpN5J8QEQJgTlkw%2BkIa8%2Bc8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp%5CnGuPpUTCtyfRmBNuGbQIDAQAB%5Cn-----END%20PUBLIC%20KEY-----','Hello%20World','MD5')&input=YjU5ODE1MzYwNjUyZWU3MWE1OTlhNTljZDQzM2Q4MmMyMzFhY2UzNzdjZWRkZDc3YzBkNTBhMmJkYWZhNDJmMjM0OTVkODVhNDg4Y2RiZmUwNDczNjNkOTI3NGE0MWM0MzgwZTgyNzJjMTEzNTExNDZkYmM4NDFhNTNkMTVkZGE0NTE2MGEyMzM4YzQxMzcwZDg2NzFhMTZmYTlkZTExNzQzZjY1N2UzZjMzYzA5MTBmMzYwMWY0MzQzNTZlNzllMWQ3NTEzNzRmOGVkZmZkZmY3MWMxMWZjOWUzNDI0ZmJlNmY0OWI2ZWI3Yzc4Zjc2MDRlZWZhYTU5ZTBhNmNhOA
Public and private key are generated only for that example purpose
C++ code:
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include "windows.h"
#include "wincrypt.h"
using namespace std;
std::string GetLastErrorAsString() {
//Get the error message ID, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0) {
return std::string(); //No error message has been recorded
}
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return message;
}
int test4(const vector<unsigned char> &signature, const vector<unsigned char> &data_to_verify) {
string rsaKey = "-----BEGIN PUBLIC KEY-----\n"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCLDny1MwbGVFrjtNXH+HFxZ/4\n"
"C5Gz0q2b5exrJLHums5YDSFl6c55QLYNZ4MOlVrahVsAOO0ENkqA5QdswTi+53nP\n"
"7zJsxmLnpN5J8QEQJgTlkw+kIa8+c8j0FWJCRFcZ4vlxcqIymJc/tpBuHfVIXpQp\n"
"GuPpUTCtyfRmBNuGbQIDAQAB\n"
"-----END PUBLIC KEY-----";
char derPubKey[2048];
size_t derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
int publicKeyInfoLen;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
/*
* Convert from PEM format to DER format - removes header and footer and decodes from base64
*/
if (!CryptStringToBinaryA(rsaKey.data(), 0, CRYPT_STRING_BASE64HEADER, reinterpret_cast<BYTE *>(derPubKey),
reinterpret_cast<DWORD *>(&derPubKeyLen), NULL, NULL)) {
fprintf(stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError());
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, reinterpret_cast<const BYTE *>(derPubKey),
derPubKeyLen,
CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo,
reinterpret_cast<DWORD *>(&publicKeyInfoLen))) {
fprintf(stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError());
return -1;
}
/*
* Acquire context
*/
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
{
printf("CryptAcquireContext failed - err=0x%x.\n", GetLastError());
return -1;
}
}
/*
* Import the public key using the context
*/
if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey)) {
fprintf(stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError());
return -1;
}
LocalFree(publicKeyInfo);
/*
* Now use hKey to encrypt whatever you need.
*/
// Hash the data_to_verify
HCRYPTHASH hHash = 0;
if (!CryptCreateHash(hProv, CALG_MD5, NULL, 0, &hHash)) {
printf("CryptCreateHash failed with error 0x%.8lX\n", GetLastError());
return -1;
}
if (!CryptHashData(hHash, data_to_verify.data(), data_to_verify.size(), 0)) {
printf("CryptHashData failed with error 0x%.8lX\n", GetLastError());
return -1;
}
// Sign the hash using our imported key
if (!CryptVerifySignature(hHash, signature.data(), signature.size(), hKey, nullptr, 0)) {
printf("Signature verification failed with error");
cout << GetLastErrorAsString() << endl;
return false;
} else {
printf("Signature verification succeeded.\n");
return true;
}
return 0;
}
//vector<unsigned char> to hex
std::string to_hex(const std::vector<unsigned char> &v) {
std::string result;
char hex[] = "0123456789abcdef";
for (unsigned char c : v) {
result += hex[c >> 4];
result += hex[c & 15];
}
return result;
}
int main() {
vector<unsigned char> signature = {0xb5, 0x98, 0x15, 0x36, 0x06, 0x52, 0xee, 0x71, 0xa5, 0x99, 0xa5, 0x9c, 0xd4,
0x33, 0xd8, 0x2c, 0x23, 0x1a, 0xce, 0x37, 0x7c, 0xed, 0xdd, 0x77, 0xc0, 0xd5,
0x0a, 0x2b, 0xda, 0xfa, 0x42, 0xf2, 0x34, 0x95, 0xd8, 0x5a, 0x48, 0x8c, 0xdb,
0xfe, 0x04, 0x73, 0x63, 0xd9, 0x27, 0x4a, 0x41, 0xc4, 0x38, 0x0e, 0x82, 0x72,
0xc1, 0x13, 0x51, 0x14, 0x6d, 0xbc, 0x84, 0x1a, 0x53, 0xd1, 0x5d, 0xda, 0x45,
0x16, 0x0a, 0x23, 0x38, 0xc4, 0x13, 0x70, 0xd8, 0x67, 0x1a, 0x16, 0xfa, 0x9d,
0xe1, 0x17, 0x43, 0xf6, 0x57, 0xe3, 0xf3, 0x3c, 0x09, 0x10, 0xf3, 0x60, 0x1f,
0x43, 0x43, 0x56, 0xe7, 0x9e, 0x1d, 0x75, 0x13, 0x74, 0xf8, 0xed, 0xff, 0xdf,
0xf7, 0x1c, 0x11, 0xfc, 0x9e, 0x34, 0x24, 0xfb, 0xe6, 0xf4, 0x9b, 0x6e, 0xb7,
0xc7, 0x8f, 0x76, 0x04, 0xee, 0xfa, 0xa5, 0x9e, 0x0a, 0x6c, 0xa8};
string message = "Hello World";
vector<unsigned char> data = vector<unsigned char>(message.begin(), message.end());
cout << "Signature: " << to_hex(signature) << endl;
cout << "Message: " << message << endl;
test4(signature, data);
}
After executing that code, i'm getting NTE_BAD_SIGNATURE
error
I used PEM import from Load an PEM encoded X.509 certificate into Windows CryptoAPI
I really hope someone would help me resolving that issue