Dr.Luiji">
Click here to Skip to main content
15,394,707 members
Articles / Programming Languages / C++
Article
Posted 8 May 2007

Stats

126.7K views
2.6K downloads
41 bookmarked

Simple Way to Crypt a File with CNG

Rate me:
Please Sign up or sign in to vote.
4.91/5 (36 votes)
9 May 2007CPOL5 min read
Cryptography API: The Next Generation (CNG) - How to crypt documents with C++ programming (without an understanding of cryptography or security)

Screenshot - CNGCryptFileSample1.gif

Contents

Introduction

This article explains some Cryptography API Next Generation (CNG) functions. This article is written under Windows Vista Business It's using Visual Studio C++ 2005 (Standard edition Sp1), Windows SDK and CNG SDK.

It's useful to:

  • save documents in a secure environment, especially over non-secure media such as the Internet
  • encrypt any kind of file, image, mp3 or document
  • create the product key of a software (with some adjustment)

Note: CNG is currently supported only on Windows Vista and it cannot be used with VB or C#. In order to build Windows applications with Visual Studio, you will need the Windows Vista SDK, which provides the documentation, samples, header files, libraries, and tools you need to develop applications that run on Windows. The CNG SDK is now available for download at the Microsoft Download Center.

Background

The basic idea is to encrypt using a CNG a file with a simple GUI. The 3 steps necessary are:

  • select Cryptographic Operations
  • select the original file and the file to encrypt
  • select the encryption key

The Application

I have created the project as an MFC application. The application has a single interface wherein you choose: the file you want to encrypt/decrypt, the operation methods (encrypt/decrypt) and the password. In the main dialog, you find a listbox; it catalogs the errors that occur as well as other useful information.

Set Up Visual Studio 2005

To use this class in Visual C++ 2005, you must add to the project properties thus:

  1. At C++ General in the "Additional Include Directories," add this path: C:\Program Files\Microsoft CNG Development Kit\Include.

    C++ General

  2. At "Linker General" in the "Additional Library Directories," add this path: C:\Program Files\Microsoft CNG Development Kit\Lib\X86.

    Linker General

  3. At "Linker Input," "Additional Dependencies", add bcrypt.lib.

    Linker Input

Using the Code

I created the class CMyCNGCryptFile that uses CNG. It has 3 public methods:

  • EnumProviders - enumerate the registered providers
  • CryptFile - encrypt or decrypt an input file
  • GetLastError - return the last error that occurred in CryptFile or EnumProviders

The Use of Cryptography

The steps involved in this application are:

  1. Open the algorithm provider
  2. Create or import a key
  3. Get and set algorithm properties
  4. Perform cryptographic operations
  5. Close the algorithm provider

The CNG API

To open the algorithm provider:

  • BCryptOpenAlgorithmProvider

To import a key:

  • BCryptGenerateSymmetricKey

To create a key:

  • BCryptCreateHash
  • BCryptHashData
  • BCryptFinishHash
  • BCryptGenerateSymmetricKey

To get the algorithm properties:

  • BCryptGetProperty

To set the algorithm properties:

  • BCryptSetProperty

To perform cryptographic operations:

  • BCryptEncrypt
  • BCryptDecrypt

To enumerate the providers:

  • BCryptEnumRegisteredProviders

To close the algorithm provider:

  • BCryptCloseAlgorithmProvider

To destroy the key:

  • BCryptDestroyKey

To destroy the hash:

  • BCryptDestroyHash

The Use of CMyCNGCryptFile

CryptFile

C++
bool CryptFile(bool bEncrypt, CString sFileToOpen,CString sFileToCrypt,CString sKey)

This is the main method called from the Dlg. It takes the Operation to perform, the InputFile, the OutputFile and the CryptKey. The steps involved are:

  1. Open the algorithm provider with OpenMSPrimitiveProviderAES
  2. Create a key with CreateSymmetricKey_AES_CBC, or import a key with CreateSymmetricKey_SHA1_Hash
  3. Retrieve the buffer from file in
  4. Perform cryptographic operations with Crypt for the intermediate byte and CryptLastByte for the last byte
  5. Save crypt data to file out

OpenMSPrimitiveProviderAES

This method opens a handle to the AES provider.

