Share via


Encrypting Content by Using CryptoAPI Functions

If you do not want to use the Active Directory Rights Management Services (AD RMS) functions to handle encryption, you can encrypt content yourself. If you are encrypting content yourself, you can encrypt the content at any time before or after you obtain a signed issuance license, as long as the encryption key you use, or will use, is passed into the DRMGetSignedIssuanceLicense function. After you have created the signed issuance license and encrypted your content, you can dispose of the key. Be sure to dispose of the key safely and completely using functions such as SecureZeroMemory.

You can encrypt your content in AES. Encryption has the following requirements:

  • The key size is 128 bits for AES.
  • Data should be encrypted in 16-byte blocks. If the data is not a multiple of 16 bytes, the data should be padded with zeros to reach a multiple of 16 bytes.
  • The only supported cipher mode is electronic code book (ECB).
  • The initialization vector should be all zeros, as shown in the following example. The initialization vector is not used because only ECB mode is allowed.

The following example shows encryption by using CryptoAPI functions. This example shows two functions: one for encrypting a file by using CryptoAPI and one for obtaining the key from the key handle that CryptoAPI uses. Note that the key size returned by the function EncryptToFile is passed back to the main function to give to DRMGetSignedIssuanceLicense.

The example creates extra padding at the end of the document. An application is expected to create a padding scheme to determine the amount of extra padding added to the clear text.

#include <windows.h>
#include <wincrypt.h>
#include <iostream>
#include <tchar.h>
 
using namespace std;

// Debugging macros.
#define HANDLE_HR(hr, failed_msg, success_msg) \
{ \
    if(FAILED(hr)) \
    { \
        cout << endl << failed_msg << endl; \
        goto e_Exit; \
    } \
    else \
    { \
        cout << endl << success_msg << endl; \
    } \
}

#define HANDLE_FAILED(failed_msg) \
{ \
    cout << endl << failed_msg << endl; \
    goto e_Exit; \
}

const int ENCRYPT_BLOCK_SIZE = 16; 

// Declaration for function defined later.
BYTE* ExtractKey(
    HCRYPTKEY hSessionKey, 
    HCRYPTPROV hCryptProv, 
    HCRYPTKEY hXchgKey);

