C++ RSA 系统备份和归档以及解密解压

引言

此代码目前属于测试阶段,不保证完全可用,但是曾经写过一个python的小工具,但是随着系统越来越大,数据越来越多,加解密速度就会变得很慢很慢,所以使用C的话,就可以发挥出机器的真实性能了。

python和c效率对比

属性 C++ Python
ZIP压缩 35s 57s
RSA加密 22s 15m36s
RSA解密 6m22s 48m21s

cmake配置

cmake_minimum_required(VERSION 3.27)
project(main)


set(VCPKG_TARGET_TRIPLET "x64-mingw-static" CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD 17)

find_package(OpenSSL REQUIRED)
find_package(jsoncpp CONFIG REQUIRED)
find_package(libzip CONFIG REQUIRED)
add_executable(main main.cpp)

target_link_libraries(main
        PUBLIC
        OpenSSL::SSL
        OpenSSL::Crypto
)

target_link_libraries(main PRIVATE libzip::zip)
target_link_libraries(main PRIVATE JsonCpp::JsonCpp)

代码

#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <json/json.h>
#include <filesystem>
#include <fstream>
#include <vector>
#include <ctime>
#include <chrono>
#include <iomanip>
#include <zip.h>
#include <tchar.h>

Json::Value config;

std::string pub_key = "-----BEGIN RSA PUBLIC KEY-----\n"
                      "MIGJ*******\n"
                      "*******\n"
                      "*******=\n"
                      "-----END RSA PUBLIC KEY-----\n";
std::string priv_key = "-----BEGIN RSA PRIVATE KEY-----\n"
                       "MIICY*******\n"
                       "*******\n"
                       "*******\n"
                       "*******\n"
                       "*******\n"
                       "*******/\n"
                       ".......\n"
                       "*******\n"
                       "-----END RSA PRIVATE KEY-----\n";

void print(const std::string &msg) {
    time_t currentTime = time(nullptr);
    struct tm *localTime = localtime(&currentTime);
    auto now = std::chrono::system_clock::now();
    auto now_civil = std::chrono::system_clock::to_time_t(now);
    std::cout << "["
              << std::put_time(std::localtime(&now_civil), "%Y-%m-%d %H:%M:%S") << "] "
              << msg
              << std::endl;

}

void initialize() {
    print("[i] Initializing");
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
    system("CHCP 65001");
    print("[i] Current OS is Windows");
#else
    print("[i] Current OS is Linux");
#endif
    std::locale::global(std::locale(""));
    time_t currentTime = time(nullptr);
    struct tm *localTime = localtime(&currentTime);
    const std::chrono::time_point now = std::chrono::system_clock::now();
    time_t now_civil = std::chrono::system_clock::to_time_t(now);
    std::stringstream date_tmp;
    date_tmp << std::put_time(std::localtime(&now_civil), "%Y%m%d_%H%M%S");
    config["backup_temp_path"] = "c:/inetpub/backup";
    config["web_path"] = "c:/inetpub/www/";
    config["date"] = date_tmp.str();
    config["db_username"] = "root";
    config["db_password"] = "******";
    config["db_host"] = "127.0.0.1";
    config["db_port"] = 3306;
    config["db_backup_db"] = "my_db";
    config["pointer"] = "client";
    config["backup_out_path"] = "c:/inetpub/www2/backup.rsa";
    config["save_path"] = _T("F:/网站备份");
    print("[i] Initialize successfully");
}

std::string get_string4config(const std::string &str) {
    return config[str].asString();
}

std::string RsaPubEncrypt(const std::string &clear_text, const std::string &pub_key) {
    std::string encrypt_text;
    BIO *keybio = BIO_new_mem_buf((unsigned char *) pub_key.c_str(), -1);
    RSA *rsa = RSA_new();
    rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, nullptr, nullptr);
    int key_len = RSA_size(rsa);
    int block_len = key_len - 11;
    char *sub_text = new char[key_len + 1];
    memset(sub_text, 0, key_len + 1);
    int ret = 0;
    int pos = 0;
    std::string sub_str;
    while (pos < clear_text.length()) {
        sub_str = clear_text.substr(pos, block_len);
        memset(sub_text, 0, key_len + 1);
        ret = RSA_public_encrypt(sub_str.length(), (const unsigned char *) sub_str.c_str(), (unsigned char *) sub_text,
                                 rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            encrypt_text.append(std::string(sub_text, ret));
        }
        pos += block_len;
    }
    BIO_free_all(keybio);
    RSA_free(rsa);
    delete[] sub_text;
    return encrypt_text;
}

