Windows CAPI CryptDecrypt error key not working - c++

I am trying to decrypt data from openssl generated private key. I have converted the public key from PEM to DER format, but when i use CryptDecrypt it throws error key does not exist. I have previously used the same method to encrypt data and decrypt using openssl, i am also aware regarding the Endianess difference in openssl and wincrypto. Here is the code if someone can point out where i am going wrong.
#include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
#pragma comment(lib, "Crypt32.lib")
char default_pub_key[] =
"-----BEGIN PUBLIC KEY-----"
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVTOXB/Ti8SFvP42Z1XFB6GQ+R"
"jnqs42XiTFRXWpsSTlSPMRHi8aXpf1KYzzKHMC+4hU3rrgdbOu8bl7FekDoy38No"
"PX8ACoEmRhdn8mXs+ftmIRCuEE44mtgWUme65A1nTyT8nRmAVF6roo/rry+Xkbe9"
"iC6vRBRbVzprmCv7jwIDAQAB"
"-----END PUBLIC KEY-----";
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwKeySize = 0;
void SwapBytes(char* pv, size_t n) {
char* p = pv;
size_t lo, hi;
for (lo = 0, hi = n - 1; hi > lo; lo++, hi--)
{
char tmp = p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
int init_crypto()
{
LPBYTE pbBuffer;
DWORD dwKeyBlob, dw_pub_key_len = 0;
unsigned int offset = 22; // 22 = 1024, 24 = 2048 and so on
DWORD dwParamSize = sizeof(DWORD);
CERT_PUBLIC_KEY_INFO* publicKeyInfo;
CryptStringToBinaryA(default_pub_key, 0, CRYPT_STRING_ANY, NULL, &dw_pub_key_len, NULL, NULL);
pbBuffer = (LPBYTE)GlobalAlloc(GPTR, dw_pub_key_len);
CryptStringToBinaryA(default_pub_key, 0, CRYPT_STRING_ANY, pbBuffer, &dw_pub_key_len, NULL, NULL);
dwKeyBlob = 0;
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbBuffer, dw_pub_key_len, 0, NULL, NULL, &dwKeyBlob);
publicKeyInfo = (CERT_PUBLIC_KEY_INFO*)GlobalAlloc(GPTR, dwKeyBlob);
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbBuffer, dw_pub_key_len, 0, NULL, publicKeyInfo, &dwKeyBlob);
CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey);
CryptGetKeyParam(hKey, KP_KEYLEN, (BYTE*)&dwKeySize, &dwParamSize, 0);
char da[16];
memset(da, 0, sizeof(da));
wsprintfA(da, "%d", dwKeySize);
MessageBoxA(NULL, da, NULL, MB_OK);
dwKeySize /= 8;
return 0;
}
int main(int argc, char** argv)
{
// start rsa crypto key
init_crypto();
// Read encrypted data
DWORD junk;
HANDLE hEncFile = CreateFile(L"test_enc.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD EncFileSize = GetFileSize(hEncFile, 0);
char* EncFileBuf = (char*)GlobalAlloc(GPTR, EncFileSize + 1);
ReadFile(hEncFile, EncFileBuf, EncFileSize, &junk, NULL);
CloseHandle(hEncFile);
// convert for win32
SwapBytes((char*)EncFileBuf, EncFileSize);
CryptDecrypt(hKey, 0, TRUE, 0,(PBYTE)EncFileBuf, &EncFileSize);
hEncFile = CreateFile(L"test_dec.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hEncFile, EncFileBuf, EncFileSize, &junk, NULL);
CloseHandle(hEncFile);
GlobalFree(EncFileBuf);
// Do proper cleanup
return 0;
}

CryptDecrypt
[in] hKey
A handle to the key to use for the decryption. An application obtains this handle by using either the CryptGenKey or CryptImportKey function.
You pass a wrong hKey to CryptDecrypt. Use CryptImportKey after CryptImportPublicKeyInfo for getting an expected hKey and pass it to CryptDecrypt.

Related

RegCreateKeyEx always returns 5 ERROR_ACCESS_DENIED even when admin

I'm really stuck...I have a Windows program that I'm trying to simply write a value to the Registry.
The problem is RegCreateKeyEx() returns 5 (ERROR_ACCESS_DENIED).
My logon username belongs to the local Administrators group.
The lpdwDisposition parameter in RegCreateKeyEx() gets set to 2 (REG_OPENED_EXISTING_KEY), but RegCreateKeyEx() still returns ERROR_ACCESS_DENIED.
It's a 32-bit program, so I have the SAM set to KEY_WOW64_32KEY | KEY_WRITE
Here is minimal working example code:
#include <Windows.h>
#include <time.h>
#include <strsafe.h>
void GetCurrentDateAndTimeWithMs(WCHAR *sValueData, DWORD dwSizeValueData)
{
CONST INT iSize = 64;
WCHAR sDate[iSize] = {0};
WCHAR sTime[iSize] = {0};
SYSTEMTIME lt = {0};
GetLocalTime(&lt);
GetDateFormat(LOCALE_USER_DEFAULT, NULL, &lt, NULL, sDate, iSize);
GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &lt, NULL, sTime, iSize);
StringCchPrintf(sValueData, dwSizeValueData, L"%s %s.%u",sDate, sTime, lt.wMilliseconds);
}
DWORD SaveToRegistry(CONST WCHAR *sPath, CONST WCHAR *sValueName, CONST WCHAR *sValueData)
{
LSTATUS dwRV = 0;
HKEY hKey = NULL;
DWORD rv = 0;
DWORD dwType_Local = REG_SZ;
dwRV = RegCreateKeyExW(HKEY_LOCAL_MACHINE, sPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WOW64_32KEY | KEY_WRITE, NULL, &hKey, &rv);
if (dwRV == ERROR_SUCCESS)
{
DWORD dwLenData = (DWORD) wcslen(sValueData) +1;
dwRV = RegSetValueExW(hKey, sValueName, 0, dwType_Local, (LPBYTE) sValueData, dwLenData * sizeof(WCHAR));
}
RegCloseKey(hKey);
return dwRV;
}
int main()
{
WCHAR sDT[64] = {0};
GetCurrentDateAndTimeWithMs(sDT, 64);
SaveToRegistry(L"Software\\Company\\Product\\Settings", L"CurrentDateTime", sDT);
}

AcquireCredentialsHandleA() returns 0x8009030e (No credentials are available in the security package) for PFX file

I'm trying to setup server-side encryption using SSPI. I'm successfully (as far as I can tell) loading a certificate stored as a PFX file, but the call to m_pSSPI->AcquireCredentialsHandleA() returns 0x8009030e.
This method seems to successfully load the file and return a CERT_CONTEXT object.
HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
HRESULT hr;
HANDLE hFile, hSection = NULL;
BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
PCCERT_CONTEXT (WINAPI* pfnCertEnumCertificatesInStore)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
CRYPT_DATA_BLOB blob; blob.pbData = NULL;
HCERTSTORE pfxStore = NULL;
hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
blob.cbData = GetFileSize(hFile, NULL);
hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
CheckIfGetLastError(NULL == hSection);
blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
CheckIfGetLastError(NULL == blob.pbData);
Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
Check(TGetFunction(m_hCrypt32, "CertEnumCertificatesInStore", &pfnCertEnumCertificatesInStore));
CheckIf(!pfnPFXIsPFXBlob(&blob), E_FAIL);
pfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, CRYPT_MACHINE_KEYSET | CRYPT_EXPORTABLE);
CheckIf(NULL == pfxStore, SEC_E_NO_CREDENTIALS);
*ppctxCert = pfnCertEnumCertificatesInStore(pfxStore, NULL);
CheckIf(NULL == *ppctxCert, SEC_E_NO_CREDENTIALS);
Cleanup:
if(pfxStore)
{
BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
pfnCertCloseStore(pfxStore, 0);
}
if(blob.pbData)
UnmapViewOfFile(blob.pbData);
SafeCloseHandle(hSection);
SafeCloseFileHandle(hFile);
return hr;
}
The result is immediately passed to another class method, which makes the failing AcquireCredentialsHandleA() call.
HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if(pCertContext)
{
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
}
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
if(!m_fServer)
SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO;
//
// Create an SSPI credential.
//
return m_pSSPI->AcquireCredentialsHandleA(
NULL, // Name of principal
m_fServer ? NEGOSSP_NAME_A : UNISP_NAME_A, // Name of package
m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL, // Pointer to logon ID
&SchannelCred, // Package specific data
NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey()
phCreds, // (out) Cred Handle
&tsExpiry); // (out) Lifetime (optional)
}
My CTLSPackage::AcquireCredentials() code path is also used for setting up client-side encryption, and that works. For the server-side path, m_fServer is TRUE. The m_hCrypt32 member was loaded from Crypt32.dll.
I've cobbled together this code from samples, but I must be missing something for the server case. I only need to setup SSL/TLS-style encryption, so the "No credentials are available in the security package" error is weird because I have no need for credential authentication.
Does anyone know what might be missing? Thanks!
With a hint from RbMm, I then found this article:
https://www.codeproject.com/articles/125124/how-to-use-certificate-from-disk-with-microsoft-cr
The short answer is that CryptAcquireCertificatePrivateKey() needed to be used when loading a PFX from a file, and UNISP_NAME_A needed to be passed to AcquireCredentialsHandleA().
For reference, here is the revised code:
HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
HRESULT hr;
HANDLE hFile, hSection = NULL;
BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
PCCERT_CONTEXT (WINAPI* pfnCertFindCertificateInStore)(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, const void* pvFindPara, PCCERT_CONTEXT pPrevCertContext);
BOOL (WINAPI* pfnCryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, void* pvReserved, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *phCryptProvOrNCryptKey, DWORD* pdwKeySpec, BOOL* pfCallerFreeProvOrNCryptKey);
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv;
DWORD dwKeySpec;
BOOL fFreeProv = FALSE;
CRYPT_DATA_BLOB blob; blob.pbData = NULL;
HCERTSTORE hpfxStore = NULL;
hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
blob.cbData = GetFileSize(hFile, NULL);
hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
CheckIfGetLastError(NULL == hSection);
blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
CheckIfGetLastError(NULL == blob.pbData);
Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
Check(TGetFunction(m_hCrypt32, "CertFindCertificateInStore", &pfnCertFindCertificateInStore));
Check(TGetFunction(m_hCrypt32, "CryptAcquireCertificatePrivateKey", &pfnCryptAcquireCertificatePrivateKey));
CheckIf(!pfnPFXIsPFXBlob(&blob), HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
hpfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, 0);
if(NULL == hpfxStore && pcwzPassword && L'\0' == *pcwzPassword)
{
hpfxStore = pfnPFXImportCertStore(&blob, NULL, 0);
CheckIf(NULL == hpfxStore, SEC_E_NO_CREDENTIALS);
}
*ppctxCert = pfnCertFindCertificateInStore(hpfxStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);
CheckIfGetLastError(NULL == *ppctxCert);
// Acquire the private key and make it available for the later AcquireCredentalsHandle() call.
if(!pfnCryptAcquireCertificatePrivateKey(*ppctxCert, 0, NULL, &hProv, &dwKeySpec, &fFreeProv))
{
DWORD dwError = GetLastError();
FreeCertificateContext(*ppctxCert);
*ppctxCert = NULL;
CheckWin32Error(dwError);
}
Cleanup:
if(fFreeProv)
FreeProvOrNCryptKey(hProv, dwKeySpec);
if(hpfxStore)
{
BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
pfnCertCloseStore(hpfxStore, 0);
}
if(blob.pbData)
UnmapViewOfFile(blob.pbData);
SafeCloseHandle(hSection);
SafeCloseFileHandle(hFile);
return hr;
}
HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if(pCertContext)
{
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
}
SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3 | SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
SchannelCred.dwFlags = SCH_USE_STRONG_CRYPTO;
if(!m_fServer)
SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
//
// Create an SSPI credential.
//
return m_pSSPI->AcquireCredentialsHandleA(
NULL, // Name of principal
UNISP_NAME_A, // Name of package
m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL, // Pointer to logon ID
&SchannelCred, // Package specific data
NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey()
phCreds, // (out) Cred Handle
&tsExpiry); // (out) Lifetime (optional)
}

