1. 前言
老板之前布置的一个任务中有一个对文件进行加解密的需求,拖了好久都没做, 今天正好想起来,做了一下, 虽然遇到了一些问题, 但是做完之后的感觉是棒棒的呢。
2. 基本思路
- 由于我们需要加密的目标文件是ascii文件,我们希望可以将他通过一个密钥进行加密,然后输出到一个二进制文件中。
- 加密算法我们这里使用的非常简单, ie, 对文件中的每个字节数据与我们的密钥进行异或操作, 得到加密后的数据, 再执行一次异或操作即可还原原始数据了
- 一些经典的加密算法:http://www.iplaysoft.com/encrypt-arithmetic.html,为了实现的方便起见(只是偷懒),我们采用了最简单的异或方法进行加密。
- 我们这里的这个异或加密算法, 是按照行来读取文件数据,以行为单位进行加解密操作, 由于需要转化为二进制文件,为了精准控制, 我们在存储每行数据之前, 先存入 4 字节大小的行内数据长度
3. 遇到的一些问题及参考资料
C++ 读写二进制文件
http://blog.csdn.net/zhyh1435589631/article/details/52181435
http://blog.csdn.net/lightlater/article/details/6364931C++ ifstream 操作 http://en.cppreference.com/w/cpp/io/basic_ifstreami
- c++中char[]与string之间的相互转换问题, 可以使用
string(char [], num)
的形式表示 从char [] 中截取 num 个字符数据转化成 string
4. 实现效果
其中, asInt.ini, asString.ini 都是原始数据文件
*.bin 文件是他们加密得到的数据文件
通过gtest 测试工具, 我们可以发现对加密完的数据进行解密可以重新得到原始数据的, 这表明我们的加密算法是成功的~~~
5. 代码实现
utils.h
#pragma once
#include <fstream>
#include <string>
/**
* 将ascii编码文件内容,根据key进行加密, 并输出到二进制文件中(以行为单位进行加密)
* @param filename 输入文件名称
* @param key 加密关键字
* @param outfile 输出二进制文件名称
* @return bool if ok
*/
bool encrypt_with_passwd_file(const std::string & filename, const std::string & key, const std::string & outfile);
/**
* 对输入的二进制文件中的数据利用 key 解密, 并返回
* @param filename 输入文件名称 (二进制)
* @param key 加密关键字
* @return 解密后的字符串数据
*/
std::string decrypt_with_passwd_file(const std::string & filename, const std::string & key);
/**
* 对输入字符串按照 key 加密
* @param input 输入字符串数据
* @param key 加密关键字
* @return 加密后的字符串数据
*/
std::string encrypt_with_passwd(const std::string & input, const std::string & key);
/**
* 对输入字符串按照 key 解密
* @param input 输入字符串数据
* @param key 加密关键字
* @return 解密后的字符串数据
*/
std::string decrypt_with_passwd(const std::string & input, const std::string & key);
/**
* 加解密核心业务逻辑
* @param input 输入字符串数据
* @param key 加密关键字
* @return 加解密后的字符串数据
*/
std::string crypt_with_passwd_core(const std::string & input, const std::string & key);
utils.cpp
#include "utils.h"
#include <sstream>
using namespace std;
const int fileSizeInByte = 4;
const int MAXBYTE = 1024;
bool encrypt_with_passwd_file(const std::string & filename, const std::string & key, const std::string & outfile){
std::ifstream ifs(filename);
std::ofstream ofs(outfile, std::ios::binary);
if (!ifs || !ofs)
return false;
std::string line;
std::string res;
while (getline(ifs, line)){
res = encrypt_with_passwd(line, key);
ofs.write(to_string(res.size()).c_str(), fileSizeInByte);
ofs.write(res.c_str(), res.size());
}
ofs.flush();
return true;
}
std::string decrypt_with_passwd_file(const std::string & filename, const std::string & key){
std::ifstream ifs(filename, std::ios::binary);
if (!ifs)
return "";
// get file size
ifs.seekg(0, ifstream::end);
int MyfileSize = ifs.tellg();
ifs.seekg(0);
std::string content;
int already_read = 0;
while (already_read < MyfileSize){
// 读取本次需要读入的数据的字节数目
char buf[fileSizeInByte] = { 0 };
ifs.read(buf, fileSizeInByte);
already_read += ifs.gcount();
// 读取字节数据
char buffer[MAXBYTE] = { 0 };
int num = stoi(buf);
ifs.read(buffer, num);
int readNum = ifs.gcount();
already_read += readNum;
content += decrypt_with_passwd(std::string(buffer, readNum), key) + "\n";
}
return content;
}
std::string encrypt_with_passwd(const std::string & input, const std::string & key){
return crypt_with_passwd_core(input, key);
}
std::string decrypt_with_passwd(const std::string & input, const std::string & key){
return crypt_with_passwd_core(input, key);
}
std::string crypt_with_passwd_core(const std::string & input, const std::string & key){
if (key == "")
return input;
int iter = 0;
std::string res;
for (int i = 0; i < input.size(); i++){
res += (input[i] ^ key[iter++]);
if (iter == key.size())
iter = 0;
}
return res;
}
test.cpp
#include "gtest/gtest.h"
#include "utils.h"
#include <string>
#include <fstream>
using namespace std;
TEST(ENCRYPTION, encryptString){
std::string filename = "asString.ini";
std::string outfile = "asString.bin";
std::string key = "";
bool ret = encrypt_with_passwd_file(filename, key, outfile);
std::string res = decrypt_with_passwd_file(outfile, key);
std::string standard = "hello world\nhi\n你好\nsafd\na 12\ndsaf\n";
ASSERT_EQ(res, standard);
}
TEST(ENCRYPTION, encryptInt){
std::string filename = "asInt.ini";
std::string outfile = "asInt.bin";
std::string key = "";
bool ret = encrypt_with_passwd_file(filename, key, outfile);
std::string res = decrypt_with_passwd_file(outfile, key);
std::string standard = "1.00 2.00 3.00\n0.0 1.2 3.4\n4.6 6.5 3.4\n";
ASSERT_EQ(res, standard);
}
TEST(ENCRYPTION, encryptIntKey){
std::string filename = "asInt.ini";
std::string outfile = "asInt.bin1";
std::string key = "131xxxx8380";
bool ret = encrypt_with_passwd_file(filename, key, outfile);
std::string res = decrypt_with_passwd_file(outfile, key);
std::string standard = "1.00 2.00 3.00\n0.0 1.2 3.4\n4.6 6.5 3.4\n";
ASSERT_EQ(res, standard);
}
TEST(ENCRYPTION, encryptStringKey){
std::string filename = "asString.ini";
std::string outfile = "asString.bin";
std::string key = "131xxxx8380";
bool ret = encrypt_with_passwd_file(filename, key, outfile);
std::string res = decrypt_with_passwd_file(outfile, key);
std::string standard = "hello world\nhi\n你好\nsafd\na 12\ndsaf\n";
ASSERT_EQ(res, standard);
}
TEST(STRINGCRYPT, stringcrypt){
std::string input = "hello world";
std::string key = "131xxxx8380";
std::string encrypted = encrypt_with_passwd(input, key);
std::string decrypted = decrypt_with_passwd(encrypted, key);
ASSERT_EQ(decrypted, input);
}
int main(int argc, char ** argv){
::testing::InitGoogleTest(&argc, argv);
RUN_ALL_TESTS();
return 0;
}