22、C++项目实战:文件加密解密工具

说实话,文件加密解密这个题目,几乎是每个C++开发者都会遇到的“必修课”。我记得刚入行那会儿,项目经理丢给我一个需求:给公司内部的文件传输工具加个简单的加密功能。我当时心想,这不就是调个库吗?结果一上手才发现,从算法选型到密钥管理,再到文件流的异常处理,坑多着呢。

今天咱们就手撸一个完整的文件加密解密工具。不依赖第三方库,只用C++标准库。你想想看,这样编译出来的程序,扔到任何一台机器上都能跑,多省心。

核心设计思路

这个工具的核心逻辑其实不复杂:

  • 读取源文件,按块处理数据
  • 对每个数据块执行加密/解密操作
  • 将处理后的数据写入目标文件

我选择XOR异或加密作为基础算法。为什么?因为XOR运算有个特性:A XOR B XOR B = A。也就是说,加密和解密可以用同一套代码。我在项目中遇到过好几次,用不同函数分别实现加密和解密,结果两边逻辑不一致出了bug。用XOR就完美避免了这个问题。

核心要点:加密和解密使用相同的函数,只是密钥相同即可。这叫做“对称加密”的简化版。

代码实现

先看头文件,我习惯把接口声明和实现分开:

// FileCrypto.h
#ifndef FILECRYPTO_H
#define FILECRYPTO_H

#include <string>
#include <vector>

class FileCrypto {
public:
    // 加密或解密文件
    // srcPath: 源文件路径
    // dstPath: 目标文件路径
    // key: 密钥字符串
    // 返回true表示成功
    bool processFile(const std::string& srcPath,
                     const std::string& dstPath,
                     const std::string& key);

private:
    // 对一块数据进行XOR处理
    void xorData(std::vector<char>& buffer,
                 const std::string& key);
};

#endif

嗯,这里要注意一点:processFile同时负责加密和解密。调用者只需要传入不同的目标路径,逻辑上就区分开了。我个人习惯用这种方式,减少代码重复。

接下来是实现文件:

// FileCrypto.cpp
#include "FileCrypto.h"
#include <fstream>
#include <stdexcept>

bool FileCrypto::processFile(const std::string& srcPath,
                             const std::string& dstPath,
                             const std::string& key) {
    // 以二进制模式打开源文件
    std::ifstream inFile(srcPath, std::ios::binary);
    if (!inFile.is_open()) {
        return false;
    }

    // 以二进制模式打开目标文件
    std::ofstream outFile(dstPath, std::ios::binary);
    if (!outFile.is_open()) {
        inFile.close();
        return false;
    }

    // 缓冲区大小:64KB
    const size_t BUFFER_SIZE = 64 * 1024;
    std::vector<char> buffer(BUFFER_SIZE);

    while (inFile.read(buffer.data(), BUFFER_SIZE) || inFile.gcount() > 0) {
        // 获取实际读取的字节数
        size_t bytesRead = inFile.gcount();

        // 调整缓冲区大小到实际读取的字节数
        buffer.resize(bytesRead);

        // 执行XOR操作
        xorData(buffer, key);

        // 写入目标文件
        outFile.write(buffer.data(), buffer.size());

        // 恢复缓冲区大小
        buffer.resize(BUFFER_SIZE);
    }

    inFile.close();
    outFile.close();
    return true;
}

void FileCrypto::xorData(std::vector<char>& buffer,
                         const std::string& key) {
    if (key.empty()) return;

    size_t keyLen = key.length();
    for (size_t i = 0; i < buffer.size(); ++i) {
        buffer[i] ^= key[i % keyLen];
    }
}

小技巧:缓冲区大小设为64KB是个经验值。太小了频繁IO效率低,太大了浪费内存。我在实际项目中测试过,64KB在大多数场景下表现都不错。

流程图:加密解密核心逻辑

下面这张图展示了整个处理流程,我建议你仔细看看:

开始处理文件 打开源文件(二进制模式) 读取数据块 是否成功? XOR加密/解密 写入目标文件 处理完成,关闭文件

主程序与使用示例

有了核心类,主程序就简单了:

// main.cpp
#include "FileCrypto.h"
#include <iostream>

int main(int argc, char* argv[]) {
    if (argc != 4) {
        std::cout << "用法: FileCrypto <源文件> <目标文件> <密钥>" << std::endl;
        return 1;
    }

    std::string srcPath = argv[1];
    std::string dstPath = argv[2];
    std::string key = argv[3];

    FileCrypto crypto;
    bool success = crypto.processFile(srcPath, dstPath, key);

    if (success) {
        std::cout << "操作成功完成!" << std::endl;
        return 0;
    } else {
        std::cerr << "操作失败,请检查文件路径和权限。" << std::endl;
        return 1;
    }
}

使用方式也很直观:

# 加密文件
./FileCrypto secret.txt secret.enc mykey123

# 解密文件(用同样的密钥)
./FileCrypto secret.enc secret_dec.txt mykey123

警告:XOR加密是教学级别的,不适合保护真正的敏感数据。我曾经在一个内部工具里用了这个方案,结果被安全审计的同事怼了一顿。生产环境请使用AES等标准算法。

避坑指南

做这个项目时,有几个坑我踩过,分享给你:

  • 二进制模式必须加:如果不加std::ios::binary,在Windows上会出问题。文本模式会把0x0A(换行)自动转换成0x0D 0x0A,加密后的数据就全乱了。
  • 密钥不能为空:空密钥会导致xorData里除零错误。我在代码里加了if (key.empty()) return;,但最好在调用前就检查。
  • 大文件处理:不要一次性把整个文件读入内存。用分块处理,64KB的缓冲区对大多数文件都够用。我试过处理2GB的视频文件,内存占用一直很稳定。

扩展思路

如果你想让这个工具更实用,可以考虑:

功能 实现思路 难度
支持AES加密 集成OpenSSL库 中等
文件完整性校验 加密前计算SHA256,附加到文件末尾 简单
多线程加速 将文件分块,每个线程处理一块 较难
图形界面 使用Qt或Win32 API 中等

我个人建议先加上文件完整性校验。为什么呢?因为XOR加密有个问题:如果文件在传输过程中损坏了,你解密出来的数据就是一堆乱码,而且你根本不知道。加个哈希校验,至少能告诉你“文件坏了”。

好了,这个文件加密解密工具的核心内容就这些。代码量不大,但涉及的知识点挺全的:文件流操作、二进制处理、位运算、异常处理。你动手写一遍,肯定能把这些知识点串起来。


公众号:蓝海资料掘金营,微信deep3321