HOWTO: Export/Import Plain Text Session Key Using CryptoAPI

ID: Q228786


The information in this article applies to:


SUMMARY

Sometimes it is convenient to export/import plain text session keys. However, the Microsoft Cryptographic Providers (Base and Enhanced) do not support this feature, for which both CryptExportKey() and CryptImportKey()require a valid key handle to encrypt and decrypt the session key, respectively. But, by using an "exponent-of-one" private key the same effect can be achieved to "encrypt" and "decrypt" the session key.


MORE INFORMATION

Since the exponent of the key is one, both the encryption and decryption do nothing to the plain text, and thus essentially leave the session key unencrypted.

Sample code below illustrates how to implement this feature:


#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>

// 
// Data to be encrypted.
// 
#define PLAIN_TEXT   TEXT("This is to test exponent-of-one key")

// 
// Private key with exponent of one.
// 
static BYTE PrivateKeyWithExponentOfOne[] =
{
   0x07, 0x02, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00,
   0x52, 0x53, 0x41, 0x32, 0x00, 0x02, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00, 0xAB, 0xEF, 0xFA, 0xC6,
   0x7D, 0xE8, 0xDE, 0xFB, 0x68, 0x38, 0x09, 0x92,
   0xD9, 0x42, 0x7E, 0x6B, 0x89, 0x9E, 0x21, 0xD7,
   0x52, 0x1C, 0x99, 0x3C, 0x17, 0x48, 0x4E, 0x3A,
   0x44, 0x02, 0xF2, 0xFA, 0x74, 0x57, 0xDA, 0xE4,
   0xD3, 0xC0, 0x35, 0x67, 0xFA, 0x6E, 0xDF, 0x78,
   0x4C, 0x75, 0x35, 0x1C, 0xA0, 0x74, 0x49, 0xE3,
   0x20, 0x13, 0x71, 0x35, 0x65, 0xDF, 0x12, 0x20,
   0xF5, 0xF5, 0xF5, 0xC1, 0xED, 0x5C, 0x91, 0x36,
   0x75, 0xB0, 0xA9, 0x9C, 0x04, 0xDB, 0x0C, 0x8C,
   0xBF, 0x99, 0x75, 0x13, 0x7E, 0x87, 0x80, 0x4B,
   0x71, 0x94, 0xB8, 0x00, 0xA0, 0x7D, 0xB7, 0x53,
   0xDD, 0x20, 0x63, 0xEE, 0xF7, 0x83, 0x41, 0xFE,
   0x16, 0xA7, 0x6E, 0xDF, 0x21, 0x7D, 0x76, 0xC0,
   0x85, 0xD5, 0x65, 0x7F, 0x00, 0x23, 0x57, 0x45,
   0x52, 0x02, 0x9D, 0xEA, 0x69, 0xAC, 0x1F, 0xFD,
   0x3F, 0x8C, 0x4A, 0xD0,

   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   0x64, 0xD5, 0xAA, 0xB1,
   0xA6, 0x03, 0x18, 0x92, 0x03, 0xAA, 0x31, 0x2E,
   0x48, 0x4B, 0x65, 0x20, 0x99, 0xCD, 0xC6, 0x0C,
   0x15, 0x0C, 0xBF, 0x3E, 0xFF, 0x78, 0x95, 0x67,
   0xB1, 0x74, 0x5B, 0x60,

   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

extern "C" int __cdecl _tmain (int argc, _TCHAR  * argv[])
{
   DWORD       dwResult= 0;  
   HCRYPTPROV  hProv = 0;
   HCRYPTKEY   hKey = 0;
   HCRYPTKEY   hSessionKey = 0;
   BYTE        *pbBlob = NULL;
   DWORD       cbBlob;
   BYTE        byData[] = PLAIN_TEXT;
   DWORD       cbData = sizeof(byData);
   DWORD       dwIndex;

   __try
   {
      // 
      // Acquire context for default user.
      // 
      if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, 0))
      {
         dwResult = GetLastError();
         if (dwResult == NTE_BAD_KEYSET)
         {
            if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
            {
               dwResult = GetLastError();
               _tprintf(TEXT("Error [0x%x]: CryptAcquireContext() failed.\n"), dwResult);
               __leave;
            }
         }
         else
         {
            dwResult = GetLastError();
            __leave;
         }
      }

      // 
      // Import the exponent-of-one private key.
      // 
      if (!CryptImportKey(hProv, 
                          PrivateKeyWithExponentOfOne,
                          sizeof(PrivateKeyWithExponentOfOne),
                          0, 0, &hKey))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptImportKey() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Generate exportable session key.
      // 
      if (!CryptGenKey(hProv, CALG_RC4, CRYPT_EXPORTABLE, &hSessionKey))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptGenKey() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Display plain text.
      // 
      _tprintf(TEXT("Original text = [%s]\n"), byData);

      // 
      // Encrypt the data.
      // 
      if (!CryptEncrypt(hSessionKey, 0, TRUE, 0, byData, &cbData, cbData))
      {
         dwResult = GetLastError();
        _tprintf(TEXT("Error [0x%x]: CryptEncrypt() failed.\n"), dwResult);
        __leave;
      }

      // 
      // Display encrypted text.
      // 
      _tprintf(TEXT("Encrypted text = ["));
      for (dwIndex = 0; dwIndex < cbData; dwIndex++)
      {
         _tprintf(TEXT("%c"), byData[dwIndex]);
      }
      _tprintf(TEXT("]\n"));

      // 
      // Determine key blob size.
      // 
      if (!CryptExportKey(hSessionKey, hKey, SIMPLEBLOB, 0, NULL, &cbBlob))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptExportKey() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Allocate memory for key blob.
      // 
      if ((pbBlob = (BYTE *) LocalAlloc(LMEM_ZEROINIT, cbBlob)) == NULL)
      {
         dwResult = ERROR_NOT_ENOUGH_MEMORY;
         _tprintf(TEXT("Error [0x%x]: LocalAlloc() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Export the session key. Resultant blob is the raw session key.
      // 
      if (!CryptExportKey(hSessionKey, hKey, SIMPLEBLOB, 0, pbBlob, &cbBlob))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptExportKey() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Destroy the session key.
      // 
      CryptDestroyKey(hSessionKey);
      hSessionKey = 0;

      // 
      // Import the session key back to decrypt the data.
      // 
      if (!CryptImportKey(hProv, pbBlob, cbBlob, 0, 0, &hSessionKey))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptImportKey() failed.\n"), dwResult);
         __leave;
      }

      // 
      // Decrypt the data.
      // 
      if (!CryptDecrypt(hSessionKey, 0, TRUE, 0, byData, &cbData))
      {
         dwResult = GetLastError();
         _tprintf(TEXT("Error [0x%x]: CryptDecrypt() failed.\n"), dwResult);
         __leave;
      }
    
      // 
      // Make sure we got all the data back.
      // 
      if (cbData != sizeof(byData) || _tcscmp((const char *) byData, PLAIN_TEXT) != 0)
      {
         _tprintf(TEXT("Error: Data was not decrypted correctly.\n"));
         __leave;
      }
    
      // 
      // Display decrypted text.
      // 
      _tprintf(TEXT("Decrypted text = [%s]\n"), byData);
   }

   __finally
   {
      // 
      // Release all allocated resources.
      // 
      if (pbBlob)
         LocalFree(pbBlob);

      if (hSessionKey)
         CryptDestroyKey(hSessionKey);

      if (hKey)
         CryptDestroyKey(hKey);

      if (hProv)
         CryptReleaseContext(hProv, 0);
   }

    return (int) dwResult;
} 


REFERENCES

Please see MSDN online document for more information on the APIs used in the sample.

Additional query words: CryptExportKey, CryptImportKey, Plain text session key, Private key with exponent of one, SIMPLEBLOB.


Keywords          : kbAPI kbCrypt kbKernBase kbSecurity 
Version           : winnt:4.0
Platform          : winnt 
Issue type        : kbhowto 

Last Reviewed: May 7, 1999