1 AES加密算法

Crypto++/CryptoPP加解密库官网:https://www.cryptopp.com/

在写桌面程序的时候一些自定义的数据文件不可避免的需要放在本地,通常这种数据文件保存了一些敏感信息,所以必须想办法对其进行加密。然后在程序中对这种数据文件进行解密然后读取正确的内容。

目前加密分为非对称加密和对称加密。对称加密算法中,加密和解密使用的是同一把钥匙,即:使用相同的密匙对同一密码进行加密和解密;非对称加密是使用不同的密钥进行加密和解密,非对称加密有两个钥匙,及公钥(Public Key)和私钥(Private Key)。公钥和私钥是成对的存在,如果对原文使用公钥加密,则只能使用对应的私钥才能解密。关于对称加密与非对称加密的区别,可参考:https://zhuanlan.zhihu.com/p/49494990

对于本地加解密数据文件,会采用对称加密方法。而目前对称加密中破解难度最高的就是AES加密算法。其中AES加密算法可参考:https://zhuanlan.zhihu.com/p/78913397。AES加密也是具有奇高的破解难度,破解难度可参考:

2 Crypto++/CryptoPP加解密库

Crypto++/CryptoPP官网:https://www.cryptopp.com/

Crypto++是一个免费开源的C++加解密库,支持大部分的加解密算法,比如AES,MD5,base64,RSA等目前常用的加密算法。

2.1 下载

库下载链接:https://www.cryptopp.com/#download

下载最新版本即可:

C++ – 使用Crypto++/CryptoPP加解密库对字符串或者文件进行AES256加密-StubbornHuang Blog

2.2 Windows下编译

下载完成之后,解压缩包,在文件夹下可以找到cryptest.sln的VS项目文件,使用VS2019打开即可。

该解决方案包括四个项目文件:

  • cryptdll:cryptdll为动态库方案,在其他项目应用cryptopp的时候,编译时链接静态库,运行时需要链接动态库库
  • dlltest:测试cryptdll编译的dll
  • cryptlib:cryptlib为静态库方案,在其他项目应用cryptopp的时候,只需在编译时链接静态库即可,不需要在运行时链接动态库
  • cryptest:各种使用示例。

编译时需要注意cryptdll和cryptlib项目下C/C++-代码生成-运行库是/MT还是/MD,此种方式需要与使用Crypto++库的项目需要保持一致,不然在编译时会出错。

3 使用Crypto++/CryptoPP加解密库对字符串或者文件进行AES256加密

基于Crypto++对其AES256加密算法进行了封装,使其变得更加好用。封装类为CryptoAES类,提供了使用AES256加密字符串,解密字符串,直接加密数据文件,直接解密数据文件,间接从文件内容加密文件,间接从文件内容解密文件。目前使用的Crypto++/CryptoPP的版本为8.6.0。

针对CryptoAES.cpp文件中CryptoAES构造函数中的std::string message,可使用AES密码在线生成:https://www.imaegoo.com/2020/aes-key-generator/

3.1 CryptoAES.h 头文件

#ifndef CRYPTO_AES_H
#define CRYPTO_AES_H

#include "aes.h"
#include "files.h"
#include "filters.h"
#include "osrng.h"
#include "default.h"
#include "hex.h"
#include "base64.h"

class CryptoAES
{
public:
    CryptoAES();
    CryptoAES(const std::string& aes_key);
    virtual~CryptoAES();

public:

    std::string EncryptString(const std::string& plain_str);
    std::string DecryptString(const std::string& cipher_str);

    bool EncryptFileDirect(const std::string& src_file_path, const std::string& target_file_path);
    bool DecryptFileDirect(const std::string& src_file_path, const std::string& target_file_path);

    bool EncryptFileUseIO(const std::string& src_file_path, const std::string& target_file_path);
    std::string DecryptFileUseIO(const std::string& src_file_path);

private:
    CryptoPP::byte* m_Key;
    CryptoPP::byte* m_IV;
};

#endif // !CRYPTO_AES_H

3.2 CryptoAES.cpp 实现文件

#include "CryptoAES.h"
#include <fstream>

