简体   繁体   中英

(Crypto++) How to realize media file encryption?

I am new to Crypto++ and want to "convert" the text encryption code I found online to a Crypto++ file encryption. Basically, an image file is just a bunch of text which needs to be read in chunks, am I right?

I want to read an image file and encrypt it with CryptoPP::AES :

void encryptImage(std::filesystem::path const& file) {
    std::ifstream inpf(file.string().c_str(), std::ios::binary);

    CryptoPP::AutoSeededRandomPool rnd;
    int keyLength = CryptoPP::AES::DEFAULT_KEYLENGTH; // 16 bytes = 128 bit key
    int defBlockSize = CryptoPP::AES::BLOCKSIZE;
    // Generate a random key
    CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH];
    rnd.GenerateBlock(key, CryptoPP::AES::DEFAULT_KEYLENGTH);

    // Generate a random IV
    CryptoPP::byte iv[CryptoPP::AES::BLOCKSIZE];
    rnd.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE);

    char plainText[] = "Hello! How are you.";
    int messageLen = (int)strlen(plainText) + 1;

    //encrypt
    CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption cfbEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv);
    cfbEncryption.ProcessData((CryptoPP::byte*)plainText, (CryptoPP::byte*)plainText, messageLen);
}

If I understand this code right it sets a random key and a random IV (don't know what that's supposed to mean) and uses the CFB_MODE::Encryption method to encrypt the given text from the plainText variable.

My question is now... how can I change this code up so that it encrypts the file I am giving as parameter and reading in with std::ifstream instead of the text?

If I understand this code right it sets a random key and a random IV (don't know what that's supposed to mean) and uses the CFB_MODE::Encryption method to encrypt the given text from the plainText variable.

Correct, the code generates a random AES key of size 128 bit and a random Initialization Vector (IV) of size 128 bit. The key size can be adjusted to increase the encryption strength (ie you could also have a 256 bit key), whereas the size size of the IV has to be equal to the AES block size (128 bit), which is unrelated to the key size. So it is only a coincidence that both are 128 bit in this case.

The block cipher mode is set to CFB ( reference ). The motivation for block cipher modes is that we do not want equal data blocks occurring at different positions in the input to have the same encryption. Otherwise, it would be possible to infer some of the structure of the original data from the encrypted data, as illustrated with the example of an image file in the linked article. To avoid this problem, block cipher modes combine the plain-text from the current block in some way with the previous block. The mathematical details of CFB are explained in the linked article, but basically it will XOR the plaintext with the encryption of the previous block.

The first block is a special case, since it does not have a previous block. The IV is used to serve as the "previous block" of the first block (hence why the IV needs to be equal to the block size). The IV does not need to be secret (unlike the key), but it should be randomly chosen for each encrypted message, to prevent an attacker from inferring information about the structure of the message.

How can I change this code up so that it encrypts the file I am giving as parameter and reading in with std::ifstream instead of the text?

To apply the encryption to a file, you can use the FileSource and FileSink classes provided by crypto++:

// Type aliases for key and IV
using aes_key_t = std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH>;
using aes_iv_t = std::array<CryptoPP::byte, CryptoPP::AES::BLOCKSIZE>;

void encrypt(const aes_key_t &key, const aes_iv_t &iv,
             const std::string &filename_in, const std::string &filename_out) {
  CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption cipher{};
  cipher.SetKeyWithIV(key.data(), key.size(), iv.data());

  std::ifstream in{filename_in, std::ios::binary};
  std::ofstream out{filename_out, std::ios::binary};

  CryptoPP::FileSource{in, /*pumpAll=*/true,
                       new CryptoPP::StreamTransformationFilter{
                           cipher, new CryptoPP::FileSink{out}}};
}

This will "pump" all data from the source to the sink, applying the given transformation. In this case, the source is the input file, the sink is the output file and the transformation is the encryption.

In general, doing manual memory management via new is discouraged, in favor of using smart pointers or other higher-level abstractions. However, the crypto++ API seems to predate this guideline, so we cannot get around using new and then trusting crypto++ with delete ing these allocations for us.

You could also pass the filename directly to FileSource and FileSink ( reference , reference ), but since you specifically asked about std::ifstream , I used that in the example.

Full example with encryption and decryption:

#include <cryptopp/files.h>
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>

#include <fstream>
#include <iostream>

using aes_key_t = std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH>;
using aes_iv_t = std::array<CryptoPP::byte, CryptoPP::AES::BLOCKSIZE>;

void encrypt(const aes_key_t &key, const aes_iv_t &iv,
             const std::string &filename_in, const std::string &filename_out) {
  CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption cipher{};
  cipher.SetKeyWithIV(key.data(), key.size(), iv.data());

  std::ifstream in{filename_in, std::ios::binary};
  std::ofstream out{filename_out, std::ios::binary};

  CryptoPP::FileSource{in, /*pumpAll=*/true,
                       new CryptoPP::StreamTransformationFilter{
                           cipher, new CryptoPP::FileSink{out}}};
}

void decrypt(const aes_key_t &key, const aes_iv_t &iv,
             const std::string &filename_in, const std::string &filename_out) {
  CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption cipher{};
  cipher.SetKeyWithIV(key.data(), key.size(), iv.data());

  std::ifstream in{filename_in, std::ios::binary};
  std::ofstream out{filename_out, std::ios::binary};

  CryptoPP::FileSource{in, /*pumpAll=*/true,
                       new CryptoPP::StreamTransformationFilter{
                           cipher, new CryptoPP::FileSink{out}}};
}

int main(int argc, char **argv) {

  std::cout <<  CryptoPP::AES::BLOCKSIZE << std::endl;

  CryptoPP::AutoSeededRandomPool rng{};

  // Generate a random key
  aes_key_t key{};
  rng.GenerateBlock(key.data(), key.size());

  // Generate a random IV
  aes_iv_t iv{};
  rng.GenerateBlock(iv.data(), iv.size());

  // encrypt
  encrypt(key, iv, "abc.jpg", "abc_encrypted");

  // decrypt
  decrypt(key, iv, "abc_encrypted", "abc_decrypted.jpg");

  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM