How to Sign with RSA private key by CNG - c++

1. How to sign with RSA private key? (SOLVED)
I followed this sample to sign data with private key. I only reserve hash and sign functions, and use ECDSA P-256 private key like the sample.
Here are my steps and the program work fine:
BCryptOpenAlgorithmProvider()
Hash some data
NCryptOpenStorageProvider()
NCryptCreatePersistedKey()
NCryptFinalizeKey()
NCryptSignHash()
NCryptSignHash()
I tried to sign with RSA private key, replace NCRYPT_ECDSA_P256_ALGORITHM with NCRYPT_RSA_ALGORITHM and NCRYPT_RSA_SIGN_ALGORITHM, but it failed in second NCryptSignHash() with NTE_INVALID_PARAMETER.
//create a persisted key
if (FAILED(secStatus = NCryptCreatePersistedKey(
hProv,
&hKey,
NCRYPT_RSA_ALGORITHM,
L"my RSA key",
0,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptCreatePersistedKey\n", secStatus);
goto Cleanup;
}
2. Why the program can't find the private key after first run?
The private key is created in the program, I tried to sign with X509 certificate which has private key(ECDSA P-256) in My certifacate store.
Here are my steps and the program only work in the first run. It failed in CryptAcquireCertificatePrivateKey() with NTE_BAD_KEYSET error
BCryptOpenAlgorithmProvider()
Hash some data
CertOpenStore()
CertFindCertificateInStore()
CryptAcquireCertificatePrivateKey()
NCryptSignHash()
NCryptSignHash()
// Open the certificate store.
if (!(hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
MyHandleError(const_cast<LPTSTR>("The MY store could not be opened."));
}
swprintf(wMY_SIGNER_NAME, 100, L"%hs", MY_SIGNER_NAME);
if (pSignerCert = CertFindCertificateInStore(
hCertStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
wMY_SIGNER_NAME,
NULL))
{
//_tprintf(TEXT("The signer's certificate was found.\n"));
}
else
{
MyHandleError(const_cast<LPTSTR>("Signer certificate not found."));
}
if (CryptAcquireCertificatePrivateKey(
pSignerCert,
CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, NULL,
&hKey,
&dwKeySpec,
false))
{
printf("Get Priv OK\n");
}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
printf("NTE_BAD_KEYSET\n");
}
UPDATE:
if (FAILED(secStatus = NCryptSignHash(
hKey,
0,
pbHash,
cbHash,
NULL,
0,
&cbSignature,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptSignHash1\n", secStatus);
goto Cleanup;
}
//allocate the signature buffer
pbSignature = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbSignature);
if (NULL == pbSignature)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
if (FAILED(secStatus = NCryptSignHash(
hKey,
0,
pbHash,
cbHash,
pbSignature,
cbSignature,
&cbSignature,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptSignHash2\n", secStatus);
goto Cleanup;
}
UPDATE2:
The code work after removing NCryptDeleteKey(hKey, 0). I think this API delete my EC key! But I don't know why RSA key not be deleted?

Related

C++ CNG NCrypt: Can't open persisted key from Key Storage Provider

I have two programms. One creates a persisted key and saves it to key storage provider, then signs the hash and write the sign to the regedit. Second program opens the key from provider and verifies the sign gotten from the regedit.
But my problem is in 2nd program NCryptOpenKey can't find the key in key storage provider! After hours of browsing documentation and internet I still don't know why. Please point me what I am doing wrong.
Code example:
Variables and Cleanup procedure:
NCRYPT_PROV_HANDLE hProv = NULL;
NCRYPT_KEY_HANDLE hKey = NULL;
SECURITY_STATUS secStatus = ERROR_SUCCESS;
BCRYPT_ALG_HANDLE hHashAlg = NULL,
hSignAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbSignature = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL,
pbSignature = NULL;
static const WCHAR* KEY_NAME = TEXT("MyPersistedKey");
void Cleanup()
{
if (hHashAlg)
BCryptCloseAlgorithmProvider(hHashAlg, 0);
if (hSignAlg)
BCryptCloseAlgorithmProvider(hSignAlg, 0);
if (hHash)
BCryptDestroyHash(hHash);
if (pbHashObject)
HeapFree(GetProcessHeap(), 0, pbHashObject);
if (pbHash)
HeapFree(GetProcessHeap(), 0, pbHash);
if (pbSignature)
HeapFree(GetProcessHeap(), 0, pbSignature);
if (hKey)
NCryptDeleteKey(hKey, 0);
if (hProv)
NCryptFreeObject(hProv);
}
1st program
// open handle to KSP
if (FAILED(secStatus = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) {
Cleanup();
return {};
}
// key doesn't exists. create it
if (FAILED(secStatus = NCryptCreatePersistedKey(hProv, &hKey, NCRYPT_ECDSA_P256_ALGORITHM, KEY_NAME, 0, 0))) {
Cleanup();
return {};
}
// create key on disk
if (FAILED(secStatus = NCryptFinalizeKey(hKey, 0))) {
Cleanup();
return {};
}
// get the length of the signature
if (FAILED(secStatus = NCryptSignHash(hKey, NULL, pbHash, cbHash, NULL, 0, &cbSignature, 0))) {
Cleanup();
return {};
}
// allocate the signature buffer
pbSignature = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbSignature);
if (NULL == pbSignature) {
Cleanup();
return {};
}
// sign the hash
if (FAILED(secStatus = NCryptSignHash(hKey, NULL, pbHash, cbHash, pbSignature, cbSignature, &cbSignature, 0))) {
Cleanup();
return {};
}
2nd program
// open handle to KSP
if (FAILED(secStatus = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) {
Cleanup();
return false;
}
// open key from KSP
if (FAILED(secStatus = NCryptOpenKey(hProv, &hKey, KEY_NAME, 0, 0))) {
Cleanup();
return false;
}
// verify signature with hash
status = NCryptVerifySignature(hKey, NULL, pbHash, cbHash, pbSignature, cbSignature, 0);
switch (status) {
case ERROR_SUCCESS: // hash is verifyied
Cleanup();
return true;
case NTE_BAD_SIGNATURE: // hash isn't verifyied
Cleanup();
return false;
default:
Cleanup();
return false;
}
According to NCryptVerifySignature ,
The handle of the key must be an identical key(Symmetric keys) or
the public key portion of the key pair(Asymmetric keys) used to
sign the data with the NCryptSignHash function.
And you need to export the public key. See Signing Data with CNG and SignHashWithPersistedKeys examples.
To delete the key file on disk, pass the handle to the NCryptDeleteKey function. You have deleted the key in Cleanup.

How to export EC private key as PKCS#1 or PKCS#8 format from certificate store by CNG?

I tried to export private key from certificate store by CNG API. It work fine when export RSA private key, but failed in EC private key.
The code failed in NCryptExportKey() with 0x80090029.
Is there any document from MS said: Export EC private key not support? or any sample code?
Here is my code:
NCRYPT_KEY_HANDLE hKey = NULL;
SECURITY_STATUS secStatus = ERROR_SUCCESS;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD dwKeySpec, cbData = 0, cbBlob = 0, KeyPolicy = 0;
PBYTE pbHash = NULL, pbBlob = NULL;
PCCERT_CONTEXT pSignerCert = NULL;
unsigned char *MessagePrivKey;
Struct_Return ExportMessage = { NULL, 0 };
bool bStatus;
pSignerCert = GetCert(MY_CERT_NAME);
if (!CryptAcquireCertificatePrivateKey(
pSignerCert,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
NULL,
&hKey,
&dwKeySpec,
NULL))
{
goto End;
}
if (FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
NULL,
NULL,
0,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto End;
}
pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob);
if (NULL == pbBlob)
{
wprintf(L"**** memory allocation failed\n");
goto End;
}
if (FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
NULL,
pbBlob,
cbBlob,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto End;
}
I also tried to call NCryptSetProperty() before export, but it failed with 0x8009000b.
KeyPolicy = NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG | NCRYPT_ALLOW_EXPORT_FLAG;
if (FAILED(secStatus = NCryptSetProperty(
hKey,
NCRYPT_EXPORT_POLICY_PROPERTY,
(PBYTE)&KeyPolicy,
sizeof(KeyPolicy),
NCRYPT_PERSIST_FLAG)))
{
wprintf(L"**** Error 0x%x returned by NCryptSetProperty\n", secStatus);
goto End;
}

How to use BCrypt for RSA (asymmetric encryption)

I'm trying to make simple working example of encryption and decryption with BCrypt but I'm can't get it to work because I don't understand exactly how.
From BCryptEncrypt Function, Microsoft Docs:
NTSTATUS BCryptEncrypt(
BCRYPT_KEY_HANDLE hKey,
PUCHAR pbInput,
ULONG cbInput,
VOID *pPaddingInfo,
PUCHAR pbIV,
ULONG cbIV,
PUCHAR pbOutput,
ULONG cbOutput,
ULONG *pcbResult,
ULONG dwFlags
);
I generated a simple 512bit key pair on 8gwifi.org:
string Public_Key = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJSNbUhCkU9RuY99L8kC2WRJ8TBES3WX1u9wYlANvUFU/h7lU8VNOWI8dNGCQ6UbK2ypHFom+Zm4BaG1zokwcUkCAwEAAQ==";
string Private_Key = "MIIBOgIBAAJBAJSNbUhCkU9RuY99L8kC2WRJ8TBES3WX1u9wYlANvUFU/h7lU8VNOWI8dNGCQ6UbK2ypHFom+Zm4BaG1zokwcUkCAwEAAQJAZ9bwZAl8L5jt//o/E+C0+2Cggt/Ka5nG+bpyTok8GNTyaG+Prmz/QCYdI3VuYdONdfAPm3jLwtbK9wTt1E8HAQIhAM8jg1nwjN9+nhPyFo0F+2o8y47mq1kHnCn+gqAdW8MhAiEAt5gQcCqX2Y5KbmMoqtQ+4RIEHQ8HD+fyGqxWUhVpESkCIEtylQJqgvVZCj0bnakqN6Q/lqlrTZg1FGWbZXrqlqThAiEAilt5v94Jc7Ws2AW4Rw0OmfVGzlNd4hnNNVa88r0Z4gkCIGfFy2H8pGxHxg1GKE2mSZAfpRMyjqeq119S3t/bhqY2";
string Encrypt_Me = "Hello World";
To be honest I don't understand exactly how to use this function for this situation, I tried searching a simple example but couldn't find any.
Thank you.
There is an sample here: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/007a0e26-7fc0-4079-9b63-2ad23f866836/bug-in-rsa-encryptiondecryption-using-cng?forum=windowssdk
Please note that the first byte of encrypted data should not exceed 0xb6. And there is a detailed explanation in the post.
Take encryption as an example,
First, use BCryptOpenAlgorithmProvider to load and initialize a CNG provider that specify RSA.
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
status = BCryptOpenAlgorithmProvider(&hAlgorithm,
BCRYPT_RSA_ALGORITHM,
NULL,
0);
if (!NT_SUCCESS(status)) {
printf("Failed to get algorithm provider..status : %08x\n", status);
goto cleanup;
}
Then, BCryptImportKeyPair
status = BCryptImportKeyPair(hAlgorithm,
NULL,
BCRYPT_RSAPUBLIC_BLOB,
&hKey,
PublicKey,
PublicKeySize,
BCRYPT_NO_KEY_VALIDATION);
if (!NT_SUCCESS(status)) {
printf("Failed to import Private key..status : %08x\n", status);
goto cleanup;
}
To get Encrypted Buffer Size:
status = BCryptEncrypt(hKey,
InputData,
InputDataSize,
NULL,
NULL,
0,
NULL,
0,
&EncryptedBufferSize,
0
);
if (!NT_SUCCESS(status)) {
printf("Failed to get required size of buffer..status : %08x\n", status);
goto cleanup;
}
encryptedBuffer = (PUCHAR)HeapAlloc(GetProcessHeap(), 0, encryptedBufferSize);
if (encryptedBuffer == NULL) {
printf("failed to allocate memory for blindedFEKBuffer\n");
goto cleanup;
}
Encrypte Data:
status = BCryptEncrypt(hKey,
InputData,
InputDataSize,
NULL,
NULL,
0,
encryptedBuffer,
encryptedBufferSize,
&encryptedBufferSize,
0
);
if (!NT_SUCCESS(status)) {
printf("Failed encrypt data..status : %08x\n", status);
goto cleanup;
}
cleanup:
if (hKey)
BCryptDestroyKey(hKey);
if (hAlgorithm)
BCryptCloseAlgorithmProvider(hAlgorithm, 0);

Is it possible to do a HMAC with wincrypt?

I've been trying to perform a straight forward SHA256 HMAC using wincrypt/cryptoapi/Cryptography API: Next Generation (CNG) and i'm really struggling. My target is Windows 8.
I can not find the correct methods or find any examples anywhere. I am looking to do the following in C/C++, that is demonstrated in C# below
HMAC hashMaker = new HMACSHA256(Encoding.ASCII.GetBytes("SecretKey"));
byte[] hash = hashMaker.ComputeHash(Encoding.ASCII.GetBytes("<SomeXmlData />"));
string hashStr = BitConverter.ToString(hash);
it returns the hash: B2-42-48-67-5A-B8-03-87-5B-00-D7-8C-65-5A-AE-B7-92-E3-F9-27-40-C1-01-A5-37-74-E1-65-51-9F-F6-6A.
Has anybody succeeded to perform a straight forward HMAC using the cryptoapi?
Thank you for the information Mgetz. I never knew about the BCrypt set of methods. It is a lot easier for HMAC than CryptHashData of the wincrypt/cryptoapi. From the example of using hashing using SHA256 I was able to create the HMAC code. You only need to add BCRYPT_ALG_HANDLE_HMAC_FLAG to the last parameter of BCryptOpenAlgorithmProvider and include the key in the call to BCryptCreateHash.
This is the completed code:
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
void __cdecl wmain(
int argc,
__in_ecount(argc) LPWSTR *wargv)
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL;
CONST BYTE key[] = { "SecretKey" };
CONST BYTE message[] = { "<SomeXmlData />" };
//open an algorithm handle
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA256_ALGORITHM,
NULL,
BCRYPT_ALG_HANDLE_HMAC_FLAG)))
{
wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status);
goto Cleanup;
}
//calculate the size of the buffer to hold the hash object
if (!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
goto Cleanup;
}
//allocate the hash object on the heap
pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (NULL == pbHashObject)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
//calculate the length of the hash
if (!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&cbHash,
sizeof(DWORD),
&cbData,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status);
goto Cleanup;
}
//allocate the hash buffer on the heap
pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash);
if (NULL == pbHash)
{
wprintf(L"**** memory allocation failed\n");
goto Cleanup;
}
//create a hash
if (!NT_SUCCESS(status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
(PBYTE)key,
sizeof(key)-1,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status);
goto Cleanup;
}
//hash some data
if (!NT_SUCCESS(status = BCryptHashData(
hHash,
(PBYTE)message,
sizeof(message)-1,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status);
goto Cleanup;
}
//close the hash
if (!NT_SUCCESS(status = BCryptFinishHash(
hHash,
pbHash,
cbHash,
0)))
{
wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status);
goto Cleanup;
}
printf("The hash is: ");
for (DWORD i = 0; i < cbHash; i++)
{
printf("%2.2X-", pbHash[i]);
}
Cleanup:
if (hAlg)
{
BCryptCloseAlgorithmProvider(hAlg, 0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if (pbHashObject)
{
HeapFree(GetProcessHeap(), 0, pbHashObject);
}
if (pbHash)
{
HeapFree(GetProcessHeap(), 0, pbHash);
}
};

Problems to import RSA key in other computer

I am creating a little tool for encrypt and decrypt using a pair keys (public and private keys).
I export public and private key on my computer and I can encrypt and decrypt files without problems. I have problem when I try decrypt files in other machine with the same public key.
// initializing CSP HCRYPTPROV hProv; HCRYPTKEY hKey;
if(!CryptAcquireContext(hProv, NULL, NULL, PROV_RSA_FULL, 0)){ if(GetLastError() == NTE_BAD_KEYSET){ if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)){ return FALSE; } } }
// create a pair keys if (!CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey)) return FALSE;
// public key if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, NULL, sizePublicKey)) return FALSE;
*publicKey = (BYTE *) LocalAlloc(LPTR, *sizePublicKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE;
if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, *publicKey, sizePublicKey)) return FALSE; // save public key on file
// private key if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, NULL, sizePrivateKey)) return FALSE;
*privateKey = (BYTE *) LocalAlloc(LPTR, *sizePrivateKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE;
if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, *privateKey, sizePrivateKey)) return FALSE;
PrivateKey.key = (BYTE *) LocalAlloc(LPTR, *sizePrivateKey * sizeof(DWORD)); if(*publicKey == NULL) return FALSE; // save private key on file
//I encrypt file using if(!CryptEncrypt(hKey, 0, TRUE, 0, cache, &sizeCache, BLOCK_SIZE_ENCRYPT)){
free(cache);
return FALSE; }
//To decrypt file //First import public key
CryptImportKey(hProv, publicKey, sizePublicKey, 0, 0, &hKey)
//To decrypt: if (!CryptDecrypt(hKey, 0, TRUE, 0, cache, &sizeCache)){
free(cache);
return FALSE; }
In the same computer that key ware created the application encrypt and decrypt correctly but if I try decrypt files in other computer the CryptDecrypt() failed with error 80090003 (error got by GetLastError())
Any idea? what am I doing wrong...?
How to I can export the public key to other computer?
Thanks!
Probably you are not exporting the key, just using the CSP containing the key, while you are in the same computer, the key is stored in the container where you "link" by using the cryptoapi. Once you go to other computer the container is not present, so you can not use the key.
Make sure that the Private Key is exportable.