【问题标题】:(Crypto++) How to realize media file encryption?(Crypto++) 如何实现媒体文件加密?
【发布时间】:2020-08-02 10:31:13
【问题描述】:

我是 Crypto++ 的新手,想将我在网上找到的文本加密代码“转换”为 Crypto++ 文件加密。基本上,图像文件只是一堆需要分块读取的文本,对吗?

我想读取一个图像文件并用 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);
}

如果我正确理解这段代码,它会设置一个随机密钥和一个随机 IV(不知道这应该是什么意思)并使用 CFB_MODE::Encryption 方法从plainText 变量。

我现在的问题是...如何更改此代码,以便它加密我作为参数提供的文件并使用 std::ifstream 而不是文本读取?

【问题讨论】:

  • 这不是 YouTube 或任何东西,请不要以 jojojo 开头,除非您想被视为 jojo。 C++ 程序员经常将“文本”与“二进制”混淆,因为stringchar[] 可用于保存二进制数据。除此之外,你哪里失败了?上面的代码已经加密了一个文件,对吧?
  • 我的名字是约翰尼,这就是我多次这样做的原因,所以我想这只是习惯的力量。正如我提到的,我是 Crypto++ 的新手,所以我不确定我是否正确理解了代码,但我认为它加密了存储在 plainText[] 字符中的文本。我在函数顶部声明的文件只是我尝试从文件加密开始,但我不知道从哪里开始考虑图像的实际读取以及加密。
  • @MaartenBodewes 所以...我猜你也不知道如何加密图像文件。

标签: c++ encryption cryptography aes crypto++


【解决方案1】:

如果我正确理解这段代码,它会设置一个随机密钥和一个随机 IV(不知道这是什么意思),并使用 CFB_MODE::Encryption 方法从 plainText 变量中加密给定文本。

正确,代码生成大小为 128 位的随机 AES 密钥和大小为 128 位的随机初始化向量 (IV)。可以调整密钥大小以增加加密强度(即您也可以拥有 256 位密钥),而 IV 的大小必须等于 AES 块大小(128 位),这与密钥无关尺寸。所以在这种情况下两者都是 128 位只是一个巧合。

分组密码模式设置为 CFB (reference)。分组密码模式的动机是我们不希望出现在输入中不同位置的相同数据块具有相同的加密。否则,可能会从加密数据中推断出原始数据的某些结构,如链接文章中的图像文件示例所示。为了避免这个问题,分组密码模式以某种方式将来自当前块的纯文本与前一个块结合起来。 CFB 的数学细节在链接的文章中有解释,但基本上它会将明文与前一个块的加密进行异或。

第一个块是一种特殊情况,因为它没有前一个块。 IV 用作第一个块的“前一个块”(因此 IV 需要等于块大小)。 IV 不需要保密(与密钥不同),但应该为每条加密消息随机选择它,以防止攻击者推断有关消息结构的信息。

如何更改此代码,以便它加密我作为参数提供的文件并使用 std::ifstream 而不是文本读取?

要对文件应用加密,可以使用crypto++提供的FileSourceFileSink类:

// 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}}};
}

这会将所有数据从源“泵”到接收器,应用给定的转换。在这种情况下,源是输入文件,接收器是输出文件,转换是加密。

一般来说,不鼓励通过new 进行手动内存管理,而是支持使用智能指针或其他更高级别的抽象。但是,crypto++ API 似乎早于本指南,因此我们无法绕过使用 new,然后使用 delete 为我们分配这些分配来信任 crypto++。

您也可以将文件名直接传递给FileSourceFileSinkreferencereference),但由于您专门询问了std::ifstream,所以我在示例中使用了它。

加密和解密的完整示例:

#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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2022-08-19
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    相关资源
    最近更新 更多