CryptoAES::CryptoAES()
{
    // sha256对字符串密码加密
    CryptoPP::SHA256 hash;
    CryptoPP::byte digest[CryptoPP::SHA256::DIGESTSIZE];
    std::string message = "brWUq5ZK263PCpLkCun5LYT3TjlQRy8jsvdaClofrCe/ZH5Vb8mxpP3fquPJSnWP";

    hash.CalculateDigest(digest, (CryptoPP::byte*)message.c_str(), message.length());
    CryptoPP::HexEncoder encoder;
    std::string sKey;
    encoder.Attach(new CryptoPP::StringSink(sKey));
    encoder.Put(digest, sizeof(digest));
    encoder.MessageEnd();

    // 取sha256加密后的hash数列前32位作为Aes加密密码
    CryptoPP::byte key[CryptoPP::AES::MAX_KEYLENGTH];
    CryptoPP::byte iv[CryptoPP::AES::BLOCKSIZE];
    memcpy(key, sKey.c_str(), CryptoPP::AES::MAX_KEYLENGTH);;
    memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE);

    m_Key = key;
    m_IV = iv;
}

CryptoAES::CryptoAES(const std::string& aes_key)
{
    // sha256对字符串密码加密
    CryptoPP::SHA256 hash;
    CryptoPP::byte digest[CryptoPP::SHA256::DIGESTSIZE];
    std::string message = aes_key;

    hash.CalculateDigest(digest, (CryptoPP::byte*)message.c_str(), message.length());
    CryptoPP::HexEncoder encoder;
    std::string sKey;
    encoder.Attach(new CryptoPP::StringSink(sKey));
    encoder.Put(digest, sizeof(digest));
    encoder.MessageEnd();

    // 取sha256加密后的hash数列前32位作为Aes加密密码
    CryptoPP::byte key[CryptoPP::AES::MAX_KEYLENGTH]; 
    CryptoPP::byte iv[CryptoPP::AES::BLOCKSIZE];
    memcpy(key, sKey.c_str(), CryptoPP::AES::MAX_KEYLENGTH);;
    memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE);

    m_Key = key;
    m_IV = iv;
}

CryptoAES::~CryptoAES()
{

}

