【第二版】自己设计的一个加密、解密算法;C++实现
本帖最后由 RainPPR 于 2022-12-19 11:12 编辑声明:此算法完全是学习,未考虑算法效率、内存占用等。仅供学习使用!
## 更新日志
```
2022年12月14日9时0分
1. 使用 unsigned long long 代替 long long 防止 UB.
2. 随机数 rand 对 0x8000 取模,使各环境保持一致.
2022年12月19日11时0分
1. 更新下载链接
```
## 〇、目录
```
一、思路来源
二、实现过程
1. 密钥生成
2. 一次加密
3. 二次加密
三、成品分享
1. 库文件&使用说明
2. 打包程序
```
## 一、思路来源
> 加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。——百度
>
> 加密算法的作用就是把一段信息(明文)通过某种算法,转化为不能/不易看懂的信息(密文)。
>
> 作用:防止个人情报的泄漏,防止数据被更改,确保数据发自特定的一方、传给特定的一方。
由上文可知,加密在当今信息时代的重要性,于是我就设计了一个加密、解密算法。
## 二、实现过程
### 1. 密钥生成
我构想的加密算法,可以让接受者清楚加密的时间。于是我就想到了“UNIX时间戳”,C++中用 `<ctime>` 可以方便地生成。
```cpp
#include <ctime>
using namespace std;
unsigned long long t1 = time(0);
```
但是C++中的 `time(0)` 是按秒计的,也就是说,一秒内这样生成的所有密钥都会是一致的;而且如果其他人知道了加密的大概时间,就可以很容易地暴力枚举进行破解了。
于是,我在 `time(0)` 的基础上,乘上一个 `rand()`。因为不同环境下 `rand()` 的结果不同(但都是 0 到至少 0x7fff),所以对 0x8000 取模。
```cpp
#include <ctime>
#include <cstdlib>
using namespace std;
srand(time(0));
unsigned long long t1 = time(0);
unsignedlong long t2 = rand() % 0x8000;
```
然后就是怎么把这两个数转换为一个密钥了。我用了非常简单的算法:`×`。没错,就是乘法……
但是这样生成的随机数是在一个较小的范围 `` 内的,所以我将随机数平方,再乘以时间戳。为了“美观”,再乘上一个大数 `20090312`。(~~猜猜这是什么意思 'u'~~)
你可能已经发现了,我用了 `unsigned long long` 存储数据,超过最大值后会“溢出”,自动取模:
```
在 C++ 中
int 溢出是 Undefined Behavior,是否取会模取决于编译器。
unsigned int 溢出是 Define Behavior,在溢出时自动取模。
```
```cpp
#include <ctime>
#include <cstdlib>
using namespace std;
srand(time(0));
unsigned long long t1 = time(0);
unsignedlong long t2 = rand() % 0x8000;
unsigned long long t = t1 * (t2 * t2) * 20090312; // 习惯性打括号
```
这么大的数,一时想不到怎么用来加密字符串,于是我有了类似 `base64` 的一种编码方式,将这个数转换为一个字符串:先生成“字典”,然后用“进制转换”的方式将数字转换为大进制数,再对应为字符串。
然后我把整个算法打包为一个 `class` 类,方便调用。
```cpp
// File: pcode.h
#include <ctime>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <string>
#pragma once
using namespace std;
class _PCODE
{
private:
// typedef
typedef unsigned long long ll;
typedef pair<ll, ll> PLL;
// rand % 0x7fff
// get time
PLL get_time()
{
ll t1 = time(0);
//ll t2 = clock();
ll t2 = rand() % 0x8000;
return {t1, t2};
}
// encode
vector<char> ppr_code;
// time to str
string time_str(PLL tt)
{
ll t = tt.first * (tt.second * tt.second) * 20090312;
string res;
while (t)
{
res.push_back(ppr_code);
t /= ppr_code.size();
}
return res;
}
public:
_PCODE()
{
srand(time(0));
for (int i = 0 ; i < 26 ; ++i)
{
if (i % 2 == 0)
{
ppr_code.push_back('a' + i);
ppr_code.push_back('A' + i);
}
else
{
ppr_code.push_back('A' + i);
ppr_code.push_back('a' + i);
}
if (i % 3 == 0)
ppr_code.push_back('0' + i / 3);
}
ppr_code.push_back('9');
ppr_code.push_back('+');
ppr_code.push_back('=');
ppr_code.push_back('!');
ppr_code.push_back('-');
ppr_code.push_back('@');
ppr_code.push_back('$');
ppr_code.push_back('%');
ppr_code.push_back('&');
}
// get code (now)
string getnow(ll *t1, ll *t2)
{
PLL t = get_time();
*t1 = t.first;
*t2 = t.second;
return time_str(t);
}
string getnow()
{
PLL t = get_time();
return time_str(t);
}
// number to code
string encode(ll t1, ll t2)
{
PLL t = {t1, t2};
return time_str(t);
}
} pcode;
```
### 2. 一次加密
在上一节中,我们已经生成了字符串密钥,接下来就是加密的过程了。(这一节叫做“一次加密”,说明接下来还有一次加密过程)
算法很简单,重复密钥多次,使密钥与明文长度一致,或者只取密钥的前一部分。然后密钥与明文的每一位进行 `^` 抑或运算。
(代码与下一节合在一起)
### 3. 二次加密
看到上面那节,你发现问题了吗?
没错,抑或运算的结果对应的符号有部分不在可见符号范围内(我用的是 `ANSI` 编码),于是,我用 `Base64` 编码进行了二次编码。
**Base64编码算法请见(论坛里找的)**:[https://www.52pojie.cn/thread-1715164-1-1.html](https://www.52pojie.cn/thread-1715164-1-1.html)。这里我加了一个 `namespace` 命名空间,方便调用。
```cpp
// File: base64.h
#include <string>
#include <cstring>
#pragma once
namespace _base64
{
// 编码表
static const char base64en[] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H','I', 'J', 'K',
'L', 'M', 'N', 'O', 'P','Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v','w', 'x', 'y', 'z', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9', '+', '/'
};
// 解码表
static const unsigned char base64_suffix_map =
{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255,62, 255, 255, 255,63,
52,53,54,55,56,57,58,59,60,61, 255, 255,
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9,10,11,12,13,14,15,16,17,18,
19,20,21,22,23,24,25, 255, 255, 255, 255, 255,
255,26,27,28,29,30,31,32,33,34,35,36,
37,38,39,40,41,42,43,44,45,46,47,48,
49,50,51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255
};
std::string base64_encode(const char *data)
{
std::string out = "";
// 长度计算
int data_len = strlen(data);
if (data_len == 0)
{
return "";
}
char c = '\0';
char last_c = '\0';
for (int i = 0; i < data_len; i++)
{
c = data;
switch (i % 3)
{
case 0:
out.push_back(base64en[(c >> 2) & 0x3f]);
break;
case 1:
out.push_back(base64en[(last_c & 0x3) << 4 | ((c >> 4) & 0xf)]);
break;
case 2:
out.push_back(base64en[((last_c & 0xf)) << 2 | ((c >> 6) & 0x3)]);
out.push_back(base64en);
break;
}
last_c = c;
}
if (data_len % 3 == 1)
{
out.push_back(base64en[(c & 0x3) << 4]);
out.push_back('=');
out.push_back('=');
}
if (data_len % 3 == 2)
{
out.push_back(base64en[(c & 0xf) << 2]);
out.push_back('=');
}
return out;
}
std::string base64_decode(const char *data)
{
std::string output;
int data_len = strlen(data);
unsigned char c = '\0';
int t = 0, y = 0;
int g = 3;
for (int x = 0; x < data_len; x++)
{
c = base64_suffix_map];
if (c == 255)
output.clear();
if (c == 253)
continue; // 对应的值是换行或者回车
if (c == 254)
{
c = 0; // 对应的值是'='
g--;
}
t = (t << 6) | c;
if (++y == 4)
{
output.push_back((t >> 16) & 0xff);
if (g > 1)
output.push_back((unsigned char)((t >> 8) & 0xff));
if (g > 2)
output.push_back((unsigned char)(t & 0xff));
y = t = 0;
}
}
return output;
}
}
```
**编码总程序(库文件)**。这里我加了一个 `class` 类,方便调用。
```cpp
// File: ppr.h
#include <string>
#include <cstring>
#include "pcode.h"
#include "base64.h"
#pragma once
using namespace std;
class _PPRCode
{
private:
//typedef
typedef unsigned long long ll;
public:
// 编码
string encode(string str, ll *t1, ll *t2)
{
string pwd = pcode.getnow(t1, t2);
string ans;
for (int i = 0, j = 0 ; i < str.size() ; ++i, ++j)
{
if (j == pwd.size())
j = 0;
ans += char(str ^ pwd);
}
ans = _base64::base64_encode(ans.c_str());
return ans;
}
// 解码
string decode(string str, ll t1, ll t2)
{
str = _base64::base64_decode(str.c_str());
string pwd = pcode.encode(t1, t2);
string ans;
for (int i = 0, j = 0 ; i < str.size() ; ++i, ++j)
{
if (j == pwd.size())
j = 0;
ans += char(str ^ pwd);
}
return ans;
}
} pprc;
```
## 三、成品分享
### 1. 库文件&使用说明
库文件源码上面有(有 `// File: *.h` 的都是)。方便下载,打包一个云盘:
> 链接:(https://www.123pan.com/s/JavSVv-NqLnH)
> 提取码:52pj
**使用说明**:
```cpp
// 1. 加入头文件
#include "ppr.h"
// 2. 编码
std::string str = "RainPPR"; // 明文(std::string)
unsigned long long t1; // 将要返回的密钥1
unsigned long long t2; // 将要返回的密钥2
std::string ans = pprc.encode(str, &t1, &t2);// ans 中的即为密文
// 3. 解码
std::string str = "Jg4FKHQ9Jg=="; // 密文(std::string)
unsigned long long t1 = 1670900298; // 密钥1
unsigned long long t2 = 17993; // 密钥2
std::string ans = pprc.decode(str, t1, t2); // ans 中的即为明文
```
### 2. 打包程序
成品发在原创区了,详见:[https://www.52pojie.cn/thread-1726292-1-1.html](https://www.52pojie.cn/thread-1726292-1-1.html)在这里也感谢我的同学——徐梓玉,她给这个软件带来了一点灵感。 膜拜大神! 期待楼主的成品 真棒, 我目前使用得就是类似得算法,RC4 +BASE64 。 用以实现空中升级这个二逼功能。 谢谢分享,收藏备用 能搞一个CPK后缀名的解密软件吗? 很不错的思路,学习中。 谢谢分享,收藏学习以备以后借鉴使用。正好我也经常使用C++ 在这里的都是大佬 谢谢分享
页:
[1]
2