The Encrypt Demo: Encrypting a File

The Encrypt demo shows how to encrypt a file (passed on the command line) using the RC2 block-type encryption (which is one of the algorithms supplied with the crypto API runtime). It demonstrates getting the default user’s exchange key, generating an encrypted key, making an exportable key blob, and writing this to disk. Finally, the data from an input file is encrypted and written to an output file.

NOTE

The Encrypt demo is included on the CD that accompanies this book, in the Chapter 20 folder. However, the actual software routines that perform the encryption and decryption in the Encrypt (and Decrypt) demo are not included on the CD. These routines must be installed, either as built-in support (for Windows 98 and Windows NT systems and newer compilers) or manually, as explained in the preceding sections. Also note that if you are using a special version of Windows 98 that does not support encryption (such as the French version), you will not be able to run the Encrypt and Decrypt demos.

Let’s begin with the #define and #include statements.

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

Since we’ll be using the encryption API,  note that you’ll need to include the WinCrypt.H header file.

Next, we define the constants we’ll use to allocate the two buffers for raw and encrypted data.

const IN_BUFFER_SIZE    = 2048;
const OUT_BUFFER_SIZE   = IN_BUFFER_SIZE + 64; // extra padding

Note that the OUT_BUFFER_SIZE is slightly larger than the input buffer. This is necessary because the encrypted data could end up being, at most, one encryption block size larger than the data supplied by the input buffer.

Next, we have the main program declaration, followed by the set of variables.

int __cdecl main(int argc, char * argv[])
{
     HANDLE     hInFile, hOutFile;
     BYTE       pbBuffer[OUT_BUFFER_SIZE];

Note that because CryptEncrypt actually encrypts the data in place, we need only a single buffer (pbBuffer), which we can use both for reading the data in and for encrypting.

Here are the rest of our declared variables.

     BOOL          finished;
     HCRYPTPROV    hProvider = 0;
     HCRYPTKEY     hKey = 0, hExchangeKey = 0;
     DWORD         dwByteCount, dwBytesWritten;

We’ll need a handle to a CSP (HCRYPTPROV) and one or more handles to crypto keys (HCRYPTKEY).

In this example, we’ll need two keys: an exchange key and an exportable key. The encryption function, plus ReadFile and WriteFile, will make use of the dwByteCount and dwBytesWritten variables.

Next, if the user hasn’t supplied the correct number of parameters, we show a usage message and exit.

     if (argc != 3) {
          printf(“Usage: ENCRYPT infile outfile\n”);
          exit(0);
     }

Now, we’re ready to begin the actual steps of the file encryption. The first thing we need to do is retrieve a handle to the default CSP. Specifying NULL rather than an explicit CSP causes the current user’s default CSP to be used.

  // Get handle for the default provider (use RSA encryption).
  CryptAcquireContext(&hProvider, NULL, NULL, PROV_RSA_FULL, 0);

Our next step is to open both the input and output files.

     // Open infile and create outfile.
     HInFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, 
         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     hOutFile = CreateFile(argv[2], GENERIC_WRITE, FILE_SHARE_READ, 
         NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

We now need to retrieve the exchange key and generate a random, exportable key. The exchange key will be used to encrypt our random key into a blob, which is then saved to include in our encrypted output file.

  // Generate a random key (in “simple blob” format).
  CryptGetUserKey(hProvider, AT_KEYEXCHANGE, &hExchangeKey);
  CryptGenKey(hProvider, CALG_RC2, CRYPT_EXPORTABLE, &hKey);

Next, we create an exportable key blob with a call to CryptExportKey. Note that calling this function with a NULL for the fifth parameter causes the function to simply calculate and return the number of bytes needed for our buffer to hold the exportable key.

  // The first call to ExportKey with NULL gets the key size.
  dwByteCount=0;
  CryptExportKey(hKey, hExchangeKey, SIMPLEBLOB, 0, NULL,           
                 &dwByteCount);
  CryptExportKey(hKey, hExchangeKey, SIMPLEBLOB, 0, pbBuffer, 
                 &dwByteCount);

We now have an encrypted version of our encryption key, in “simple blob” format. This is safe to write to the output file. We first write the size of this blob, so the decryption program knows how much data to expect when loading the key blob. Then, we write out the blob itself. The data that immediately follows this blob will be the encrypted data, which we will write out next.

          // Write size of key blob, then key blob itself, to output file.
          WriteFile(hOutFile, &dwByteCount, sizeof(dwByteCount), 
                     &dwBytesWritten, NULL);
          WriteFile(hOutFile, pbBuffer, dwByteCount, &dwBytesWritten, NULL);

Now, we read in a block of data, encrypt it using CryptEncrypt, and write it out to the output file, repeating this until we reach the end of the input data. Note the third parameter to CryptEncrypt needs to be a Boolean value that tells CryptEncrypt when we’re passing it the last block of data to encrypt.

       // Now, read data in, encrypt it, and write encrypted data to output.
          do 
          {
               ReadFile(hInFile, pbBuffer, IN_BUFFER_SIZE,&dwByteCount,NULL);
               finished = (dwByteCount < IN_BUFFER_SIZE);
               CryptEncrypt(hKey, 0, finished, 0, pbBuffer, &dwByteCount,  
                         OUT_BUFFER_SIZE);
               WriteFile(hOutFile, pbBuffer, dwByteCount, &dwBytesWritten,
                         NULL);
          } while (!finished);

And we’re finished—almost. All we need to do now is clean up. We’re finished with both keys at this point, so let’s delete them.

   // Clean up: release handles, close files.
   CryptDestroyKey(hKey);
   CryptDestroyKey(hExchangeKey);

We’re finished using the CSP handle, so we must release it. We close the input and output files, and we’re finished.

   CryptReleaseContext(hProvider, 0);
   CloseHandle(hInFile);
   CloseHandle(hOutFile);
   return(0);
}

NOTE

For clarity in the code, error checking in the Encrypt and Decrypt demos is nonexistent. The only thing that is likely to go wrong is that you don’t have the default CSP or keys installed. Fortunately, these crypto API functions also return Boolean success/failure results, so you could surround the first few functions with a Check function if you need some help with debugging your crypto installation.

© 1998 SYBEX Inc. All rights reserved.