// Function to read, encrypt, and save encrypted file in a 
// new location. Returns the encrypted key in a format used
// by DRMGetSignedIssuanceLicense and the size of the key.
HRESULT EncryptToFile(
    LPCTSTR pszInputFileNameAndPath, 
    LPCTSTR pszOutputFileNameAndPath, 
    BYTE** ppbKey, 
    ALG_ID AlgType, 
    INT *piKeyLen)
{
    HRESULT hr            =   S_OK;
    HCRYPTKEY hSessionKey   =   NULL;
    HCRYPTPROV hCryptProv   =   0;
    HCRYPTKEY hXchgKey      =   NULL;
    DWORD dwSize         =   0;

    FILE *hSource         =   NULL; 
    FILE *hDestination    =   NULL; 

    PBYTE pbBuffer        =   NULL; 
    DWORD dwBlockLen      =   0; 
    DWORD dwBufferLen     =   0; 
    DWORD dwCount         =   0; 

    BYTE  *pbKeyData      =   NULL;
    DWORD dwKeyDataLen    =   0;
    DWORD dwMode          =   CRYPT_MODE_ECB;

    // AES is the only supported algorithm type.
    if (CALG_AES != AlgType)
    {
        hr = E_FAIL;
        HANDLE_FAILED("You can use only AES for encryption.");
    }

    // Open source file for binary reading. 
    if(hSource = _tfopen(pszInputFileNameAndPath, TEXT("rb")))
    {
        cout << endl;
        cout << "The source plaintext file ";
        cout << pszInputFileNameAndPath;
        cout << " is open.";
        cout << endl;
    }
    else
    { 
        HANDLE_FAILED("Error opening source plaintext file!");
    } 

    // Open destination file for binary writing. 
    if(hDestination = _tfopen(
                            pszOutputFileNameAndPath, 
                            TEXT("wb")))
    {
        cout << endl;
        cout << "The destination file ";
        cout << pszOutputFileNameAndPath;
        cout << " is open.";
        cout << endl;
    }
    else
    {
        HANDLE_FAILED("Cannot open destination ciphertext file!");
    }

    /*
    Try to get a handle to the Microsoft Enhanced Cryptographic 
    provider. 
    */
    if(!CryptAcquireContext(
        &hCryptProv, 
        NULL, 
        MS_ENHANCED_PROV, 
        PROV_RSA_FULL, 
        0))
    {
        // Try to get a handle to the default provider. 
        if(!CryptAcquireContext(
            &hCryptProv, 
            NULL, 
            NULL, 
            PROV_RSA_FULL, 
            0))
        {
            HANDLE_FAILED("Error during CryptAcquireContext!"); 
        }
    }

    // Create a random session key. 
    if(CryptGenKey(
        hCryptProv, 
        AlgType, 
        CRYPT_EXPORTABLE,
        &hSessionKey))
    {
        cout << endl << "A session key has been created." << endl;
    } 
    else
    {
        HANDLE_FAILED("Error during CryptGenKey."); 
    }

    // RM encryption and decryption only allow 
    // ECB mode (block without feedback).

    CryptSetKeyParam(hSessionKey, KP_MODE,(BYTE*)&dwMode, NULL);

    // Get a handle to the user's exchange public key. 
    if(!CryptGetUserKey(
        hCryptProv, 
        AT_KEYEXCHANGE, 
        &hXchgKey))
    {
        // Try once to generate a new key pair, 
        // or fail if key generation is unsuccessful.
 
        if(!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, 0, &hXchgKey))
        {
            HANDLE_FAILED("Could not get the user's key pair.");
        }
    }

    // Determine the number of bytes to encrypt at one time. 
    // This must be a multiple of ENCRYPT_BLOCK_SIZE.
    // ENCRYPT_BLOCK_SIZE is set by a #define statement.
    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; 

    // Determine the block size. When a block cipher is used, 
    // it must have room for an extra block. 
    if(ENCRYPT_BLOCK_SIZE > 1) 
        dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE; 
    else 
        dwBufferLen = dwBlockLen; 
        
    // Allocate memory to hold the data buffer. 
    if(pbBuffer = new BYTE[dwBufferLen])
    {
        cout << endl;
        cout << "Memory has been allocated for the data buffer.";
        cout << endl ;
    }
    else
    { 
        HANDLE_FAILED("Out of memory."); 
    }

    // Loop through input file, encrypt the source file 
    // and write to the source file. 

    do 
    { 
        // Read up to dwBlockLen bytes from the source file. 
        dwCount = (DWORD)fread(pbBuffer, 1, dwBlockLen, hSource); 
        if(ferror(hSource))
        { 
            HANDLE_FAILED("Error reading plaintext!");
        }
        
        // Pad the last block with random numbers to make 
        // it fill the block size.
 
        if(dwCount < dwBlockLen)
        {
            for(int i = dwCount % ENCRYPT_BLOCK_SIZE; 
                i < ENCRYPT_BLOCK_SIZE; 
                i++)
            {
                cout << endl << "Padding buffer" << endl;
                pbBuffer[dwCount] = rand() % 255;
                dwCount++;
            }
        }
        // Encrypt data. 
        if(!CryptEncrypt(
            hSessionKey, 
            0, 
            false, // Do not allow extra padding to be added.
            0, 
            pbBuffer, 
            &dwCount, 
            dwBufferLen))
        { 
            HANDLE_FAILED("Error during CryptEncrypt."); 
        } 

        // Write data to the destination file. 

        fwrite(pbBuffer, 1, dwCount, hDestination); 
        if(ferror(hDestination))
        { 
            HANDLE_FAILED("Error writing ciphertext.");
        }

    } 
    while(!feof(hSource)); 

    // End the Do loop when the last block of the source 
    // file has been read, encrypted, and written to the 
    // destination file.
 
    // Extract symmetric key, needed to pass into 
    // DRMGetSignedIssuanceLicense.
    *ppbKey = ExtractKey(hSessionKey, hCryptProv, hXchgKey);

     
    // Get key length. 
    if (!CryptGetKeyParam(
        hSessionKey,
        KP_KEYLEN,
        NULL,
        &dwKeyDataLen,
        NULL)
        )
    {
        HANDLE_FAILED("Can't get key parameter length");
    }

    pbKeyData = new BYTE[dwKeyDataLen];
    dwKeyDataLen = sizeof(pbKeyData);

    CryptGetKeyParam(
        hSessionKey,
        KP_KEYLEN,
        pbKeyData,
        &dwKeyDataLen,
        NULL);

    *piKeyLen = ((int) *pbKeyData) / 8;

    delete[] pbKeyData;

