Using AES encryption in C++

published at 26.11.2014 12:22 by Jens Weller
Save to Instapaper Pocket

When it comes to encryption, there a few options you have in C++, a few years ago I implemented an RSA encryption based on the OpenSSL APIs, which was not too pleasant, but worked. For my current project, I was looking for something else, as I can add any dependency to my project. So this blog post is a short example of how to use AES with crypto++.

The feedback from this blog post has shown, that this is not a very secure option, a very good alternative to this is libsodium, which offers good apis for encryption. I will later post a libsodium based interface.

When looking for an encryption library in C++, there are at least 3 well known alternatives: crypto++, botan, and QCA. The last option is based on Qt, which in this case is for me not an option: I already use Qt a lot, but don't see the need in this case. For my current use case, I am using AES, as I need symmetric encryption, aka a way to encrypt data based on a password, and not a public key. Neither the C++ Standard nor boost provides a library, so with Qt not being an option, its crypto++ or botan.

Both, crypto++ and botan are fairly easy to use, and seem to have similar designs. I ended up using crypto++, but botan seems to be as good. Also note, that I am not a crypto expert, you need to read into this before using. One current flaw that I see in my implementation, is that the passwords are only 32 bytes at max. Also, be aware that when you need to encrypt a pipe/datastream/socket, there are better options.

Also, as botan and crypto++ predate C++11, they are written in a mildy modern C++.

A basic AESEncryption class

Currently, all I want is to encrypt strings with a certain password. So I need a type which gets initialized with a password and the interface is a fairly simple encode/decode function which takes a string that is en- or decoded:

typedef std::string bytearray;

class AESEncryption
{
    bytearray pw,iv_encrypt,iv_decrypt;
    std::string error;
    std::unique_ptr<CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption> encrypt;
    std::unique_ptr<CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption> decrypt;

public:
    AESEncryption(const bytearray& password, const bytearray& iv);
    bool encode(bytearray &text);
    bool decode(bytearray &cipher);
    std::string getError(){return error;}
};

The interface is a bit more complex then just two functions and a constructor. The objects needed for en- or decryption are held in unique_ptrs, and only instantiated, when needed. (Maybe I just want to decrypt some data as a export method, or import data with this object?). Using pimpl could make this a bit more cleaner, so that the headers from crypto++ will not leak into any code using this class. As the encoding/decoding shows, I decided to go with the CFB mode of AES.

The implementation of encode is then pretty simple:

try
{
    if(!encrypt)
        encrypt.reset(new CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption);
    CryptoPP::MD5 hash;
    byte digest[ CryptoPP::MD5::DIGESTSIZE ];
    std::string message = iv_encrypt + pw;
    hash.CalculateDigest( digest, reinterpret_cast<unsigned char*>(&message[0]), message.size() );
    iv_encrypt = std::string(reinterpret_cast< char*>(digest),16);
    encrypt->SetKeyWithIV(reinterpret_cast<unsigned char*>(&pw[0]),pw.size(),digest);
    encrypt->ProcessData(reinterpret_cast<unsigned char*>(&text[0]),reinterpret_cast<unsigned char*>(&text[0]),text.size());
}
catch(CryptoPP::Exception& e)
{
    error = e.what();
    return false;
}
return true;

I construct the encrypt object once, then the InitializationVector(iv) of the AES Algorithm needs to be constructed. It has the size of the AES Block: 16 bytes. As I don't share this with the decoding at any point, it needs to depend on the password too. Otherwise, it would be wise not to do this. I've chosen to go with the MD5 checksum, as it gives me a fairly distributed 16 byte hash.

The key and iv is then set with the SetKeyWithIV method, before the actual encryption happens in process data. All buffers handled here are unsigned char, so that the content of the std::string needs to be casted via reinterpret_cast. This is only safe from C++11 on, as it guarantees std::string to be an array. Also crypto++ does throw exception, as this class will be used with Qt, I decided to catch the exceptions, and return the state of success from the function.

The decode method is nearly the same, except that it needs to construct a Decryption object:

if(!decrypt)
     decrypt.reset(new CryptoPP::CFB_Mode< CryptoPP::AES >::Decryption);

And this is already the whole example on how to use AES in C++, most work is done by crypto++.

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!