title: Crypto API RSA public key can decrypt data, is not asymmetric as expected
body: <p>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.</p>
  
  <p>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 <strong>only</strong> the private key. </p>
  
  <p>I have produced a simple test class and code to reproduce my problem. The steps I'm taking are as follows:</p>
  
  <ol>
  <li>Acquire a context to Microsoft Enhanced Cryptographic Provider v1.0</li>
  <li>Generate a public / private key pair.</li>
  <li>Export the public and private key BLOBs to separate files.</li>
  <li>Load up the public key and encrypt some simple text.</li>
  <li>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).</li>
  </ol>
  
  <p>TestEncryptDecrypt helper class: TestEncryptDecrypt.h</p>
  
  <pre><code>#pragma once
  #include &lt;Windows.h&gt;
  #include &lt;wincrypt.h&gt;
  
  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(&amp;hProvider, NULL, pszProvider, dwProvType, 0);
      }
  
      BOOL Generate2048BitKeys(ALG_ID Algid)
      {
          DWORD dwFlags = (0x800 &lt;&lt; 16) | CRYPT_EXPORTABLE;
          return CryptGenKey(hProvider, Algid, dwFlags, &amp;hKey);
      }
  
      VOID ExportPrivatePublicKey(LPTSTR lpFileName)
      {
          if (hKey == NULL)
              return;
  
          DWORD dwDataLen = 0;
          BOOL exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &amp;dwDataLen);
          LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
          exportResult = CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, lpKeyBlob, &amp;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, &amp;dwDataLen);
          LPBYTE lpKeyBlob = (LPBYTE)malloc(dwDataLen);
          exportResult = CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, lpKeyBlob, &amp;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, &amp;lpKeyContent, &amp;dwDataLen);
          BOOL importResult = CryptImportKey(hProvider, lpKeyContent, dwDataLen, 0, 0, &amp;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, &amp;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, &amp;lpEncryptedData, &amp;dwDataLen);
          BOOL decryptResult = CryptDecrypt(hKey, NULL, TRUE, 0, lpEncryptedData, &amp;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, &amp;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, &amp;dwBytesRead, NULL);
  
          *dwDataLen = dwBytesRead;
  
          CloseHandle(hFile);
      }
  };
  </code></pre>
  
  <p>Test Code: Main .cpp file</p>
  
  <pre><code>#include "stdafx.h"
  #include "TestEncryptDecrypt.h"
  #include &lt;Windows.h&gt;
  #include &lt;wincrypt.h&gt;
  
  int main()
  {
      TestEncryptDecrypt *edc = new TestEncryptDecrypt();
      //Initialize the provider
      edc-&gt;InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
  
      //Generate a 2048-bit asymmetric key pair
      edc-&gt;Generate2048BitKeys(CALG_RSA_KEYX);
  
      //Export the private / public key pair
      edc-&gt;ExportPrivatePublicKey(L"privpubkey.txt");
  
      //Export only the public key
      edc-&gt;ExportPublicKey(L"pubkey.txt");
  
      //Import the public key (destroys the private/public key pair already set)
      edc-&gt;ImportKey(L"pubkey.txt");
  
      //Encrypt and write some test data to file
      edc-&gt;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-&gt;DecryptDataFromFile(&amp;lpDecryptedData, L"encrypteddata.txt", &amp;dwDataLen);
  
      //Write the supposedly decrypted data to another file
      edc-&gt;WriteBytesFile(L"decrypteddata.txt", lpDecryptedData, dwDataLen);
  
      //Clear data
      delete[] lpDecryptedData;
      delete edc;
  
      return 0;
  }
  </code></pre>
  
  <p>Unfortunately I don't get the opportunity to work with C++ very often so you may notice some problems. Feel free to constructively criticize.</p>
  
  <p>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. </p>
  
  <p><strong>Edit:</strong>
  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):</p>
  
  <pre><code>// CPPTests.cpp : Defines the entry point for the console application.
  //
  
  #include "stdafx.h"
  #include "TestEncryptDecrypt.h"
  #include &lt;Windows.h&gt;
  #include &lt;wincrypt.h&gt;
  
  int main()
  {
      TestEncryptDecrypt *edc = new TestEncryptDecrypt();
      //Initialize the provider
      edc-&gt;InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
  
      //Generate a 2048-bit asymmetric key pair
      edc-&gt;Generate2048BitKeys(CALG_RSA_KEYX);
  
      //Export the private / public key pair
      edc-&gt;ExportPrivatePublicKey(L"privpubkey.txt");
  
      //Export only the public key
      edc-&gt;ExportPublicKey(L"pubkey.txt");
  
      //Destroy everything and load up only the public key to write some encrypted data
      delete edc;
      edc = new TestEncryptDecrypt();
      edc-&gt;InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
      edc-&gt;ImportKey(L"pubkey.txt");
  
      //Encrypt and write some test data to file
      edc-&gt;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-&gt;InitializeProvider(MS_ENHANCED_PROV, PROV_RSA_FULL);
      edc-&gt;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-&gt;DecryptDataFromFile(&amp;lpDecryptedData, L"encrypteddata.txt", &amp;dwDataLen);
  
      //Write the supposedly decrypted data to another file
      edc-&gt;WriteBytesFile(L"decrypteddata.txt", lpDecryptedData, dwDataLen);
  
      //Clear data
      delete[] lpDecryptedData;
      delete edc;
  
      return 0;
  }
  </code></pre>