Crypto++ Holds the Key to Encrypting Your C++ Application Data

Every programmer knows that the early development of the programmable electronic computer was largely spurred by the crypto-war between the British intelligence services and the German navy in WWII. Less known is that the infamous German Enigma cryptography machines were just a modification of off-the-shelf equipment used by banks in the mid-1930s to encode sensitive data about wire transfers.

Nowadays, the need to encode sensitive information is more important than ever. Consider devices such as USB thumb drives, which can store an entire CD's worth of data in a package the size of a cigarette lighter. Can your data ever be safe if it can all be downloaded in a few clicks and then carried out the door? Encryption of your data gives you one more level of security beyond merely the physical. The good news is that you can encrypt regularly and painlessly with a C++ class library such as Crypto++.

Crypto++: Many Algorithms for Many Platforms

The Crypto++ library is unabashedly a best-of-breed creature: rather than re-implement many popular schemes, the Crypto++ project seeks to integrate them under a uniform framework based on simple abstract base classes. A complete list of features and ciphers would be too long to include here, but the following are a few I found important:

  • Symmetric block ciphers: including Advanced Encryption Standard (AES), Triple-DES, Blowfish, Skipjack, and many more
  • Generic cipher modes: ECB, CBC, CBC ciphertext stealing (CTS), CFB, OFB, counter mode (CTR)
  • Stream cipher modes: Panama, ARC4, SEAL, WAKE, WAKE-OFB, BlumBlumShub
  • Public-key cryptography: including RSA, DSA, ElGamal, Nyberg-Rueppel (NR), Rabin, Rabin-Williams (RW), and many others
  • Elliptic curve cryptography: doesn't rely on large random prime numbers
  • One-way hash functions: such as SHA-1, MD5, RIPEMD, Panama, Whirlpool
  • Message Authentication Codes (MAC): based on MD5, HMAC, XOR, CBC, and others
  • Gzip: compression/decompression built-in
  • Prime number generation and verification
  • OS-independent wrappers: for timers, sockets, named pipes, random number generation, and crypto APIs

Crypto++ supports a wide variety of platforms, including Microsoft Visual C++ version 6.0, 7.0, 7.1, and 8.0, GCC 3.X and 4.0 for Unix and Windows, MacOS X, and Sun Solaris systems, to name a few. A Visual Studio .NET project file was included in Crypto++ 5.2.1 and imported into Visual Studio .NET 2003 without any problems. The project file creates cryptopp.dll, which is about 1.7MB in a DEBUG build. You may add #define CRYPTOPP_DEFAULT_NO_DLL to use a static library implementation rather than a DLL interface, if you desire.

Unlike many such libraries, the Crypto++ distro doesn't include a lot of convoluted scripts and makefiles. In general, you can simply recompile the source code and make your own library.

Hello, Crypto++ Basic Encryption/Decryption

For a "Hello World" example, I wanted to build the simplest command-line-driven program that could encrypt and decrypt files. Basically, you pass it the following three files on the command line:

  1. The original file to encrypt
  2. The filename for encrypted output
  3. The filename for the result of roundtrip encryption/decryption

The program then prompts for a passphrase, which is used during the encryption/decryption. Let me dissect the code line-by-line to see how it's done.

 1 // testcrypto.cpp
 2
 3 #define CRYPTOPP_DEFAULT_NO_DLL
 4 #include "dll.h"
 5 #include "default.h"
 6 #include <iostream>
 7
 8 #ifdef CRYPTOPP_WIN32_AVAILABLE
 9 #include <windows.h>
10 #endif
11
12 USING_NAMESPACE(CryptoPP)
13 USING_NAMESPACE(std)
14
15 const int MAX_PHRASE_LENGTH=250;
16
17 void EncryptFile(const char *in,
                    const char *out,
                    const char *passPhrase);
18 void DecryptFile(const char *in,
                    const char *out,
                    const char *passPhrase);
19
20
21 int CRYPTOPP_CDECL main(int argc, char *argv[])
22 {
23    try
24    {
25       char passPhrase[MAX_PHRASE_LENGTH];
26       cout << "Passphrase: ";
27       cin.getline(passPhrase, MAX_PHRASE_LENGTH);
28       EncryptFile(argv[1], argv[2], passPhrase);
29       DecryptFile(argv[2], argv[3], passPhrase);
30    }
31    catch(CryptoPP::Exception &e)
32    {
33       cout << "\nCryptoPP::Exception caught: "
              << e.what() << endl;
34       return -1;
35    }
36    catch(std::exception &e)
37    {
38       cout << "\nstd::exception caught: " << e.what() << endl;
39       return -2;
40    }
41 }
42
43
44 void EncryptFile(const char *in,
                    const char *out,
                    const char *passPhrase)
45 {
46    FileSource f(in, true, new DefaultEncryptorWithMAC(passPhrase,
                   new FileSink(out)));
47 }
48
49 void DecryptFile(const char *in,
                    const char *out,
                    const char *passPhrase)
50 {
51    FileSource f(in, true,
         new DefaultDecryptorWithMAC(passPhrase, new FileSink(out)));
52 }
53
54 RandomPool & GlobalRNG()
55 {
56    static RandomPool randomPool;
57    return randomPool;
58 }
59 int (*AdhocTest)(int argc, char *argv[]) = NULL;

Crypto++ Holds the Key to Encrypting Your C++ Application Data