std::string CryptoAES::EncryptString(const std::string& plain_str)
{
    std::string cipher_str = "";

    try
    {
        CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption aesEncryptor;
        aesEncryptor.SetKeyWithIV(m_Key, CryptoPP::AES::MAX_KEYLENGTH, m_IV);

        CryptoPP::StringSource s(plain_str, true,
            new CryptoPP::StreamTransformationFilter(aesEncryptor,
                new CryptoPP::StringSink(cipher_str)
            ) 
        ); 
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

    return cipher_str;
}

std::string CryptoAES::DecryptString(const std::string& cipher_str)
{
    std::string plain_str = "";

    try 
    {
        CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption aesDecryptor;
        aesDecryptor.SetKeyWithIV(m_Key, CryptoPP::AES::MAX_KEYLENGTH, m_IV);

        CryptoPP::StringSource s(cipher_str, true,
            new CryptoPP::StreamTransformationFilter(aesDecryptor,
                new CryptoPP::StringSink(plain_str)));

    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }


    return plain_str;
}

bool CryptoAES::EncryptFileDirect(const std::string& src_file_path, const std::string& target_file_path)
{
    // 判断源文件是否存在
    std::ifstream src_file(src_file_path.c_str());
    if (!src_file.good())
    {
        return false;
    }

    std::ifstream in{ src_file_path, std::ios::binary };
    std::ofstream out{ target_file_path, std::ios::binary };

    try
    {
        CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption aesEncryptor;
        aesEncryptor.SetKeyWithIV(m_Key, CryptoPP::AES::MAX_KEYLENGTH, m_IV);

        CryptoPP::FileSource s(in, true,
            new CryptoPP::StreamTransformationFilter(aesEncryptor,
                new CryptoPP::FileSink(out)
            ) 
        ); 
    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

    return true;
}

bool CryptoAES::DecryptFileDirect(const std::string& src_file_path, const std::string& target_file_path)
{
    // 判断源文件是否存在
    std::ifstream src_file(src_file_path.c_str());
    if (!src_file.good())
    {
        return false;
    }

    std::ifstream in{ src_file_path, std::ios::binary };
    std::ofstream out{ target_file_path, std::ios::binary };

    try
    {
        CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption aesDecryptor;
        aesDecryptor.SetKeyWithIV(m_Key, CryptoPP::AES::MAX_KEYLENGTH, m_IV);

        CryptoPP::FileSource s(in, true,
            new CryptoPP::StreamTransformationFilter(aesDecryptor,
                new CryptoPP::FileSink(out)));

    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

    return true;
}

bool CryptoAES::EncryptFileUseIO(const std::string& src_file_path, const std::string& target_file_path)
{
    // 从输入文本一次性读取文件内容
    std::ifstream inFile(src_file_path, std::ios::in);
    if (!inFile.is_open())
    {
        inFile.close();
        return false;
    }
    std::istreambuf_iterator<char> beg(inFile), end;
    std::string strdata(beg, end);
    inFile.close();

    // Aes加密文件
    std::string cipher_str = EncryptString(strdata);

    // base64编码,为防止转换后的字符串在解码时发生错误:StreamTransformationFilter: ciphertext length is not a multiple of block size,采用base64编码密文
    std::string cipher_base64_str;
    CryptoPP::StringSource ss(cipher_str, true,
        new CryptoPP::Base64Encoder(new CryptoPP::StringSink(cipher_base64_str))
    );

    // 输出文件
    std::ofstream out(target_file_path, std::ios::binary|std::ios::trunc);
    if (out.is_open())
    {
        out.write(cipher_base64_str.c_str(), cipher_base64_str.length());
        out.close();
    }

    return true;
}

std::string CryptoAES::DecryptFileUseIO(const std::string& src_file_path)
{
    std::string plain_str = "";

    // 从输入文本一次性读取文件内容
    std::ifstream inFile(src_file_path, std::ios::in);
    if (!inFile.is_open())
    {
        inFile.close();
        abort();
    }
    std::istreambuf_iterator<char> beg(inFile), end;
    std::string strdata(beg, end);
    inFile.close();

    // base64解码
    std::string cipher_base64_str;

    CryptoPP::StringSource ss(strdata, true,
        new CryptoPP::Base64Decoder(new CryptoPP::StringSink(cipher_base64_str))
    );


    // Aes解密
    try
    {
        CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption aesDecryptor;
        aesDecryptor.SetKeyWithIV(m_Key, CryptoPP::AES::MAX_KEYLENGTH, m_IV);

        CryptoPP::StringSource s(cipher_base64_str, true,
            new CryptoPP::StreamTransformationFilter(aesDecryptor,
                new CryptoPP::StringSink(plain_str)));

    }
    catch (const CryptoPP::Exception& e)
    {
        std::cerr <<"文件解密出错:" <<e.what() << std::endl;
        exit(1);
    }

    return plain_str;
}

3.3 使用示例

#include <iostream>

#include "CryptoAES.h"

int main(int argc, char* argv[])
{
    CryptoAES aesEncrypt("dog");
    std::string cipher_str = aesEncrypt.EncryptString("hello world");
    std::cout << "加密后字符串:" << cipher_str << std::endl;

    std::string plain_str = aesEncrypt.DecryptString(cipher_str);
    std::cout << "解密后字符串" << plain_str << std::endl;

    aesEncrypt.EncryptFileDirect("helloworld.txt", "helloworld_direct_encrypt.data");
    aesEncrypt.EncryptFileDirect("helloworld_direct_encrypt.data", "helloworld_direct_decrypt.txt");

    aesEncrypt.EncryptFileUseIO("helloworld.txt", "helloworld_direct_encrypt.data");
    std::string a = aesEncrypt.DecryptFileUseIO("helloworld_direct_encrypt.data");
    std::cout << "解密文件后字符串:" << a << std::endl;

    std::cout << "所有函数执行完成" << std::endl;

    return 0;
}

如果您觉得对您有帮助,可以请站长喝一杯咖啡哦!

记得在赞赏备注里写上您的昵称

您可在本站资助名单中查看你的打赏记录哦!

支付宝扫一扫

微信扫一扫

金额随意,礼轻义重

当前分类随机文章推荐

全站随机文章推荐

关于本站站长 StubbornHuang
C++ – 使用Crypto++/CryptoPP加解密库对字符串或者文件进行AES256加密-StubbornHuang Blog纵使晴明无雨色,入云深处亦沾衣。