How to create a public/private key pair to use with OpenSSL

i am trying to create a windows application which creates a public and private key and export this so that i can use this with OpenSSL.
I took same examples provided by MSDN but there is something wrong... I think its a problem of the sizes for allocating memory.
The result i need is a base64 encoded public and private key like this:
const char* szPemPrivKey =
"-----BEGIN RSA PRIVATE KEY-----"
"MIICXAIBAAKBgQCf6YAJOSBYPve1jpYDzq+w++8YVoATI/YCi/RKZaQk+l2ZfoUQ"
"g0qrYrfkzeoOa/qd5VLjTTvHEgwXnlDXMfo+vSgxosUxDOZXMTBqJGOViv5K2QBv"
"k8A1wi4k8tuo/7OWya29HvcfavUk3YXaV2YFe8V6ssaZjNcVWmDdjqNkXwIDAQAB"
"AoGALrd+ijNAOcebglT3ioE1XpUbUpbir7TPyAqvAZUUESF7er41jY9tnwgmBRgL"
"Cs+M1dgLERCdKBkjozrDDzswifFQmq6PrmYrBkFFqCoLJwepSYdWnK1gbZ/d43rR"
"2sXzSGZngscx0CxO7KZ7xUkwENGd3+lKXV7J6/vgzJ4XnkECQQDTP6zWKT7YDckk"
"We04hbhHyBuNOW068NgUUvoZdBewerR74MJx6nz28Tp+DeNvc0EveiQxsEnbV8u+"
"NRkX5y0xAkEAwcnEAGBn5kJd6SpU0ALA9XEpUv7tHTAGQYgCRbfTT59hhOq6I22A"
"ivjOCNG9c6E7EB2kcPVGuCpYUhy7XBIGjwJAK5lavKCqncDKoLwGn8HJdNcyCIWv"
"q5iFoDw37gTt1ricg2yx9PzmabkDz3xiUmBBNeFJkw/FToXiQRGIakyGIQJAJIem"
"PPPvYgZssYFbT4LVYO8d/Rk1FWVyKHQ9CWtnmADRXz7oK7l+m7PfEuaGsf9YpOcR"
"koGJ/TluQLxNzUNQnQJBAImwr/yYFenIx3HQ6UX/fCt6qpGDv0VfOLyR64MNeegx"
"o7DhNxHbFkIGzk4lKhMKcHKDrawZbdJtS9ie2geSwVQ="
"-----END RSA PRIVATE KEY-----";
const char* szPemPubKey =
"-----BEGIN RSA PUBLIC KEY-----"
"MIGJAoGBAJ/pgAk5IFg+97WOlgPOr7D77xhWgBMj9gKL9EplpCT6XZl+hRCDSqti"
"t+TN6g5r+p3lUuNNO8cSDBeeUNcx+j69KDGixTEM5lcxMGokY5WK/krZAG+TwDXC"
"LiTy26j/s5bJrb0e9x9q9STdhdpXZgV7xXqyxpmM1xVaYN2Oo2RfAgMBAAE="
"-----END RSA PUBLIC KEY-----";
https://www.idrix.fr/Root/Samples/capi_pem.cpp
Can someone correct my code or give me a hint what i do wrong?
int CreateKeys(DWORD keyLength)
{
/* variables */
HCRYPTPROV hCryptProv = NULL;
DWORD flags = keyLength /*key length*/ << 16;
flags |= CRYPT_EXPORTABLE;
DWORD size = 0;
HCRYPTKEY hKey = NULL;
/* variables public key */
HCRYPTKEY hPublicKey = NULL;
DWORD dwPublicKeyLen = 0;
BYTE* pbPublicKey = NULL;
HANDLE hPublicKeyFile = NULL;
LPBYTE pPublicBLOB = (LPBYTE)LocalAlloc(0, size);
/* variables private key */
HCRYPTKEY hPrivateKey = NULL;
DWORD dwPrivateKeyLen = 0;
BYTE* pbPrivateKey = NULL;
HANDLE hPrivateKeyFile = NULL;
LPBYTE pPrivateKeyBLOB = (LPBYTE)LocalAlloc(0, size);
/* get provider */
DWORD rc = CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
// Generate new key pair
//_tprintf(_T("CryptGenKey...\n"));
if (!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey))
{
// Error
//_tprintf(_T("CryptGenKey error 0x%x\n"), GetLastError());
return 1;
}
// Get public key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the public key
//_tprintf(_T("malloc...\n"));
if (!(pbPublicKey = (BYTE *)malloc(dwPublicKeyLen)))
{
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get public key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbPublicKey, &dwPublicKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Get private key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the private key
//_tprintf(_T("malloc...\n"));
if (!(pbPrivateKey = (BYTE *)malloc(dwPrivateKeyLen)))
{
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get private key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbPrivateKey, &dwPrivateKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
/*
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
LPBYTE pPrivKeyBLOB = (LPBYTE)LocalAlloc(0, size);
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBLOB, &size);
*/
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, NULL, &dwPrivateKeyLen);
LPBYTE pPrivateDER = (LPBYTE)LocalAlloc(0, dwPrivateKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, pPrivateDER, &dwPrivateKeyLen);
/* PEM */
DWORD pemPrivateSize = 0;
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPrivateSize);
LPSTR pPrivatePEM = (LPSTR)LocalAlloc(0, pemPrivateSize);
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPrivateSize);
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbPublicKey, 0, NULL, NULL, &dwPublicKeyLen);
LPBYTE pPublicDER = (LPBYTE)LocalAlloc(0, dwPublicKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbPublicKey, 0, NULL, pPublicDER, &dwPublicKeyLen);
/* PEM */
DWORD pemPublicSize = 0;
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPublicSize);
LPSTR pPublicPEM = (LPSTR)LocalAlloc(0, pemPublicSize);
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPublicSize);
printf("%s", pPrivatePEM);
printf("%s", pPublicPEM);
return 0;
}
it seem like you provided wrong format to the CryptEncodeObjectEx Function to get public keys
I used CryptExportPublicKeyInfo to get the right key info.
Try this code:
int CreateKeys(DWORD keyLength)
{
/* variables */
HCRYPTPROV hCryptProv = NULL;
DWORD flags = keyLength /*key length*/ << 16;
flags |= CRYPT_EXPORTABLE;
DWORD size = 0;
HCRYPTKEY hKey = NULL;
/* variables public key */
HCRYPTKEY hPublicKey = NULL;
DWORD dwPublicKeyLen = 0;
BYTE* pbPublicKey = NULL;
HANDLE hPublicKeyFile = NULL;
LPBYTE pPublicBLOB = (LPBYTE)LocalAlloc(0, size);
/* variables private key */
HCRYPTKEY hPrivateKey = NULL;
DWORD dwPrivateKeyLen = 0;
BYTE* pbPrivateKey = NULL;
HANDLE hPrivateKeyFile = NULL;
LPBYTE pPrivateKeyBLOB = (LPBYTE)LocalAlloc(0, size);
/* get provider */
DWORD rc = CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
// Generate new key pair
//_tprintf(_T("CryptGenKey...\n"));
if (!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey)) {
// Error
//_tprintf(_T("CryptGenKey error 0x%x\n"), GetLastError());
return 1;
}
// Get public key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the public key
//_tprintf(_T("malloc...\n"));
if (!(pbPublicKey = (BYTE *)malloc(dwPublicKeyLen))) {
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get public key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbPublicKey, &dwPublicKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Get private key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the private key
//_tprintf(_T("malloc...\n"));
if (!(pbPrivateKey = (BYTE *)malloc(dwPrivateKeyLen))) {
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get private key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbPrivateKey, &dwPrivateKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
/*
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
LPBYTE pPrivKeyBLOB = (LPBYTE)LocalAlloc(0, size);
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBLOB, &size);
*/
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, NULL, &dwPrivateKeyLen);
LPBYTE pPrivateDER = (LPBYTE)LocalAlloc(0, dwPrivateKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, pPrivateDER, &dwPrivateKeyLen);
/* PEM */
DWORD pemPrivateSize = 0;
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPrivateSize);
LPSTR pPrivatePEM = (LPSTR)LocalAlloc(0, pemPrivateSize);
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPrivateSize);
DWORD pkiLen = 0;
LPVOID pki = nullptr;
rc = CryptExportPublicKeyInfo(hCryptProv, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &pkiLen);
// allocate memory
pki = (LPBYTE)LocalAlloc(0, pkiLen);
rc = CryptExportPublicKeyInfo(hCryptProv, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, (PCERT_PUBLIC_KEY_INFO)pki, &pkiLen);
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pki, 0, NULL, NULL, &pkiLen);
LPBYTE pPublicDER = (LPBYTE)LocalAlloc(0, dwPublicKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pki, 0, NULL, pPublicDER, &dwPublicKeyLen);
/* PEM */
DWORD pemPublicSize = 0;
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPublicSize);
LPSTR pPublicPEM = (LPSTR)LocalAlloc(0, pemPublicSize);
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, pPublicPEM, &pemPublicSize);
printf("%s", pPrivatePEM);
printf("%s", pPublicPEM);
return 0;
}

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.

Digital signature with CryptVerifySignature

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