C++
bool CMyCNGCryptFile::OpenMSPrimitiveProviderAES()
{
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    ntStatus  = BCryptOpenAlgorithmProvider( &m_hAesAlg, 
        BCRYPT_AES_ALGORITHM, NULL, 0);
    switch (ntStatus)
    {
    case STATUS_SUCCESS:
        return true;
    case STATUS_INVALID_PARAMETER:
    case STATUS_NO_MEMORY:
    default:
    //... Check and log the error ...        
    }
    return false;
}

CreateSymmetricKey_AES_CBC

This method retrieves a key stored in the program as a static constant BYTE variable called rgbAES128Key. First, I get the property of the algorithm with BCryptGetProperty. Then, I use the algorithm provider handle to obtain implementation details for the algorithm, such as the key size and IV size. Next, I allocate it in the heap and modify the properties of the algorithm with BCryptSetProperty. Here, I want to use BCRYPT_CHAIN_MODE_CBC block cipher chaining with AES. I set the BCRYPT_CHAINING_MODE property of an AES algorithm to BCRYPT_CHAIN_MODE_CBC. Now, I create an ephemeral key with the BCryptGenerateSymmetricKey.

C++
bool CMyCNGCryptFile::CreateSymmetricKey_AES_CBC(DWORD &cbKeyObject, 
    DWORD &cbIV )
{
    NTSTATUS    ntStatus = STATUS_UNSUCCESSFUL;
    DWORD        cbData    = 0; 

    cbKeyObject    = 0;
    cbIV  = 0;

    ntStatus = BCryptGetProperty(m_hAesAlg, BCRYPT_OBJECT_LENGTH, 
        (PBYTE)&cbKeyObject,  sizeof(DWORD), &cbData, 0);
    ...
    m_pbKeyObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbKeyObject);
    ...
    ntStatus = BCryptGetProperty( m_hAesAlg, BCRYPT_BLOCK_LENGTH, 
        (PBYTE)&cbIV, sizeof(DWORD), &cbData, 0);
    ...
    m_pbIV= (PBYTE) HeapAlloc (GetProcessHeap (), 0, cbIV);

    memcpy(m_pbIV, rgbIV, cbIV);

    ntStatus = BCryptSetProperty(m_hAesAlg, BCRYPT_CHAINING_MODE, 
        (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
    ... 
    ntStatus = BCryptGenerateSymmetricKey(m_hAesAlg, &m_hKey, m_pbKeyObject, 
        cbKeyObject, (PBYTE)rgbAES128Key, sizeof(rgbAES128Key), 0);
    ... 
    return true;
}

CreateSymmetricKey_SHA1_Hash

This method retrieves a key from the user. First, I open a new algorithm SHA1 with BCryptOpenAlgorithmProvider. I use SHA1 because the provider supports the hash interface used later. Then I get the property of the algorithm with BCryptGetProperty. I use the algorithm provider handle to obtain implementation details for the algorithm, such as the key size and hash size. After this, I allocate it in the heap and create the hash object for the key with BCryptCreateHash. Next, I use the BCryptHashData function to perform a one-way hash on a data buffer. I then retrieve the hash value for the data accumulated from prior calls to BCryptHashData. To do this, I use the BCryptFinishHash. Now I modify the properties of the algorithm with BCryptSetProperty. Here, I want to use BCRYPT_CHAIN_MODE_CBC block cipher chaining with AES. I set the BCRYPT_CHAINING_MODE property of an AES algorithm to BCRYPT_CHAIN_MODE_CBC. Finally, I create an ephemeral key with the BCryptGenerateSymmetricKey.

C++
bool CMyCNGCryptFile::CreateSymmetricKey_SHA1_Hash(PCWSTR pwszText, 
    DWORD cbKeyObject)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    BCRYPT_KEY_HANDLE    hKey = NULL;
    
    DWORD               cbHashObject, cbResult;
    BYTE                rgbHash[20];
    DWORD                cbData    = 0; 

    ntStatus = BCryptOpenAlgorithmProvider(&m_hHashAlg,
                     BCRYPT_SHA1_ALGORITHM,NULL,0);
    ... 
    ntStatus = BCryptGetProperty(m_hAesAlg, BCRYPT_OBJECT_LENGTH,
                    (PBYTE)&cbKeyObject,  sizeof(DWORD), &cbData, 0);
    ...    
    ntStatus = BCryptGetProperty( m_hHashAlg,BCRYPT_OBJECT_LENGTH,
                    (PBYTE) &cbHashObject,sizeof(DWORD),&cbResult,0);
    ...
    ntStatus = BCryptCreateHash(m_hHashAlg, &m_hHash, m_pbHashObject,
                    cbHashObject, NULL, 0, 0 );

    ntStatus = BCryptHashData( m_hHash,  (PBYTE)pwszText, (ULONG)wcslen(
        pwszText), 0);

    ntStatus = BCryptFinishHash( m_hHash, rgbHash, sizeof(rgbHash), 0);
    ...
    ntStatus = BCryptGenerateSymmetricKey( m_hAesAlg, &hKey,  m_pbKeyObject, 
                cbKeyObject, rgbHash, SYMM_KEY_SIZE_SECRET,  0  );
    ...
    return true;
}