To get the ball rolling, you only need to include the two Crypto++ header files, dll.h and default.h (see lines #3-4). The USING_NAMESPACE() macro allows Crypto++ to work around old compilers whose namespace handling is broken (see lines #12-13). Getting into the main() function, you see that all the code is wrapped in a try/catch block. You can expect Crypto++ API failures to result in throwing CryptoPP::Exception, and you also trap std::exception for good measure.

The passphrase is a string of up to 250 characters that you will use to encrypt and later decrypt the file. Now, step inside the little application functions EncryptFile() and DecryptFile() to see how it's really done (lines #44-52):

FileSource f(in, true,
             new DefaultEncryptorWithMAC(passPhrase,
                                         new FileSink(out)));

In C++ fashion, the FileSource constructor does all the work and then as EncryptFile() returns, the destructor cleans up after it. I don't think you can get any more object-oriented than this design—at least as far as declarative coding goes. The following are the four possible arguments used in this constructor:

FileSource (const char *filename,    // Our input filename line
            bool pumpAll,            // "drain" everything at once?
            BufferedTransformation *attachment=NULL,
            bool binary=true)        // Assume non-text data

In this case, you let binary mode default to true and create your own BufferedTransformation object with a new DefaultEncryptorWithMAC() call. A BufferedTransformation is an object that takes a stream of bytes as input (this may be done in stages), does some computation on them, and then places the result into an internal buffer for later retrieval. Any partial result already in the output buffer is not modified by further input. (A demonstration of blocking I/O is outside the immediate scope of this article.)

Now, take a closer look at the third argument in the FileSource constructor back on line #46:

...  new DefaultEncryptorWithMAC(passPhrase, new FileSink(out)));

Finally, you get to see the use of the passPhrase as sent down to the default encryptor, and you create a FileSink object that will open the output file and manage the buffering to the output file. Again, you could take manual control of the I/O buffering for performance or other reasons. At the end, you have a BufferedTransformation object with an encryptor and its corresponding Message Authentication Code as derived from the key.

To test the encryption/decryption, input the Constitution of the United States as the input file, store the intermediate encrypted file, and decrypt to test it as follows:

D:\crypto++>testcrypto.exe constitution.txt encrypt.txt
                           constitution2.txt Passphrase: Meathead

D:\temp\crypto++>dir *.txt
Directory of D:\temp\crypto++

04/06/2006  02:48 PM        28,365 constitution.txt
05/02/2006  03:54 PM        28,365 constitution2.txt
05/02/2006  03:54 PM        28,408 encrypt.txt

As you can see from the above test (and my own diffs later), the input and output files were identical and the intermediate file was precisely 43 bytes larger than the input or output. I would show you what the encrypted file looked like, but it is just so much random-looking data at that point.

Sign and Verify a File with RSA

Another easy-to-implement Crypto++ task is RSA signing and verification of files. RSA was named after its inventors Rivest, Shamir, and Adleman in 1977. RSA involves two keys: public key and private key (a key is a constant number later used in the encryption formula.) The public key is known to everyone and is used to encrypt messages. These messages can be decrypted only by use of the private key. In other words, anybody can encrypt a message, but only the holder of a private key can actually decrypt and read it.

RSA is also often used for signing messages. The most common example of this is protecting software license files from tampering while still keeping them human readable. In this case, the signature data is embedded as a hexadecimal string inside the license file. The function RSASignFile() in the following code takes a private key file and a message to be signed, and then writes the RSA signature to the final named file. To verify the file with RSAVerify(), you must supply the public key file, the message again, and its signature:

void RSASignFile(const char *privFilename,
                 const char *messageFilename,
                 const char *signatureFilename)
{
   FileSource privFile(privFilename, true, new HexDecoder);
   RSASSA_PKCS1v15_SHA_Signer priv(privFile);
   FileSource f(messageFilename, true, new SignerFilter(GlobalRNG(),
                priv, new HexEncoder(new FileSink(signatureFilename))));
}

bool RSAVerifyFile(const char *pubFilename,
                   const char *messageFilename,
                   const char *signatureFilename)
{
   FileSource pubFile(pubFilename, true, new HexDecoder);
   RSASSA_PKCS1v15_SHA_Verifier pub(pubFile)

   FileSource signatureFile(signatureFilename, true, new HexDecoder);
   if (signatureFile.MaxRetrievable() != pub.SignatureLength())
       return false;
       SecByteBlock signature(pub.SignatureLength());
   signatureFile.Get(signature, signature.size());

   VerifierFilter *verifierFilter = new VerifierFilter(pub);
   verifierFilter->Put(signature, pub.SignatureLength());
   FileSource f(messageFilename, true, verifierFilter);

   return verifierFilter->GetLastResult();
}

Crypto for Data Security

Beyond the obvious financial and military applications, cryptography can be used to store any information you would prefer not to leave accessible to everyone and everything. With seven million Americans reporting identity theft every year, any options you can give your users to increase their data security will surely make them breathe easier.

This article has barely scratched the surface of what Crypto++ can do for you. Perhaps its best aspect is that it can keep up with your needs for more sophisticated algorithms, ciphers, and compression, and thus provide a stable vehicle for your app to enter crypto-land.

About the Author

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries; just drop an e-mail to sysop@HAL9K.com.



About the Author

Victor Volkman

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries, just drop an e-mail to sysop@HAL9K.com.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • With the average hard drive now averaging one terabyte in size, the fallout from the explosion of user-created data has become an overwhelming volume of potential evidence that law-enforcement and corporate investigators spend countless hours examining. Join Us and SANS' Rob Lee for our 45-minute webinar, A Triage and Collection Strategy for Time-Sensitive Investigations, will demonstrate how to: Identify the folders and files that often contain key insights Reduce the time spent sifting through content by …

Most Popular Programming Stories

More for Developers

RSS Feeds