RyanH
RyanH

Reputation: 141

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:

  1. Acquire a context to Microsoft Enhanced Cryptographic Provider v1.0
  2. Generate a public / private key pair.
  3. Export the public and private key BLOBs to separate files.
  4. Load up the public key and encrypt some simple text.
  5. 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;
}

Upvotes: 4

Views: 4652

Answers (3)

VanGogh Gaming
VanGogh Gaming

Reputation: 466

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.

Upvotes: 2

Captain Nemo
Captain Nemo

Reputation: 262

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.

Upvotes: 3

RyanH
RyanH

Reputation: 141

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:

  1. openssl genpkey -out private2.pem -outform PEM -des3 -algorithm RSA -pkeyopt rsa_keygen_bits:2048

    or

    openssl genrsa -des3 -out private.pem 2048

  2. openssl rsa -in private.pem -outform PEM -pubout -out public.pem

  3. 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;
}

Upvotes: 0

Related Questions