Crypt

This method performs cryptographic operations, such as encrypting or decrypting data, by using the BCryptEncrypt or BCryptDecrypt functions. I use this method to encrypt data with the same length of ciphertext. If the data must encrypt, I use BCryptEncrypt, else I use BCryptEncrypt.

C++
bool CMyCNGCryptFile::Crypt(bool bEncrypt,PUCHAR pbufFileToOpen, 
    ULONG iBytesRead, ULONG cbIV, PBYTE pbufFileToSave, DWORD& iBufToSave)
{    
    NTSTATUS ntStatus =STATUS_UNSUCCESSFUL;    
    DWORD        cbCipherText        = 0;

    if ( bEncrypt )                     
        ntStatus = BCryptEncrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL,
                   m_pbIV, cbIV, pbufFileToSave, iBytesRead, &iBufToSave, 0);

    else        
        ntStatus = BCryptDecrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL,
                   m_pbIV, cbIV, pbufFileToSave, iBytesRead, &iBufToSave,  0);

    ...  
    return false;
}

CryptLastByte

I use this method to encrypt data with different lengths of ciphertext. I call BCryptEncrypt or BCryptDecrypt twice. The first time, I obtain the size of the encrypted data. The second time, I retrieve the ciphertext.

C++
bool CMyCNGCryptFile::CryptLastByte(bool bEncrypt,PUCHAR pbufFileToOpen, 
    ULONG iBytesRead, ULONG cbIV, PBYTE pbufFileToSave, DWORD& iBufToSave)
{
    NTSTATUS ntStatus= STATUS_UNSUCCESSFUL;
    DWORD        cbCipherText        = 0;

    if (bEncrypt)
    {
        ntStatus = BCryptEncrypt(m_hKey, pbufFileToOpen, iBytesRead, NULL, 
                     m_pbIV, cbIV, NULL, 0, &cbCipherText, 
                     BCRYPT_BLOCK_PADDING);
        ...
        ntStatus = BCryptEncrypt( m_hKey, pbufFileToOpen, iBytesRead, NULL, 
                     m_pbIV, cbIV, pbufFileToSave, cbCipherText, 
                     &cbCipherText,BCRYPT_BLOCK_PADDING);
        iBufToSave        =    cbCipherText;
    ...    
    }
    else
    {
        ntStatus = BCryptDecrypt( m_hKey, pbufFileToOpen, iBytesRead, NULL, 
        m_pbIV, cbIV, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING);
    ...        
        ntStatus = BCryptDecrypt( m_hKey, pbufFileToOpen, iBytesRead, NULL, 
        m_pbIV, cbIV, pbufFileToSave, cbCipherText, &cbCipherText, 
        BCRYPT_BLOCK_PADDING);
        ...
    }
    return false;
}

EnumProviders

This method retrieves the current providers installed on the machine. I call the function BCryptEnumRegisteredProviders to retrieve information about the registered providers. I enumerate the providers retrieved from pProviders, a PCRYPT_PROVIDERS structure.

C++
bool CMyCNGCryptFile::EnumProviders(CStringList *lstRegisteredProviders)
{
    ...
    ntStatus = BCryptEnumRegisteredProviders(&cbBuffer, &pProviders);
    ...    
    for ( DWORD i = 0; i < pProviders->cProviders; i++)
    {
        sProvider.Format(_T("%s\n"), 
        pProviders->rgpszProviders[i]);
        lstRegisteredProviders->AddHead(sProvider);
    } 
    
    if (pProviders != NULL)
    {
        BCryptFreeBuffer(pProviders);
    }
    return true;
}

~CMyCNGCryptFile

The destructor closes the algorithm provider, deletes all of the pointers stored for memory leaks and destroys the key hash. I call the function BCryptCloseAlgorithmProvider to close the algorithm provider. I call the function BCryptDestroyKey to destroy the key and then I call the function BCryptDestroyHash destroy the hash. Finally, I enumerate the providers retrieved from pProviders, a PCRYPT_PROVIDERS structure:

C++
CMyCNGCryptFile::~CMyCNGCryptFile()
{
    BCryptCloseAlgorithmProvider(m_hAesAlg,0);
    BCryptDestroyKey(m_hKey);
    HeapFree(GetProcessHeap(), 0, m_pbKeyObject);
    HeapFree(GetProcessHeap(), 0, m_pbIV);
    //Hash
    BCryptDestroyHash(m_hHash);
    free(m_pbHashObject);
    BCryptCloseAlgorithmProvider(m_hHashAlg,0);
}

Points of Interest

To deploy the application, I learned the MSDN Cryptography API: Next Generation. I think I'll be adding Signing Data, BLOB and the provider registration information soon. Stay tuned.

History

  • 6th May, 2007 - First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dr.Luiji
Software Developer (Senior) Welcome Italia spa
Italy Italy
No Biography provided

Comments and Discussions

 
QuestionUsing CNG in Android. Pin
Amit Khobragade6-Aug-22 8:07
MemberAmit Khobragade6-Aug-22 8:07 
GeneralMy vote of 5 Pin
Tech_dog7-Nov-20 4:51
professionalTech_dog7-Nov-20 4:51 
QuestionBCryptRegisterProvider failing Pin
Member 1096297022-Jul-14 0:44
MemberMember 1096297022-Jul-14 0:44 
AnswerRe: BCryptRegisterProvider failing Pin
Dr.Luiji1-Sep-14 11:01
professionalDr.Luiji1-Sep-14 11:01 
QuestionAn issue Pin
arabadjiev19-Jan-12 8:21
Memberarabadjiev19-Jan-12 8:21 
AnswerRe: An issue Pin
Dr.Luiji20-Jan-12 10:50
professionalDr.Luiji20-Jan-12 10:50 
BugRe: An issue Pin
sanjeev.5678-Sep-18 9:43
Membersanjeev.5678-Sep-18 9:43 
Questioncan i use C# ? Pin
adam syria21-Oct-09 12:20
Memberadam syria21-Oct-09 12:20 
AnswerRe: can i use C# ? Pin
Dr.Luiji22-Oct-09 1:12
professionalDr.Luiji22-Oct-09 1:12 
Generalerror:bcrypt.dll Pin
adam syria21-Oct-09 12:18
Memberadam syria21-Oct-09 12:18 
GeneralRe: error:bcrypt.dll Pin
Dr.Luiji22-Oct-09 0:49
professionalDr.Luiji22-Oct-09 0:49 
Generalbrilliant Pin
fox_of_197823-Jun-07 1:03
Memberfox_of_197823-Jun-07 1:03 
GeneralRe: brilliant Pin
Dr.Luiji23-Jun-07 11:42
professionalDr.Luiji23-Jun-07 11:42 
GeneralNice article Pin
James Ashley27-May-07 18:15
MemberJames Ashley27-May-07 18:15 
GeneralRe: Nice article Pin
Dr.Luiji27-May-07 21:09
professionalDr.Luiji27-May-07 21:09 
GeneralRe: Nice article Pin
James Ashley28-May-07 6:18
MemberJames Ashley28-May-07 6:18 
GeneralRe: Nice article Pin
Dr.Luiji29-May-07 23:45
professionalDr.Luiji29-May-07 23:45 
GeneralRe: Nice article Pin
James Ashley30-May-07 5:24
MemberJames Ashley30-May-07 5:24 
GeneralRe: Nice article Pin
Dr.Luiji1-Jun-07 1:02
professionalDr.Luiji1-Jun-07 1:02 
GeneralGood article, nice animation Pin
PhilipDonner16-May-07 2:11
MemberPhilipDonner16-May-07 2:11 
GeneralRe: Good article, nice animation Pin
Dr.Luiji16-May-07 7:49
professionalDr.Luiji16-May-07 7:49 
GeneralVery nice Pin
mr.ahf15-May-07 5:57
Membermr.ahf15-May-07 5:57 
GeneralRe: Very nice Pin
Dr.Luiji15-May-07 8:21
professionalDr.Luiji15-May-07 8:21 
GeneralExcellent Pin
Raj Lal9-May-07 13:51
professionalRaj Lal9-May-07 13:51 
GeneralRe: Excellent Pin
Dr.Luiji9-May-07 21:11
professionalDr.Luiji9-May-07 21:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.