std::string RsaPriDecrypt(const std::string &cipher_text, const std::string &pri_key) {
    std::string decrypt_text;
    RSA *rsa = RSA_new();
    BIO *keybio;
    keybio = BIO_new_mem_buf((unsigned char *) pri_key.c_str(), -1);
    rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, nullptr, nullptr);
    if (rsa == nullptr) {
        unsigned long err = ERR_get_error(); //获取错误号
        char err_msg[1024] = {0};
        ERR_error_string(err, err_msg); // 格式:error:errId:库:函数:原因
        printf("err msg: err:%ld, msg:%s\n", err, err_msg);
        return {};
    }
    int key_len = RSA_size(rsa);
    char *sub_text = new char[key_len + 1];
    memset(sub_text, 0, key_len + 1);
    int ret = 0;
    std::string sub_str;
    int pos = 0;
    // 对密文进行分段解密
    while (pos < cipher_text.length()) {
        sub_str = cipher_text.substr(pos, key_len);
        memset(sub_text, 0, key_len + 1);
        ret = RSA_private_decrypt(sub_str.length(), (const unsigned char *) sub_str.c_str(), (unsigned char *) sub_text,
                                  rsa, RSA_PKCS1_PADDING);
        if (ret >= 0) {
            decrypt_text.append(std::string(sub_text, ret));
            pos += key_len;
        }
    }
    // 释放内存
    delete[] sub_text;
    BIO_free_all(keybio);
    RSA_free(rsa);

    return decrypt_text;
}

// 读文件
std::string readFile(const std::string &filePath) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        print("[x] Open file failure " + filePath);
        return {};
    }
    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close();
    return content;
}

void save_file(const std::string &filePath, const std::string &content) {

    std::ofstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        print("[x] Open file failure " + filePath);
        return;
    }
    file.write(content.c_str(), content.length());
    file.close();
}


void check_path() {
    print("[i] Checking backup temp path");
    if (!std::filesystem::exists(get_string4config("backup_temp_path"))) {
        print("[i] Creating directory: " + get_string4config("backup_temp_path"));
        std::filesystem::create_directory(get_string4config("backup_temp_path"));
    } else {
        print("[i] Directory exists: " + get_string4config("backup_temp_path"));
        std::filesystem::remove_all(get_string4config("backup_temp_path"));
        check_path();
    }
}


bool compress_directory(const std::string &zip_path, const std::string &dir_path) {
    int err = 0;
    zip_source_t *source = nullptr;
    zip_t *zip = zip_open(zip_path.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &err);
    if (zip == nullptr) {
        std::cerr << "[x] Error creating ZIP archive: " << err << std::endl;
        return false;
    }

    for (const auto &entry: std::filesystem::recursive_directory_iterator(dir_path)) {
        if (std::filesystem::is_regular_file(entry.path())) {
            const std::string &abs_path = entry.path().string();
            std::ifstream file(abs_path, std::ios::binary);
            source = zip_source_file(zip,
                                     abs_path.c_str(), 0,
                                     0);
            if (source == nullptr) {
                std::cerr << "[x] Error adding file to ZIP: " << abs_path << std::endl;
                zip_close(zip);
                return false;
            }
            zip_file_add(zip, abs_path.substr(dir_path.size(), abs_path.size() - dir_path.size()).c_str(), source,
                         ZIP_FL_OVERWRITE);
            zip_source_keep(source);
            zip_source_free(source);
        }
    }

    if (zip_close(zip) != 0) {
        std::cerr << "[x] Error closing ZIP archive" << std::endl;
        return false;
    }

    return true;
}