e_Exit:
    if(hSource) 
        fclose(hSource); 
    if(hDestination) 
        fclose(hDestination); 
    if(pbBuffer) 
        free(pbBuffer); 
    if (hSessionKey) 
        CryptDestroyKey(hSessionKey);
    if (hXchgKey) 
        CryptDestroyKey(hXchgKey);
    if (hCryptProv) 
        CryptReleaseContext(hCryptProv, 0);

    return hr;
}


// Function to extract the symmetric key from a key handle.
// Key is in the format of 12 bytes of BLOB header, 
// then remaining bytes (usually eight) of symmetric key.
BYTE* ExtractKey(
    HCRYPTKEY hSessionKey, 
    HCRYPTPROV hCryptProv, 
    HCRYPTKEY hXchgKey)
{
    DWORD dwSize = 0;
    BYTE *pbExportedKeyBlob = NULL;
    BYTE *pbEncryptedKey = NULL;
    BYTE *pbKeyBytes = NULL;
    DWORD dwKeyLen = 0;

    // Extract the key blob from the session handle. 
    // Call twice to determine size and allocate memory.
    CryptExportKey( 
        hSessionKey, 
        hXchgKey, 
        SIMPLEBLOB, 
        0, 
        NULL, 
        &dwSize);
    if (dwSize == 0)
    {      
        HANDLE_FAILED("Cannot determine size of the exported key "
            "blob.");
    }
   
    pbExportedKeyBlob = new BYTE[dwSize];
    if (!pbExportedKeyBlob)
    {
        HANDLE_FAILED("Cannot create a variable to hold exported "
            "blob");
    }
 
    if (!CryptExportKey( 
            hSessionKey, 
            hXchgKey, 
            SIMPLEBLOB, 
            0, 
            pbExportedKeyBlob,  
            &dwSize))
    {
        HANDLE_FAILED("Cannot export the key blob.");
    }


    // Get the encrypted key. The key is located after the 
    // first 12 bytes, which are just key blob information.
    dwKeyLen = dwSize - 12;   
    pbEncryptedKey = new BYTE[dwKeyLen];

    for (UINT c = 0 ; c < dwKeyLen ; c++ )
    {
        pbEncryptedKey[c] =  pbExportedKeyBlob[c+12]; 
    }
 
    // Decrypt the key.
    if (!CryptDecrypt(hXchgKey,0, true, 0,  pbEncryptedKey, &dwKeyLen))
    {
        HANDLE_FAILED("Cannot decrypt the key.");
    }
 
    // Copy header and decrypted key into buffer in a
    // format that DRMGetSignedIssuanceLicense can use.
    pbKeyBytes = new BYTE[ dwKeyLen];
    for (c = 0; c < dwKeyLen; c++)
    {
        pbKeyBytes[c] = pbEncryptedKey[c];
    }

e_Exit:
    // Clean up.
    if (pbExportedKeyBlob != NULL)
    {
        delete [] pbExportedKeyBlob;
    }

    if (pbEncryptedKey != NULL)
    {           
        delete [] pbEncryptedKey;
    }

    return pbKeyBytes;
 }

See Also

Building a Publishing Application

Send comments about this topic to Microsoft

Build date: 3/13/2008