void backup_database() {
    print("[i] Backing up database");
    std::string backup_path = get_string4config("backup_temp_path");
    std::string command = "mysqldump -h" + get_string4config("db_host") +
                          " -P" + get_string4config("db_port") +
                          " -u" + get_string4config("db_username") +
                          " -p" + get_string4config("db_password") +
                          " -E -R --databases " + get_string4config("db_backup_db") +
                          " > " + get_string4config("backup_temp_path") + "/db_backup_" + get_string4config("date") +
                          ".sql";
    system(command.c_str());
    if (std::filesystem::exists(backup_path + "/db_backup_" + get_string4config("date") + ".sql")) {
        if (compress_directory(get_string4config("backup_temp_path") + "/db_" + get_string4config("date") + ".zip",
                               get_string4config("backup_temp_path") + "/")) {
            print("[v] DB compressed successfully.");
            std::filesystem::remove(backup_path + "/db_backup_" + get_string4config("date") + ".sql");
        } else {
            print("[x] Error compressing directory.");
        }
    } else {
        print("[x]Database backup failed");
    }
}

int backup_website() {
    print("[i] Creating website ZIP archive");
    if (compress_directory(get_string4config("backup_temp_path") + "/web_" + get_string4config("date") + ".zip",
                           get_string4config("web_path"))) {
        print("[v] Directory compressed successfully.");
    } else {
        print("[x] Error compressing directory.");
    }
    return 0;
}

int compress_backup() {
    print("[i] Creating backup ZIP archive");
    if (compress_directory(get_string4config("backup_temp_path") + "/all_backup.zip",
                           get_string4config("backup_temp_path"))) {
        print("[v] Directory compressed successfully.");
    } else {
        print("[x] Error compressing directory.");
    }
    return 0;
}

int delete_temp_file() {
    print("[i] Deleting temp files");
    std::filesystem::remove_all(get_string4config("backup_temp_path"));
    print("[v] Deleted temp files");
    return 0;
}

int encrypt_publish() {
    print("[i] Encrypting");
    std::string encrypt = RsaPubEncrypt(readFile(get_string4config("backup_temp_path") + "all_backup.zip"), pub_key);
    save_file(get_string4config("backup_out_path"), encrypt);
    print("[i] Encrypted");
    return 0;
}

int decrypt_backup(const std::string &path) {
    print("[i] Decrypting");
    std::string decrypt = RsaPriDecrypt(readFile(path), priv_key);
    print("[i] Decrypted");
    save_file(get_string4config("save_path") + "/" + get_string4config("date") + ".zip", decrypt);
    return 0;
}

int main(int argc, char **argv) {
    initialize();
    if (get_string4config("pointer") == "server") {
        print("[i] CURRENT MODE: SERVER");
        check_path();
        backup_database();
        backup_website();
        compress_backup();
        encrypt_publish();
        delete_temp_file();
    } else if (get_string4config("pointer") == "client") {
        print("[i] CURRENT MODE: CLIENT");
        if (argc == 2) {
            std::string path = argv[1];
            decrypt_backup(path);
        } else {
            print("[x] ERROR INPUT PARAMS");
        }
    }
    return 0;
}

image.png
image.png

使用方法

服务端要修改config["pointer"]为"server";,这样就执行的是备份和加密操作
归档存储机器要修改config["pointer"]为"client";,这样就执行的是解密操作

⚠:示例使用的是windows服务器和windows的归档服务器其中需要手动下载归档文件到归档服务器
⚠:如果无法使用中文路径,可修改代码的编码为GBK,即可解决该问题
⚠:测试使用的均为windows机器,尚未对其它系统做测试
⚠:建议使用vcpkg进行软件包管理
⚠:建议在服务端删除掉私钥,以免程序泄露,通过反汇编获得到私钥。

ℹ:公钥加密私钥解密:是为了保证数据安全性,因为私钥可生成公钥,一旦私钥泄露,那么加密就没有任何作用了,服务端采用公钥加密,保证只有唯一的私钥才能解密。

版本

bzip2==1.0.8#5
jsoncpp==1.9.5#2
libzip==1.10.1
openssl==3.2.1#1
vcpkg-cmake-config==2022-02-06#1
vcpkg-cmake-get-vars==2023.12031
vcpkg-cmake==2023-05-04
zlib=1.3.1

扩展

可以将下载远程归档也嵌入到代码中,实现自动保存备份

评论 0

还没有评论,来说点什么吧。

提交后需要审核才会公开。